Remove frontend source from repository.

This commit is contained in:
James Cole 2022-01-29 14:01:17 +01:00
parent aff0e4a9df
commit acb2a8697a
No known key found for this signature in database
GPG Key ID: BDE6667570EADBD5
135 changed files with 0 additions and 29134 deletions

View File

@ -1,53 +0,0 @@
#!/usr/bin/env bash
#
# build.sh
# Copyright (c) 2021 james@firefly-iii.org
#
# This file is part of Firefly III (https://github.com/firefly-iii).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
[ -d "~/Sites" ] && exit 1;
# build translations.
php /sites/FF3/dev/tools/cli.php ff3:json-translations v2
# remove old stuff
rm -rf public/
rm -rf ../public/fonts
rm -rf ../public/images
rm -rf ../public/v2/js
rm -rf ../public/v2/css
mkdir -p public/js
mkdir -p public/css
# build new stuff
yarn install
yarn audit fix
yarn upgrade
yarn prod
# yarn watch
# move to right directory
# mv public/js ../public/v2
# mv public/css ../public/v2
# also copy fonts
#cp -r fonts ../public/v2/css
# remove built stuff
rm -rf public/

View File

@ -1,32 +0,0 @@
{
"/public/js/dashboard.js": "/public/js/dashboard.js",
"/public/js/accounts/index.js": "/public/js/accounts/index.js",
"/public/js/accounts/delete.js": "/public/js/accounts/delete.js",
"/public/js/accounts/show.js": "/public/js/accounts/show.js",
"/public/js/accounts/create.js": "/public/js/accounts/create.js",
"/public/js/accounts/edit.js": "/public/js/accounts/edit.js",
"/public/js/bills/index.js": "/public/js/bills/index.js",
"/public/js/bills/create.js": "/public/js/bills/create.js",
"/public/js/budgets/index.js": "/public/js/budgets/index.js",
"/public/js/transactions/create.js": "/public/js/transactions/create.js",
"/public/js/transactions/edit.js": "/public/js/transactions/edit.js",
"/public/js/transactions/index.js": "/public/js/transactions/index.js",
"/public/js/empty.js": "/public/js/empty.js",
"/public/js/register.js": "/public/js/register.js",
"/public/js/manifest.js": "/public/js/manifest.js",
"/public/css/app.css": "/public/css/app.css",
"/public/js/vendor.js": "/public/js/vendor.js",
"/public/v2/js/dashboard.js": "/public/v2/js/dashboard.js",
"/public/v2/js/dashboard.js.map": "/public/v2/js/dashboard.js.map",
"/public/v2/js/empty.js": "/public/v2/js/empty.js",
"/public/v2/js/empty.js.map": "/public/v2/js/empty.js.map",
"/public/v2/js/manifest.js": "/public/v2/js/manifest.js",
"/public/v2/js/manifest.js.map": "/public/v2/js/manifest.js.map",
"/public/v2/js/register.js": "/public/v2/js/register.js",
"/public/v2/js/register.js.map": "/public/v2/js/register.js.map",
"/public/v2/js/vendor.js": "/public/v2/js/vendor.js",
"/public/v2/js/vendor.js.LICENSE.txt": "/public/v2/js/vendor.js.LICENSE.txt",
"/public/v2/js/vendor.js.map": "/public/v2/js/vendor.js.map",
"/public/v2/css/app.css": "/public/v2/css/app.css",
"/public/v2/css/app.css.map": "/public/v2/css/app.css.map"
}

View File

@ -1,48 +0,0 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production"
},
"devDependencies": {
"axios": "^0.21",
"date-fns": "^2.27.0",
"laravel-mix": "^6",
"lodash": "^4.17.21",
"lodash.clonedeep": "^4.5.0",
"postcss": "^8.4.5",
"resolve-url-loader": "^4.0.0",
"sass": "^1.49.0",
"sass-loader": "^12.2.0",
"vue-i18n": "^8.27.0",
"vue-loader": "^15",
"vue-template-compiler": "^2.6.12",
"vuex": "^3.6.2",
"webpack": "^5.67.0"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.3",
"@johmun/vue-tags-input": "^2.1.0",
"admin-lte": "^3.1.0",
"axios-cache-adapter": "^2.7.3",
"bootstrap": "^4.6.0",
"bootstrap-vue": "^2.21.2",
"chart.js": "^3.7.0",
"icheck-bootstrap": "^3.0.1",
"jquery-ui": "^1.13.1",
"leaflet": "^1.7.1",
"localforage": "^1.9.0",
"localforage-memoryStorageDriver": "^0.9.2",
"overlayscrollbars": "^1.13.1",
"sortablejs": "^1.14.0",
"uiv": "^1.4.1",
"v-calendar": "^2.3.2",
"vue-typeahead-bootstrap": "^2.8.0",
"vue2-leaflet": "^2.7.1"
}
}

View File

@ -1,26 +0,0 @@
#!/usr/bin/env bash
#
# render.sh
# Copyright (c) 2021 james@firefly-iii.org
#
# This file is part of Firefly III (https://github.com/firefly-iii).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
[ -d "~/Sites" ] && exit 1;
# build translations.
php /sites/FF3/dev/tools/cli.php ff3:json-translations v2

90
frontend/src/app.scss vendored
View File

@ -1,90 +0,0 @@
/*!
* app.scss
* Copyright (c) 2021 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
$blue: #1E6581;
$green: #64B624;
$red: #CD5029;
// Fonts
@import '~@fortawesome/fontawesome-free/css/all.css';
// OverlayScrollbars
@import '~overlayscrollbars/css/OverlayScrollbars.css';
// iCheck
@import '~icheck-bootstrap/icheck-bootstrap.css';
// AdminLTE
//@import 'dist/css/adminlte.css';
//@import 'adminlte/scss/adminlte.css'
//@import '~admin-lte/build/scss/AdminLTE';
// ADMIN LTE
@import '~bootstrap/scss/functions';
@import '~admin-lte/build/scss/bootstrap-variables';
@import '~bootstrap/scss/bootstrap';
@import '~bootstrap-vue/src/index';
// Variables and Mixins
// ---------------------------------------------------
@import '~admin-lte/build/scss/variables';
@import '~admin-lte/build/scss/variables-alt';
@import '~admin-lte/build/scss/mixins';
@import '~admin-lte/build/scss/parts/core';
// admin LTE components
@import '~admin-lte/build/scss/forms';
@import '~admin-lte/build/scss/progress-bars';
@import '~admin-lte/build/scss/cards';
@import '~admin-lte/build/scss/modals';
//@import '../toasts';
@import '~admin-lte/build/scss/buttons';
@import '~admin-lte/build/scss/callout';
@import '~admin-lte/build/scss/alerts';
@import '~admin-lte/build/scss/table';
//@import '../carousel';
// admin LTE extra components
//@import '../small-box';
@import '~admin-lte/build/scss/info-box';
//@import '../timeline';
//@import '../products';
//@import '../direct-chat';
//@import '../users-list';
//@import '../social-widgets';
// admin LTE pages (unused)
@import '~admin-lte/build/scss/parts/pages';
// admin LTE plugins (unused)
// @import 'parts/plugins';
// admin LTE misc
@import '~admin-lte/build/scss/miscellaneous';
@import '~admin-lte/build/scss/print';
@import '~admin-lte/build/scss/text';
@import '~admin-lte/build/scss/elevation';
@import '~admin-lte/build/scss/colors';

View File

@ -1,26 +0,0 @@
/*
* bootstrap-basic.js
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// export jquery for others scripts to use
window.$ = window.jQuery = require('jquery');
// admin stuff
require('bootstrap');

View File

@ -1,64 +0,0 @@
/*
* bootstrap.js
* Copyright (c) 2021 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// // imports
import Vue from 'vue';
import VueI18n from 'vue-i18n'
import * as uiv from 'uiv';
// export jquery for others scripts to use
window.$ = window.jQuery = require('jquery');
// axios
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
// CSRF
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
// locale
let localeToken = document.head.querySelector('meta[name="locale"]');
if (localeToken) {
localStorage.locale = localeToken.content;
} else {
localStorage.locale = 'en_US';
}
// admin stuff
require('jquery-ui');
require('bootstrap'); // bootstrap CSS?
require('admin-lte/dist/js/adminlte');
require('overlayscrollbars');
// vue
window.vuei18n = VueI18n;
window.uiv = uiv;
Vue.use(vuei18n);
Vue.use(uiv);
window.Vue = Vue;

View File

@ -1,92 +0,0 @@
<!--
- AssetAccountRole.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('form.account_role') }}
</div>
<div class="input-group" v-if="loading">
<span class="fas fa-spinner fa-spin"></span>
</div>
<div class="input-group" v-if="!loading">
<select
ref="account_role"
v-model="account_role"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:title="$t('form.account_role')"
autocomplete="off"
name="account_role"
:disabled=disabled
>
<option v-for="role in this.roleList" :label="role.title" :value="role.slug">{{ role.title }}</option>
</select>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "AssetAccountRole",
props: {
value: {},
errors: {},
disabled: {
type: Boolean,
default: false
},
},
data() {
return {
roleList: [],
account_role: this.value,
loading: false
}
},
methods: {
loadRoles: function () {
//
axios.get('./api/v1/configuration/firefly.accountRoles')
.then(response => {
let content = response.data.data.value;
for (let i in content) {
if (content.hasOwnProperty(i)) {
let current = content[i];
this.roleList.push({slug: current, title: this.$t('firefly.account_role_' + current)})
}
}
}
);
}
},
watch: {
account_role: function (value) {
this.$emit('set-field', {field: 'account_role', value: value});
},
},
created() {
this.loadRoles()
}
}
</script>

View File

@ -1,397 +0,0 @@
<!--
- Create.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<Alert :message="errorMessage" type="danger"/>
<Alert :message="successMessage" type="success"/>
<form @submit="submitForm" autocomplete="off">
<div class="row">
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">
{{ $t('firefly.mandatoryFields') }}
</h3>
</div>
<div class="card-body">
<GenericTextInput :disabled="submitting" v-model="name" field-name="name" :errors="errors.name" :title="$t('form.name')"
v-on:set-field="storeField($event)"/>
<GenericCurrency :disabled="submitting" v-model="currency_id" :errors="errors.currency_id" v-on:set-field="storeField($event)"/>
<AssetAccountRole :disabled="submitting" v-if="'asset' === type" v-model="account_role" :errors="errors.account_role"
v-on:set-field="storeField($event)"/>
<!-- some CC fields -->
<CreditCardType :disabled="submitting" v-if="'ccAsset' === account_role" v-model="credit_card_type" :errors="errors.credit_card_type"
v-on:set-field="storeField($event)" />
<GenericTextInput :disabled="submitting" v-if="'ccAsset' === account_role" field-type="date" v-model="monthly_payment_date" field-name="monthly_payment_date"
:errors="errors.monthly_payment_date" :title="$t('form.cc_monthly_payment_date')" v-on:set-field="storeField($event)" />
<!-- liability fields -->
<LiabilityType :disabled="submitting" v-if="'liabilities' === type" v-model="liability_type" :errors="errors.liability_type"
v-on:set-field="storeField($event)"/>
<LiabilityDirection :disabled="submitting" v-if="'liabilities' === type" v-model="liability_direction" :errors="errors.liability_direction"
v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-if="'liabilities' === type" field-type="number" field-step="any" v-model="liability_amount"
field-name="liability_amount" :errors="errors.liability_amount" :title="$t('form.amount')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-if="'liabilities' === type" field-type="date" v-model="liability_date" field-name="liability_date"
:errors="errors.liability_date" :title="$t('form.date')" v-on:set-field="storeField($event)"/>
<Interest :disabled="submitting" v-if="'liabilities' === type" v-model="interest" :errors="errors.interest" v-on:set-field="storeField($event)"/>
<InterestPeriod :disabled="submitting" v-if="'liabilities' === type" v-model="interest_period" :errors="errors.interest_period"
v-on:set-field="storeField($event)"/>
</div>
</div>
</div>
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">
{{ $t('firefly.optionalFields') }}
</h3>
</div>
<div class="card-body">
<GenericTextInput :disabled="submitting" v-model="iban" field-name="iban" :errors="errors.iban" :title="$t('form.iban')"
v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-model="bic" field-name="bic" :errors="errors.bic" :title="$t('form.BIC')"
v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-model="account_number" field-name="account_number" :errors="errors.account_number"
:title="$t('form.account_number')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-if="'asset' === type" field-type="amount" v-model="virtual_balance" field-name="virtual_balance"
:errors="errors.virtual_balance" :title="$t('form.virtual_balance')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-if="'asset' === type" field-type="amount" v-model="opening_balance" field-name="opening_balance"
:errors="errors.opening_balance" :title="$t('form.opening_balance')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-if="'asset' === type" field-type="date" v-model="opening_balance_date"
field-name="opening_balance_date" :errors="errors.opening_balance_date" :title="$t('form.opening_balance_date')"
v-on:set-field="storeField($event)"/>
<GenericCheckbox :disabled="submitting" v-if="'asset' === type" :title="$t('form.include_net_worth')" field-name="include_net_worth"
v-model="include_net_worth" :errors="errors.include_net_worth" :description="$t('form.include_net_worth')"
v-on:set-field="storeField($event)"/>
<GenericCheckbox :disabled="submitting" :title="$t('form.active')" field-name="active"
v-model="active" :errors="errors.active" :description="$t('form.active')"
v-on:set-field="storeField($event)"/>
<GenericTextarea :disabled="submitting" field-name="notes" :title="$t('form.notes')" v-model="notes" :errors="errors.notes"
v-on:set-field="storeField($event)"/>
<GenericLocation :disabled="submitting" v-model="location" :title="$t('form.location')" :errors="errors.location"
v-on:set-field="storeField($event)"/>
<!-- attachments -->
<GenericAttachments :disabled="submitting" :title="$t('form.attachments')" field-name="attachments" :errors="errors.attachments"
v-on:selected-attachments="selectedAttachments($event)"
v-on:selected-no-attachments="selectedNoAttachments($event)"
v-on:uploaded-attachments="uploadedAttachments($event)"
:upload-trigger="uploadTrigger"
:upload-object-type="uploadObjectType"
:upload-object-id="uploadObjectId"
/>
</div>
</div>
</div>
</div>
</form>
<div class="row">
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12 offset-xl-6 offset-lg-6">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-6 offset-lg-6">
<button :disabled=submitting type="button" @click="submitForm" class="btn btn-success btn-block">{{
$t('firefly.store_new_' + type + '_account')
}}
</button>
<div class="form-check">
<input id="createAnother" v-model="createAnother" class="form-check-input" type="checkbox">
<label class="form-check-label" for="createAnother">
<span class="small">{{ $t('firefly.create_another') }}</span>
</label>
</div>
<div class="form-check">
<input id="resetFormAfter" v-model="resetFormAfter" :disabled="!createAnother" class="form-check-input" type="checkbox">
<label class="form-check-label" for="resetFormAfter">
<span class="small">{{ $t('firefly.reset_after') }}</span>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import format from "date-fns/format";
const lodashClonedeep = require('lodash.clonedeep');
import GenericCurrency from "../form/GenericCurrency";
import AssetAccountRole from "./AssetAccountRole"
import LiabilityType from "./LiabilityType";
import CreditCardType from "./CreditCardType";
import LiabilityDirection from "./LiabilityDirection";
import Interest from "./Interest";
import InterestPeriod from "./InterestPeriod";
import GenericTextInput from "../form/GenericTextInput";
import GenericTextarea from "../form/GenericTextarea";
import GenericLocation from "../form/GenericLocation";
import GenericAttachments from "../form/GenericAttachments";
import GenericCheckbox from "../form/GenericCheckbox";
import Alert from '../partials/Alert';
export default {
name: "Create",
components: {
GenericCurrency, AssetAccountRole, LiabilityType, LiabilityDirection, Interest, InterestPeriod,
GenericTextInput, GenericTextarea, GenericLocation, GenericAttachments, GenericCheckbox, Alert,
CreditCardType
},
created() {
this.errors = lodashClonedeep(this.defaultErrors);
let pathName = window.location.pathname;
let parts = pathName.split('/');
this.type = parts[parts.length - 1];
this.date = format(new Date, 'yyyy-MM-dd');
this.monthly_payment_date = format(new Date, 'yyyy-MM-dd');
},
data() {
return {
submitting: false,
successMessage: '',
errorMessage: '',
createAnother: false,
resetFormAfter: false,
returnedId: 0,
returnedTitle: '',
// info
name: '',
type: 'any',
currency_id: null,
// liabilities
liability_type: 'Loan',
liability_direction: 'debit',
liability_amount: null,
liability_date: null,
interest: null,
interest_period: 'monthly',
// optional fields
iban: null,
bic: null,
account_number: null,
virtual_balance: null,
opening_balance: null,
opening_balance_date: null,
include_net_worth: true,
active: true,
notes: null,
location: {},
// credit card fields
monthly_payment_date: null,
credit_card_type: 'monthlyFull',
// has attachments to upload?
hasAttachments: false,
uploadTrigger: false,
uploadObjectId: 0,
uploadObjectType: 'Account',
account_role: 'defaultAsset',
errors: {
currency_id: [],
credit_card_type: [],
},
defaultErrors: {
name: [],
monthly_payment_date: [],
currency_id: [],
account_role: [],
liability_type: [],
liability_direction: [],
liability_amount: [],
credit_card_type: [],
liability_date: [],
interest: [],
interest_period: [],
iban: [],
bic: [],
account_number: [],
virtual_balance: [],
opening_balance: [],
opening_balance_date: [],
include_net_worth: [],
notes: [],
location: [],
}
}
},
methods: {
storeField: function (payload) {
// console.log(payload);
if ('location' === payload.field) {
if (true === payload.value.hasMarker) {
this.location = payload.value;
return;
}
this.location = {};
return;
}
this[payload.field] = payload.value;
},
selectedAttachments: function (e) {
this.hasAttachments = true;
},
selectedNoAttachments: function (e) {
this.hasAttachments = false;
},
uploadedAttachments: function (e) {
// console.log('Response to event uploaded-attachments');
// console.log(e);
this.finishSubmission();
},
submitForm: function (e) {
e.preventDefault();
this.submitting = true;
let submission = this.getSubmission();
// console.log('Will submit:');
// console.log(submission);
let url = './api/v1/accounts';
axios.post(url, submission)
.then(response => {
this.errors = lodashClonedeep(this.defaultErrors);
this.returnedId = parseInt(response.data.data.id);
this.returnedTitle = response.data.data.attributes.name;
if (this.hasAttachments) {
// upload attachments. Do a callback to a finish up method.
this.uploadObjectId = this.returnedId;
this.uploadTrigger = true;
}
if (!this.hasAttachments) {
this.finishSubmission();
}
})
.catch(error => {
this.submitting = false;
this.parseErrors(error.response.data);
});
},
finishSubmission: function () {
this.successMessage = this.$t('firefly.stored_new_account_js', {ID: this.returnedId, name: this.returnedTitle});
// stay here is false?
if (false === this.createAnother) {
window.location.href = (window.previousURL ?? '/') + '?account_id=' + this.returnedId + '&message=created';
return;
}
this.submitting = false;
if (this.resetFormAfter) {
// console.log('reset!');
this.name = '';
this.liability_type = 'Loan';
this.liability_direction = 'debit';
this.liability_amount = null;
this.liability_date = null;
this.interest = null;
this.interest_period = 'monthly';
this.iban = null;
this.bic = null;
this.account_number = null;
this.virtual_balance = null;
this.opening_balance = null;
this.opening_balance_date = null;
this.include_net_worth = true;
this.active = true;
this.notes = null;
this.location = {};
}
},
parseErrors: function (errors) {
this.errors = lodashClonedeep(this.defaultErrors);
// console.log(errors);
for (let i in errors.errors) {
if (errors.errors.hasOwnProperty(i)) {
this.errors[i] = errors.errors[i];
}
if ('liability_start_date' === i) {
this.errors.opening_balance_date = errors.errors[i];
}
}
},
getSubmission: function () {
let submission = {
"name": this.name,
"type": this.type,
"iban": this.iban,
"bic": this.bic,
"account_number": this.account_number,
"currency_id": this.currency_id,
"virtual_balance": this.virtual_balance,
"active": this.active,
"order": 31337,
"include_net_worth": this.include_net_worth,
"account_role": this.account_role,
"notes": this.notes,
};
if ('liabilities' === this.type) {
submission.liability_type = this.liability_type.toLowerCase();
submission.interest = this.interest;
submission.interest_period = this.interest_period;
submission.liability_amount = this.liability_amount;
submission.liability_start_date = this.liability_date;
submission.liability_direction = this.liability_direction;
}
if ((null !== this.opening_balance || null !== this.opening_balance_date) && 'asset' === this.type) {
submission.opening_balance = this.opening_balance;
submission.opening_balance_date = this.opening_balance_date;
}
if ('' === submission.opening_balance) {
delete submission.opening_balance;
}
if ('asset' === this.type && 'ccAsset' === this.account_role) {
submission.credit_card_type = this.credit_card_type;
submission.monthly_payment_date = this.monthly_payment_date;
}
if (Object.keys(this.location).length >= 3) {
submission.longitude = this.location.lng;
submission.latitude = this.location.lat;
submission.zoom_level = this.location.zoomLevel;
}
return submission;
}
}
}
</script>

View File

@ -1,97 +0,0 @@
<!--
- LiabilityType.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('form.cc_type') }}
</div>
<div class="input-group" v-if="loading">
<span class="fas fa-spinner fa-spin"></span>
</div>
<div class="input-group" v-if="!loading">
<select
ref="credit_card_type"
v-model="credit_card_type"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:title="$t('form.cc_type')"
autocomplete="off"
name="credit_card_type"
:disabled=disabled
>
<option v-for="type in this.typeList" :label="type.title" :value="type.slug">{{ type.title }}</option>
</select>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "CreditCardType",
props: {
value: {},
errors: {},
disabled: {
type: Boolean,
default: false
},
},
data() {
return {
typeList: [],
credit_card_type: this.value,
loading: true
}
},
methods: {
loadRoles: function () {
//
axios.get('./api/v1/configuration/firefly.credit_card_types')
.then(response => {
let content = response.data.data.value;
for (let i in content) {
if (content.hasOwnProperty(i)) {
let current = content[i];
this.typeList.push({slug: current, title: this.$t('firefly.credit_card_type_' + current)})
}
}
this.loading = false;
}
);
}
},
watch: {
credit_card_type: function (value) {
this.$emit('set-field', {field: 'credit_card_type', value: value});
},
value: function(value) {
this.credit_card_type = value;
}
},
created() {
this.loadRoles()
}
}
</script>

View File

@ -1,188 +0,0 @@
<!--
- Delete.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="card card-default card-danger">
<div class="card-header">
<h3 class="card-title">
<span class="fas fa-exclamation-triangle"></span>
{{ $t('firefly.delete_account') }}
</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="callout callout-danger" v-if="!deleting && !deleted">
<p>
<span class="far fa-dizzy"></span> {{ $t('form.permDeleteWarning') }}
</p>
</div>
<p v-if="!loading && !deleting && !deleted">
{{ $t('form.account_areYouSure_js', {'name': this.accountName}) }}
</p>
<p v-if="!loading && !deleting && !deleted">
<span v-if="piggyBankCount > 0">
{{ $tc('form.also_delete_piggyBanks_js', piggyBankCount, {count: piggyBankCount}) }}
</span>
<span v-if="transactionCount > 0">
{{ $tc('form.also_delete_transactions_js', transactionCount, {count: transactionCount}) }}
</span>
</p>
<p v-if="transactionCount > 0 && !deleting && !deleted">
{{ $tc('firefly.save_transactions_by_moving_js', transactionCount) }}
</p>
<p v-if="transactionCount > 0 && !deleting && !deleted">
<select name="account" v-model="moveToAccount" class="form-control">
<option :label="$t('firefly.none_in_select_list')" :value="0">{{ $t('firefly.none_in_select_list') }}</option>
<option v-for="account in accounts" :label="account.name" :value="account.id">{{ account.name }}</option>
</select>
</p>
<p v-if="loading || deleting || deleted" class="text-center">
<span class="fas fa-spinner fa-spin"></span>
</p>
</div>
<div class="card-footer">
<button @click="deleteAccount" class="btn btn-danger float-right" v-if="!loading && !deleting && !deleted"> {{
$t('form.deletePermanently')
}}
</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Delete",
data() {
return {
loading: true,
deleting: false,
deleted: false,
accountId: 0,
accountName: '',
piggyBankCount: 0,
transactionCount: 0,
moveToAccount: 0,
accounts: []
}
},
created() {
let pathName = window.location.pathname;
let parts = pathName.split('/');
this.accountId = parseInt(parts[parts.length - 1]);
this.getAccount();
},
methods: {
deleteAccount: function () {
this.deleting = true;
if (0 === this.moveToAccount) {
this.execDeleteAccount();
}
if (0 !== this.moveToAccount) {
// move to another account:
this.moveTransactions();
}
},
moveTransactions: function () {
let query =
{
where: {account_id: this.accountId},
update: {account_id: this.moveToAccount}
};
axios.post('./api/v1/data/bulk/transactions', {query: JSON.stringify(query)}).then(response => {
this.execDeleteAccount();
});
},
execDeleteAccount: function () {
axios.delete('./api/v1/accounts/' + this.accountId)
.then(response => {
this.deleted = true;
this.deleting = false;
window.location.href = (window.previousURL ?? '/') + '?account_id=' + this.accountId + '&message=deleted';
});
},
getAccount: function () {
axios.get('./api/v1/accounts/' + this.accountId)
.then(response => {
let account = response.data.data;
this.accountName = account.attributes.name;
// now get piggy and transaction count
this.getPiggyBankCount(account.attributes.type, account.attributes.currency_code);
}
);
},
getAccounts: function (type, currencyCode) {
axios.get('./api/v1/accounts?type=' + type)
.then(response => {
let accounts = response.data.data;
for (let i in accounts) {
if (accounts.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = accounts[i];
if (false === current.attributes.active) {
continue;
}
if (currencyCode !== current.attributes.currency_code) {
continue;
}
if (this.accountId === parseInt(current.id)) {
continue;
}
this.accounts.push({id: current.id, name: current.attributes.name});
}
}
this.loading = false;
}
);
// get accounts of the same type.
// console.log('Go for "' + type + '"');
},
getPiggyBankCount: function (type, currencyCode) {
axios.get('./api/v1/accounts/' + this.accountId + '/piggy_banks')
.then(response => {
this.piggyBankCount = response.data.meta.pagination.total ? parseInt(response.data.meta.pagination.total) : 0;
this.getTransactionCount(type, currencyCode);
}
);
},
getTransactionCount: function (type, currencyCode) {
axios.get('./api/v1/accounts/' + this.accountId + '/transactions')
.then(response => {
this.transactionCount = response.data.meta.pagination.total ? parseInt(response.data.meta.pagination.total) : 0;
if (this.transactionCount > 0) {
this.getAccounts(type, currencyCode);
}
if (0 === this.transactionCount) {
this.loading = false;
}
}
);
}
}
}
</script>

View File

@ -1,388 +0,0 @@
<!--
- Edit.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<Alert :message="errorMessage" type="danger"/>
<Alert :message="successMessage" type="success"/>
<form @submit="submitForm" autocomplete="off">
<div class="row">
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">
{{ $t('firefly.mandatoryFields') }}
</h3>
</div>
<div class="card-body">
<GenericTextInput :disabled="submitting" v-model="account.name" field-name="name" :errors="errors.name" :title="$t('form.name')"
v-on:set-field="storeField($event)"/>
<GenericCurrency :disabled="submitting" v-model="account.currency_id" :errors="errors.currency_id" v-on:set-field="storeField($event)"/>
<AssetAccountRole :disabled="submitting" v-if="'asset' === account.type" v-model="account.account_role" :errors="errors.account_role"
v-on:set-field="storeField($event)"/>
<LiabilityType :disabled="submitting" v-if="'liabilities' === account.type" v-model="account.liability_type" :errors="errors.liability_type"
v-on:set-field="storeField($event)"/>
<LiabilityDirection :disabled="submitting" v-if="'liabilities' === account.type" v-model="account.liability_direction"
:errors="errors.liability_direction"
v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-if="'liabilities' === account.type" field-type="number" field-step="any"
v-model="account.liability_amount"
field-name="liability_amount" :errors="errors.liability_amount" :title="$t('form.amount')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-if="'liabilities' === account.type" field-type="date" v-model="account.liability_date"
field-name="liability_date"
:errors="errors.liability_date" :title="$t('form.date')" v-on:set-field="storeField($event)"/>
<Interest :disabled="submitting" v-if="'liabilities' === account.type" v-model="account.interest" :errors="errors.interest"
v-on:set-field="storeField($event)"/>
<InterestPeriod :disabled="submitting" v-if="'liabilities' === account.type" v-model="account.interest_period" :errors="errors.interest_period"
v-on:set-field="storeField($event)"/>
</div>
</div>
</div>
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">
{{ $t('firefly.optionalFields') }}
</h3>
</div>
<div class="card-body">
<GenericTextInput :disabled="submitting" v-model="account.iban" field-name="iban" :errors="errors.iban" :title="$t('form.iban')"
v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-model="account.bic" field-name="bic" :errors="errors.bic" :title="$t('form.BIC')"
v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-model="account.account_number" field-name="account_number" :errors="errors.account_number"
:title="$t('form.account_number')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-if="'asset' === account.type" field-type="amount" v-model="account.virtual_balance"
field-name="virtual_balance"
:errors="errors.virtual_balance" :title="$t('form.virtual_balance')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-if="'asset' === account.type" field-type="amount" v-model="account.opening_balance"
field-name="opening_balance"
:errors="errors.opening_balance" :title="$t('form.opening_balance')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" v-if="'asset' === account.type" field-type="date" v-model="account.opening_balance_date"
field-name="opening_balance_date" :errors="errors.opening_balance_date" :title="$t('form.opening_balance_date')"
v-on:set-field="storeField($event)"/>
<GenericCheckbox :disabled="submitting" v-if="'asset' === account.type" :title="$t('form.include_net_worth')" field-name="include_net_worth"
v-model="account.include_net_worth" :errors="errors.include_net_worth" :description="$t('form.include_net_worth')"
v-on:set-field="storeField($event)"/>
<GenericCheckbox :disabled="submitting" :title="$t('form.active')" field-name="active"
v-model="account.active" :errors="errors.active" :description="$t('form.active')"
v-on:set-field="storeField($event)"/>
<GenericTextarea :disabled="submitting" field-name="notes" :title="$t('form.notes')" v-model="account.notes" :errors="errors.notes"
v-on:set-field="storeField($event)"/>
<GenericLocation :disabled="submitting" v-model="account.location" :title="$t('form.location')" :errors="errors.location"
v-on:set-field="storeField($event)"/>
<GenericAttachments :disabled="submitting" :title="$t('form.attachments')" field-name="attachments" :errors="errors.attachments"
v-on:selected-attachments="selectedAttachments($event)"
v-on:selected-no-attachments="selectedNoAttachments($event)"
v-on:uploaded-attachments="uploadedAttachments($event)"
:upload-trigger="uploadTrigger"
:upload-object-type="uploadObjectType"
:upload-object-id="uploadObjectId"
/>
</div>
</div>
</div>
</div>
</form>
<div class="row">
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12 offset-xl-6 offset-lg-6">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-6 offset-lg-6">
<button :disabled=submitting type="button" @click="submitForm" class="btn btn-success btn-block">{{
$t('firefly.update_' + account.type + '_account')
}}
</button>
<div class="form-check">
<input id="stayHere" v-model="stayHere" class="form-check-input" type="checkbox">
<label class="form-check-label" for="stayHere">
<span class="small">{{ $t('firefly.after_update_create_another') }}</span>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Alert from '../partials/Alert';
import lodashClonedeep from "lodash.clonedeep";
import GenericTextInput from '../form/GenericTextInput';
import GenericCurrency from "../form/GenericCurrency";
import AssetAccountRole from "./AssetAccountRole";
import LiabilityType from "./LiabilityType";
import LiabilityDirection from "./LiabilityDirection";
import Interest from "./Interest";
import InterestPeriod from "./InterestPeriod";
import GenericTextarea from "../form/GenericTextarea";
import GenericCheckbox from "../form/GenericCheckbox";
import GenericAttachments from "../form/GenericAttachments";
import GenericLocation from "../form/GenericLocation";
export default {
name: "Edit",
created() {
// console.log('Created');
let parts = window.location.pathname.split('/');
this.accountId = parseInt(parts[parts.length - 1]);
this.uploadObjectId = parseInt(parts[parts.length - 1]);
this.getAccount();
},
components: {
Alert,
GenericTextInput,
GenericCurrency,
AssetAccountRole,
LiabilityDirection,
LiabilityType,
Interest,
InterestPeriod,
GenericTextarea,
GenericCheckbox,
GenericAttachments,
GenericLocation
},
data() {
return {
successMessage: '',
errorMessage: '',
stayHere: false,
inError: false,
accountId: 0,
submitting: false,
// account + original account
account: {},
originalAccount: {},
// has attachments to upload?
hasAttachments: false,
uploadTrigger: false,
uploadObjectId: 0,
uploadObjectType: 'Account',
// errors
errors: {
currency_id: [],
account_role: [],
liability_type: [],
location: []
},
defaultErrors: {
name: [],
currency_id: [],
account_role: [],
liability_type: [],
liability_direction: [],
liability_amount: [],
liability_date: [],
interest: [],
interest_period: [],
iban: [],
bic: [],
account_number: [],
virtual_balance: [],
opening_balance: [],
opening_balance_date: [],
include_net_worth: [],
active: [],
notes: [],
location: [],
attachments: [],
}
}
},
methods: {
selectedAttachments: function (e) {
this.hasAttachments = true;
},
selectedNoAttachments: function (e) {
this.hasAttachments = false;
},
uploadedAttachments: function (e) {
this.finaliseSubmission();
},
submitForm: function (e) {
e.preventDefault();
this.submitting = true;
let submission = this.getSubmission();
if (0 === Object.keys(submission).length) {
// console.log('Nothing to submit. Just finish up.');
this.finaliseSubmission();
return;
}
// console.log('Will submit:');
// console.log(submission);
const url = './api/v1/accounts/' + this.accountId;
axios.put(url, submission)
.then(this.processSubmission)
.catch(err => {
this.handleSubmissionError(err.response.data)
});
},
processSubmission: function() {
if (this.hasAttachments) {
// upload attachments. Do a callback to a finish up method.
this.uploadTrigger = true;
return;
}
this.finaliseSubmission();
},
finaliseSubmission: function () {
// console.log('finaliseSubmission');
// stay here, display message
if (true === this.stayHere && false === this.inError) {
this.errorMessage = '';
this.successMessage = this.$t('firefly.updated_account_js', {ID: this.accountId, title: this.account.name});
this.submitting = false;
}
// return to previous (bad hack), display message:
if (false === this.stayHere && false === this.inError) {
//console.log('no error + changes + redirect');
window.location.href = (window.previousURL ?? '/') + '?account_id=' + this.accountId + '&message=updated';
this.submitting = false;
}
// error or warning? here.
// console.log('end of finaliseSubmission');
},
handleSubmissionError: function (errors) {
console.error('Bad');
console.error(errors);
this.inError = true;
this.submitting = false;
this.errors = lodashClonedeep(this.defaultErrors);
for (let i in errors.errors) {
if (errors.errors.hasOwnProperty(i)) {
this.errors[i] = errors.errors[i];
}
}
},
getSubmission: function () {
let submission = {};
// console.log('getSubmission');
// console.log(this.account);
for (let i in this.account) {
// console.log(i);
if (this.account.hasOwnProperty(i) && this.originalAccount.hasOwnProperty(i) && JSON.stringify(this.account[i]) !== JSON.stringify(this.originalAccount[i])) {
// console.log('Field "' + i + '" has changed.');
// console.log('Original:')
// console.log(this.account[i]);
// console.log('Backup : ');
// console.log(this.originalAccount[i]);
submission[i] = this.account[i];
}
// else {
// console.log('Field "' + i + '" has not changed.');
// }
}
return submission;
},
/**
* Grab account from URL and submit GET.
*/
getAccount: function () {
// console.log('getTransactionGroup');
axios.get('./api/v1/accounts/' + this.accountId)
.then(response => {
this.parseAccount(response.data);
}
).catch(error => {
console.error('I failed :(');
console.error(error);
});
},
storeField: function (payload) {
//console.log(payload);
if ('location' === payload.field) {
if (true === payload.value.hasMarker) {
this.account.location = payload.value;
return;
}
this.account.location = {};
return;
}
this.account[payload.field] = payload.value;
},
/**
* Parse transaction group. Title is easy, transactions have their own method.
* @param response
*/
parseAccount: function (response) {
// console.log('Will now parse');
// console.log(response);
let attributes = response.data.attributes;
let account = {};
// parse account:
account.account_number = attributes.account_number;
account.account_role = attributes.account_role;
account.active = attributes.active;
account.bic = attributes.bic;
account.credit_card_type = attributes.credit_card_type;
account.currency_code = attributes.currency_code;
account.currency_decimal_places = parseInt(attributes.currency_decimal_places);
account.currency_id = parseInt(attributes.currency_id);
account.currency_symbol = attributes.currency_symbol;
account.iban = attributes.iban;
account.include_net_worth = attributes.include_net_worth;
account.interest = attributes.interest;
account.interest_period = attributes.interest_period;
account.liability_direction = attributes.liability_direction;
account.liability_type = attributes.liability_type;
account.monthly_payment_date = attributes.monthly_payment_date;
account.name = attributes.name;
account.notes = attributes.notes;
account.opening_balance = attributes.opening_balance;
account.opening_balance_date = attributes.opening_balance_date;
account.type = attributes.type;
account.virtual_balance = attributes.virtual_balance;
account.location = {};
if (null !== attributes.latitude && null !== attributes.longitude && null !== attributes.zoom_level) {
account.location = {
latitude: attributes.latitude,
longitude: attributes.longitude,
zoom_level: attributes.zoom_level
};
}
this.account = account;
this.originalAccount = lodashClonedeep(this.account);
},
}
}
</script>

View File

@ -1,611 +0,0 @@
<!--
- Index.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<div class="row">
<div class="col-lg-8 col-md-6 col-sm-12 col-xs-12">
<b-pagination
v-model="currentPage"
:total-rows="total"
:per-page="perPage"
aria-controls="my-table"
></b-pagination>
</div>
<div class="col-lg-4 col-md-6 col-sm-12 col-xs-12">
<a :href="'./accounts/create/' + type" class="btn btn-sm mb-2 float-right btn-success" :title="$t('firefly.create_new_' + type)"><span class="fas fa-plus"></span> {{ $t('firefly.create_new_' + type) }}</a>
<button @click="newCacheKey" class="btn btn-sm mb-2 mr-2 float-right btn-info"><span class="fas fa-sync"></span></button>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="card">
<div class="card-body p-0">
<b-table id="my-table" striped hover responsive="md" primary-key="id" :no-local-sorting="false"
:items="accounts" :fields="fields"
:per-page="perPage"
sort-icon-left
ref="table"
:current-page="currentPage"
:busy.sync="loading"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
>
<template #table-busy>
<span class="fas fa-spinner fa-spin"></span>
</template>
<template #cell(name)="data">
<a :class="false === data.item.active ? 'text-muted' : ''" :href="'./accounts/show/' + data.item.id" :title="data.value">{{ data.value }}</a>
</template>
<template #cell(acct_number)="data">
{{ data.item.acct_number }}
</template>
<template #cell(last_activity)="data">
<span v-if="'asset' === type && 'loading' === data.item.last_activity">
<span class="fas fa-spinner fa-spin"></span>
</span>
<span v-if="'asset' === type && 'none' === data.item.last_activity" class="text-muted">
{{ $t('firefly.never') }}
</span>
<span v-if="'asset' === type && 'loading' !== data.item.last_activity && 'none' !== data.item.last_activity">
{{ data.item.last_activity }}
</span>
</template>
<template #cell(amount_due)="data">
<span class="text-success" v-if="parseFloat(data.item.amount_due) > 0">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: data.item.currency_code}).format(data.item.amount_due) }}
</span>
<span class="text-danger" v-if="parseFloat(data.item.amount_due) < 0">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: data.item.currency_code}).format(data.item.amount_due) }}
</span>
<span class="text-muted" v-if="parseFloat(data.item.amount_due) === 0.0">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: data.item.currency_code}).format(data.item.amount_due) }}
</span>
</template>
<template #cell(current_balance)="data">
<span class="text-success" v-if="parseFloat(data.item.current_balance) > 0">
{{
Intl.NumberFormat(locale, {
style: 'currency', currency:
data.item.currency_code
}).format(data.item.current_balance)
}}
</span>
<span class="text-danger" v-if="parseFloat(data.item.current_balance) < 0">
{{
Intl.NumberFormat(locale, {
style: 'currency', currency:
data.item.currency_code
}).format(data.item.current_balance)
}}
</span>
<span class="text-muted" v-if="0 === parseFloat(data.item.current_balance)">
{{
Intl.NumberFormat(locale, {
style: 'currency', currency:
data.item.currency_code
}).format(data.item.current_balance)
}}
</span>
<span v-if="'asset' === type && 'loading' === data.item.balance_diff">
<span class="fas fa-spinner fa-spin"></span>
</span>
<span v-if="'asset' === type && 'loading' !== data.item.balance_diff">
(<span class="text-success" v-if="parseFloat(data.item.balance_diff) > 0">{{
Intl.NumberFormat(locale, {
style: 'currency', currency:
data.item.currency_code
}).format(data.item.balance_diff)
}}</span><span class="text-muted" v-if="0===parseFloat(data.item.balance_diff)">{{
Intl.NumberFormat(locale, {
style: 'currency', currency:
data.item.currency_code
}).format(data.item.balance_diff)
}}</span><span class="text-danger" v-if="parseFloat(data.item.balance_diff) < 0">{{
Intl.NumberFormat(locale, {
style: 'currency', currency:
data.item.currency_code
}).format(data.item.balance_diff)
}}</span>)
</span>
</template>
<template #cell(interest)="data">
{{ parseFloat(data.item.interest) }}% ({{ data.item.interest_period }})
</template>
<template #cell(menu)="data">
<div class="btn-group btn-group-sm">
<div class="dropdown">
<button class="btn btn-light btn-sm dropdown-toggle" type="button" :id="'dropdownMenuButton' + data.item.id" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
{{ $t('firefly.actions') }}
</button>
<div class="dropdown-menu" :aria-labelledby="'dropdownMenuButton' + data.item.id">
<a class="dropdown-item" :href="'./accounts/edit/' + data.item.id"><span class="fa fas fa-pencil-alt"></span> {{ $t('firefly.edit') }}</a>
<a class="dropdown-item" :href="'./accounts/delete/' + data.item.id"><span class="fa far fa-trash"></span> {{ $t('firefly.delete') }}</a>
<a v-if="'asset' === type" class="dropdown-item" :href="'./accounts/reconcile/' + data.item.id + '/index'"><span
class="fas fa-check"></span>
{{ $t('firefly.reconcile_this_account') }}</a>
</div>
</div>
</div>
</template>
</b-table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-8 col-md-6 col-sm-12 col-xs-12">
<b-pagination
v-model="currentPage"
:total-rows="total"
:per-page="perPage"
aria-controls="my-table"
></b-pagination>
</div>
<div class="col-lg-4 col-md-6 col-sm-12 col-xs-12">
<a :href="'./accounts/create/' + type" class="btn btn-sm mt-2 float-right btn-success" :title="$t('firefly.create_new_' + type)"><span class="fas fa-plus"></span> {{ $t('firefly.create_new_' + type) }}</a>
<button @click="newCacheKey" class="btn btn-sm mt-2 mr-2 float-right btn-info"><span class="fas fa-sync"></span></button>
</div>
</div>
</div>
</template>
<script>
import {mapGetters, mapMutations} from "vuex";
import Sortable from "sortablejs";
import format from "date-fns/format";
import {configureAxios} from "../../shared/forageStore";
export default {
name: "Index",
props: {
accountTypes: String
},
data() {
return {
accounts: [],
allAccounts: [],
type: 'all',
downloaded: false,
loading: false,
ready: false,
fields: [],
currentPage: 1,
perPage: 5,
total: 1,
sortBy: 'order',
sortDesc: false,
api: null,
sortableOptions: {
disabled: false,
chosenClass: 'is-selected',
onEnd: null
},
sortable: null,
locale: 'en-US'
}
},
watch: {
start: function () {
this.getAccountList();
},
end: function () {
this.getAccountList();
},
orderMode: function (value) {
// update the table headers
this.updateFieldList();
// reorder the accounts:
this.reorderAccountList(value);
// make table sortable:
this.makeTableSortable(value);
},
activeFilter: function (value) {
this.filterAccountList();
}
},
computed: {
...mapGetters('root', ['listPageSize', 'cacheKey']),
...mapGetters('accounts/index', ['orderMode', 'activeFilter']),
...mapGetters('dashboard/index', ['start', 'end',]),
'indexReady': function () {
return null !== this.start && null !== this.end && null !== this.listPageSize && this.ready;
},
cardTitle: function () {
return this.$t('firefly.' + this.type + '_accounts');
}
},
created() {
this.locale = localStorage.locale ?? 'en-US';
let pathName = window.location.pathname;
let parts = pathName.split('/');
this.type = parts[parts.length - 1];
this.perPage = this.listPageSize ?? 51;
// console.log('Per page: ' + this.perPage);
let params = new URLSearchParams(window.location.search);
this.currentPage = params.get('page') ? parseInt(params.get('page')) : 1;
this.updateFieldList();
this.ready = true;
// make object thing:
// let token = document.head.querySelector('meta[name="csrf-token"]');
// this.api = setup(
// {
// // `axios` options
// //baseURL: './',
// headers: {'X-CSRF-TOKEN': token.content, 'X-James': 'yes'},
//
// // `axios-cache-adapter` options
// cache: {
// maxAge: 15 * 60 * 1000,
// readHeaders: false,
// exclude: {
// query: false,
// },
// debug: true
// }
// });
},
methods: {
...mapMutations('root', ['refreshCacheKey',]),
// itemsProvider: function (ctx, callback) {
// console.log('itemsProvider()');
// console.log('ctx.currentPage = ' + ctx.currentPage);
// console.log('this.currentPage = ' + this.currentPage);
// if (ctx.currentPage === this.currentPage) {
// let direction = this.sortDesc ? '-' : '+';
// let url = 'api/v1/accounts?type=' + this.type + '&page=' + ctx.currentPage + '&sort=' + direction + this.sortBy;
// this.api.get(url)
// .then(async (response) => {
// this.total = parseInt(response.data.meta.pagination.total);
// let items = this.parseAccountsAndReturn(response.data.data);
// items = this.filterAccountListAndReturn(items);
// callback(items);
// }
// );
// return null;
// }
// return [];
// },
saveAccountSort: function (event) {
let oldIndex = parseInt(event.oldIndex);
let newIndex = parseInt(event.newIndex);
let identifier = parseInt(event.item.attributes.getNamedItem('data-pk').value);
for (let i in this.accounts) {
if (this.accounts.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = this.accounts[i];
// the actual account
if (current.id === identifier) {
let newOrder = parseInt(current.order) + (newIndex - oldIndex);
this.accounts[i].order = newOrder;
let url = './api/v1/accounts/' + current.id;
axios.put(url, {order: newOrder}).then(response => {
// See reference nr. 8
this.getAccountList();
});
}
}
}
},
reorderAccountList: function (orderMode) {
if (orderMode) {
this.sortBy = 'order';
this.sortDesc = false;
}
},
newCacheKey: function () {
this.refreshCacheKey();
this.downloaded = false;
this.accounts = [];
this.getAccountList();
},
makeTableSortable: function (orderMode) {
this.sortableOptions.disabled = !orderMode;
this.sortableOptions.onEnd = this.saveAccountSort;
// make sortable of table:
if (null === this.sortable) {
this.sortable = Sortable.create(this.$refs.table.$el.querySelector('tbody'), this.sortableOptions);
}
this.sortable.option('disabled', this.sortableOptions.disabled);
},
updateFieldList: function () {
this.fields = [];
this.fields = [{key: 'name', label: this.$t('list.name'), sortable: !this.orderMode}];
if ('asset' === this.type) {
this.fields.push({key: 'role', label: this.$t('list.role'), sortable: !this.orderMode});
}
if ('liabilities' === this.type) {
this.fields.push({key: 'liability_type', label: this.$t('list.liability_type'), sortable: !this.orderMode});
this.fields.push({key: 'liability_direction', label: this.$t('list.liability_direction'), sortable: !this.orderMode});
this.fields.push({key: 'interest', label: this.$t('list.interest') + ' (' + this.$t('list.interest_period') + ')', sortable: !this.orderMode});
}
// add the rest
this.fields.push({key: 'acct_number', label: this.$t('list.iban'), sortable: !this.orderMode});
this.fields.push({key: 'current_balance', label: this.$t('list.currentBalance'), sortable: !this.orderMode});
if ('liabilities' === this.type) {
this.fields.push({key: 'amount_due', label: this.$t('firefly.left_in_debt'), sortable: !this.orderMode});
}
if ('asset' === this.type || 'liabilities' === this.type) {
this.fields.push({key: 'last_activity', label: this.$t('list.lastActivity'), sortable: !this.orderMode});
}
this.fields.push({key: 'menu', label: ' ', sortable: false});
},
getAccountList: function () {
// console.log('getAccountList()');
if (this.indexReady && !this.loading && !this.downloaded) {
// console.log('Index ready, not loading and not already downloaded. Reset.');
this.loading = true;
this.perPage = this.listPageSize ?? 51;
this.accounts = [];
this.allAccounts = [];
this.downloadAccountList(1);
}
if (this.indexReady && !this.loading && this.downloaded) {
// console.log('Index ready, not loading and not downloaded.');
this.loading = true;
this.filterAccountList();
}
},
downloadAccountList: function (page) {
// console.log('downloadAccountList(' + page + ')');
configureAxios().then(async (api) => {
api.get('./api/v1/accounts?type=' + this.type + '&page=' + page + '&key=' + this.cacheKey)
.then(response => {
let currentPage = parseInt(response.data.meta.pagination.current_page);
let totalPage = parseInt(response.data.meta.pagination.total_pages);
this.total = parseInt(response.data.meta.pagination.total);
this.parseAccounts(response.data.data);
if (currentPage < totalPage) {
let nextPage = currentPage + 1;
this.downloadAccountList(nextPage);
}
if (currentPage >= totalPage) {
// console.log('Looks like all downloaded.');
this.downloaded = true;
this.filterAccountList();
}
}
);
});
},
filterAccountListAndReturn: function (allAccounts) {
// console.log('filterAccountListAndReturn()');
let accounts = [];
for (let i in allAccounts) {
if (allAccounts.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
// 1 = active only
// 2 = inactive only
// 3 = both
if (1 === this.activeFilter && false === allAccounts[i].active) {
// console.log('Skip account #' + this.allAccounts[i].id + ' because not active.');
continue;
}
if (2 === this.activeFilter && true === allAccounts[i].active) {
// console.log('Skip account #' + this.allAccounts[i].id + ' because active.');
continue;
}
// console.log('Include account #' + this.allAccounts[i].id + '.');
accounts.push(allAccounts[i]);
}
}
return accounts;
},
filterAccountList: function () {
// console.log('filterAccountList()');
this.accounts = [];
for (let i in this.allAccounts) {
if (this.allAccounts.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
// 1 = active only
// 2 = inactive only
// 3 = both
if (1 === this.activeFilter && false === this.allAccounts[i].active) {
// console.log('Skip account #' + this.allAccounts[i].id + ' because not active.');
continue;
}
if (2 === this.activeFilter && true === this.allAccounts[i].active) {
// console.log('Skip account #' + this.allAccounts[i].id + ' because active.');
continue;
}
// console.log('Include account #' + this.allAccounts[i].id + '.');
this.accounts.push(this.allAccounts[i]);
}
}
this.total = this.accounts.length;
this.loading = false;
},
roleTranslate: function (role) {
if (null === role) {
return '';
}
return this.$t('firefly.account_role_' + role);
},
parsePages: function (data) {
this.total = parseInt(data.pagination.total);
// console.log('Total is now ' + this.total);
},
// parseAccountsAndReturn: function (data) {
// console.log('In parseAccountsAndReturn()');
// let allAccounts = [];
// for (let key in data) {
// if (data.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
// let current = data[key];
// let acct = {};
// acct.id = parseInt(current.id);
// acct.order = current.attributes.order;
// acct.name = current.attributes.name;
// acct.active = current.attributes.active;
// acct.role = this.roleTranslate(current.attributes.account_role);
// acct.account_number = current.attributes.account_number;
// acct.current_balance = current.attributes.current_balance;
// acct.currency_code = current.attributes.currency_code;
//
// if ('liabilities' === this.type) {
// acct.liability_type = this.$t('firefly.account_type_' + current.attributes.liability_type);
// acct.liability_direction = this.$t('firefly.liability_direction_' + current.attributes.liability_direction + '_short');
// acct.interest = current.attributes.interest;
// acct.interest_period = this.$t('firefly.interest_calc_' + current.attributes.interest_period);
// acct.amount_due = current.attributes.current_debt;
// }
// acct.balance_diff = 'loading';
// acct.last_activity = 'loading';
//
// if (null !== current.attributes.iban) {
// acct.iban = current.attributes.iban.match(/.{1,4}/g).join(' ');
// }
// if (null === current.attributes.iban) {
// acct.iban = null;
// }
//
// allAccounts.push(acct);
// if ('asset' === this.type) {
// See reference nr. 9
// //this.getAccountBalanceDifference(this.allAccounts.length - 1, current);
// //this.getAccountLastActivity(this.allAccounts.length - 1, current);
// }
// }
// }
// return allAccounts;
// },
parseAccounts: function (data) {
// console.log('In parseAccounts()');
for (let key in data) {
if (data.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
let current = data[key];
let acct = {};
acct.id = parseInt(current.id);
acct.order = current.attributes.order;
acct.name = current.attributes.name;
acct.active = current.attributes.active;
acct.role = this.roleTranslate(current.attributes.account_role);
// account number in 'acct_number'
acct.acct_number = '';
let iban = null;
let acctNr = null;
acct.acct_number = '';
if (null !== current.attributes.iban) {
iban = current.attributes.iban.match(/.{1,4}/g).join(' ');
}
if (null !== current.attributes.account_number) {
acctNr = current.attributes.account_number;
}
// only account nr
if (null === iban && null !== acctNr) {
acct.acct_number = acctNr;
}
// only iban
if (null !== iban && null === acctNr) {
acct.acct_number = iban;
}
// both:
if (null !== iban && null !== acctNr) {
acct.acct_number = iban + ' (' + acctNr + ')';
}
acct.current_balance = current.attributes.current_balance;
acct.currency_code = current.attributes.currency_code;
if ('liabilities' === this.type) {
acct.liability_type = this.$t('firefly.account_type_' + current.attributes.liability_type);
acct.liability_direction = this.$t('firefly.liability_direction_' + current.attributes.liability_direction + '_short');
acct.interest = current.attributes.interest;
acct.interest_period = this.$t('firefly.interest_calc_' + current.attributes.interest_period);
acct.amount_due = current.attributes.current_debt;
}
acct.balance_diff = 'loading';
acct.last_activity = 'loading';
this.allAccounts.push(acct);
if ('asset' === this.type) {
this.getAccountBalanceDifference(this.allAccounts.length - 1, current);
this.getAccountLastActivity(this.allAccounts.length - 1, current);
}
}
}
},
getAccountLastActivity: function (index, acct) {
// console.log('getAccountLastActivity(' + index + ')');
// get single transaction for account:
// /api/v1/accounts/1/transactions?limit=1
configureAxios().then(async (api) => {
api.get('./api/v1/accounts/' + acct.id + '/transactions?limit=1&key=' + this.cacheKey).then(response => {
if (0 === response.data.data.length) {
this.allAccounts[index].last_activity = 'none';
return;
}
let date = new Date(response.data.data[0].attributes.transactions[0].date);
this.allAccounts[index].last_activity = format(date, this.$t('config.month_and_day_fns'));
});
});
},
getAccountBalanceDifference: function (index, acct) {
// console.log('getAccountBalanceDifference(' + index + ')');
// get account on day 0
let promises = [];
// add meta data to promise context.
promises.push(Promise.resolve({
account: acct,
index: index,
}));
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
configureAxios().then(api => {
return api.get('./api/v1/accounts/' + acct.id + '?date=' + startStr + '&key=' + this.cacheKey);
});
//promises.push(axios.get('./api/v1/accounts/' + acct.id + '?date=' + startStr + '&key=' + this.cacheKey));
promises.push(configureAxios().then(api => {
return api.get('./api/v1/accounts/' + acct.id + '?date=' + startStr + '&key=' + this.cacheKey);
}));
promises.push(configureAxios().then(api => {
return api.get('./api/v1/accounts/' + acct.id + '?date=' + endStr + '&key=' + this.cacheKey);
}));
Promise.all(promises).then(responses => {
let index = responses[0].index;
let startBalance = parseFloat(responses[1].data.data.attributes.current_balance);
let endBalance = parseFloat(responses[2].data.data.attributes.current_balance);
this.allAccounts[index].balance_diff = endBalance - startBalance;
});
},
}
}
</script>

View File

@ -1,90 +0,0 @@
<!--
- IndexOptions.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="order_mode" id="order_mode" v-model="orderMode">
<label class="form-check-label" for="order_mode">
Enable order mode
</label>
</div>
<div class="form-check">
<input class="form-check-input" :disabled="orderMode" type="radio" value="1" v-model="activeFilter" id="active_filter_1">
<label class="form-check-label" for="active_filter_1">
Show active accounts
</label>
</div>
<div class="form-check">
<input class="form-check-input" :disabled="orderMode" type="radio" value="2" v-model="activeFilter" id="active_filter_2">
<label class="form-check-label" for="active_filter_2">
Show inactive accounts
</label>
</div>
<div class="form-check">
<input class="form-check-input" :disabled="orderMode" type="radio" value="3" v-model="activeFilter" id="active_filter_3">
<label class="form-check-label" for="active_filter_3">
Show both
</label>
</div>
</div>
</template>
<script>
export default {
name: "IndexOptions",
data() {
return {
type: 'invalid'
}
},
// watch orderMode, if its false then go to active in filter.
computed: {
orderMode: {
get() {
return this.$store.getters["accounts/index/orderMode"];
},
set(value) {
this.$store.commit('accounts/index/setOrderMode', value);
if(true===value) {
this.$store.commit('accounts/index/setActiveFilter', 1);
}
}
},
activeFilter: {
get() {
return this.$store.getters["accounts/index/activeFilter"];
},
set(value) {
this.$store.commit('accounts/index/setActiveFilter', parseInt(value));
}
},
},
created() {
let pathName = window.location.pathname;
let parts = pathName.split('/');
this.type = parts[parts.length - 1];
}
}
</script>

View File

@ -1,70 +0,0 @@
<!--
- Interest.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('form.interest') }}
</div>
<div class="input-group">
<input
v-model="interest"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="$t('form.interest')"
name="interest"
:disabled=disabled
type="number"
step="8"
/>
<div class="input-group-append">
<div class="input-group-text">%</div>
<button class="btn btn-outline-secondary" tabindex="-1" type="button"><span class="far fa-trash-alt"></span></button>
</div>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "Interest",
props: {
value: {},
errors: {},
disabled: {
type: Boolean,
default: false
},
},
data() {
return {
interest: this.value
}
},
watch: {
interest: function (value) {
this.$emit('set-field', {field: 'interest', value: value});
},
}
}
</script>

View File

@ -1,93 +0,0 @@
<!--
- InterestPeriod.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('form.interest_period') }}
</div>
<div class="input-group" v-if="loading">
<span class="fas fa-spinner fa-spin"></span>
</div>
<div class="input-group" v-if="!loading">
<select
ref="interest_period"
v-model="interest_period"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:title="$t('form.interest_period')"
autocomplete="off"
:disabled=disabled
name="interest_period"
>
<option v-for="period in this.periodList" :label="period.title" :value="period.slug">{{ period.title }}</option>
</select>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "InterestPeriod",
props: {
value: {},
errors: {},
disabled: {
type: Boolean,
default: false
},
},
data() {
return {
periodList: [],
interest_period: this.value,
loading: true
}
},
methods: {
loadPeriods: function () {
//
axios.get('./api/v1/configuration/firefly.interest_periods')
.then(response => {
let content = response.data.data.value;
for (let i in content) {
if (content.hasOwnProperty(i)) {
let current = content[i];
this.periodList.push({slug: current, title: this.$t('firefly.interest_calc_' + current)})
}
}
this.loading = false;
}
);
}
},
watch: {
interest_period: function (value) {
this.$emit('set-field', {field: 'interest_period', value: value});
},
},
created() {
this.loadPeriods()
}
}
</script>

View File

@ -1,68 +0,0 @@
<!--
- LiabilityAmount.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('form.amount') }}
</div>
<div class="input-group">
<input
v-model="amount"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="$t('form.amount')"
name="liability_amount"
:disabled=disabled
type="number" step="any" min="0"
/>
<div class="input-group-append">
<button class="btn btn-outline-secondary" tabindex="-1" type="button"><span class="far fa-trash-alt"></span></button>
</div>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "LiabilityAmount",
props: {
value: {},
errors: {},
disabled: {
type: Boolean,
default: false
},
},
data() {
return {
amount: this.value
}
},
watch: {
amount: function (value) {
this.$emit('set-field', {field: 'liability_amount', value: value});
},
}
}
</script>

View File

@ -1,68 +0,0 @@
<!--
- LiabilityDate.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('form.date') }}
</div>
<div class="input-group">
<input
v-model="date"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="$t('form.date')"
name="liability_date"
:disabled=disabled
type="date"
/>
<div class="input-group-append">
<button class="btn btn-outline-secondary" tabindex="-1" type="button"><span class="far fa-trash-alt"></span></button>
</div>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "LiabilityDate",
props: {
value: {},
errors: {},
disabled: {
type: Boolean,
default: false
},
},
data() {
return {
date: this.value
}
},
watch: {
date: function (value) {
this.$emit('set-field', {field: 'liability_date', value: value});
},
}
}
</script>

View File

@ -1,71 +0,0 @@
<!--
- LiabilityDirection.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('form.liability_direction') }}
</div>
<div class="input-group">
<select
ref="liability_type"
v-model="liability_direction"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:title="$t('form.liability_direction')"
autocomplete="off"
name="liability_direction"
:disabled=disabled
>
<option :label="$t('firefly.liability_direction_credit')" value="credit">{{ $t('firefly.liability_direction_credit') }}</option>
<option :label="$t('firefly.liability_direction_debit')" value="debit">{{ $t('firefly.liability_direction_debit') }}</option>
</select>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "LiabilityDirection",
props: {
value: {},
errors: {},
disabled: {
type: Boolean,
default: false
},
},
data() {
return {
liability_direction: this.value
}
},
methods: {
},
watch: {
liability_direction: function (value) {
this.$emit('set-field', {field: 'liability_direction', value: value});
},
}
}
</script>

View File

@ -1,93 +0,0 @@
<!--
- LiabilityType.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('form.liability_type') }}
</div>
<div class="input-group" v-if="loading">
<span class="fas fa-spinner fa-spin"></span>
</div>
<div class="input-group" v-if="!loading">
<select
ref="liability_type"
v-model="liability_type"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:title="$t('form.liability_type')"
autocomplete="off"
name="liability_type"
:disabled=disabled
>
<option v-for="type in this.typeList" :label="type.title" :value="type.slug">{{ type.title }}</option>
</select>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "LiabilityType",
props: {
value: {},
errors: {},
disabled: {
type: Boolean,
default: false
},
},
data() {
return {
typeList: [],
liability_type: this.value,
loading: true
}
},
methods: {
loadRoles: function () {
//
axios.get('./api/v1/configuration/firefly.valid_liabilities')
.then(response => {
let content = response.data.data.value;
for (let i in content) {
if (content.hasOwnProperty(i)) {
let current = content[i];
this.typeList.push({slug: current, title: this.$t('firefly.account_type_' + current)})
}
}
this.loading = false;
}
);
}
},
watch: {
liability_type: function (value) {
this.$emit('set-field', {field: 'liability_type', value: value});
},
},
created() {
this.loadRoles()
}
}
</script>

View File

@ -1,175 +0,0 @@
<!--
- Show.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<div class="row">
<div class="col-lg-12 col-md-6 col-sm-12 col-xs-12">
<!-- Custom Tabs will be put here (see file history). -->
</div>
</div>
<TransactionListLarge
:entries="rawTransactions"
:isEmpty="isEmpty"
:page="currentPage"
ref="list"
:total="total"
:per-page="perPage"
:sort-desc="sortDesc"
v-on:jump-page="jumpToPage($event)"
v-on:refreshed-cache-key="refreshedKey"
/>
<div class="row">
<div class="col-lg-12 col-md-6 col-sm-12 col-xs-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">
Blocks
</h3>
</div>
<div class="card-body">
Blocks
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import TransactionListLarge from "../transactions/TransactionListLarge";
import {mapGetters} from "vuex";
import format from "date-fns/format";
import {configureAxios} from "../../shared/forageStore";
export default {
name: "Show",
computed: {
...mapGetters('root', ['listPageSize', 'cacheKey']),
...mapGetters('dashboard/index', ['start', 'end',]),
'showReady': function () {
return null !== this.start && null !== this.end && null !== this.listPageSize && this.ready;
},
},
data() {
return {
accountId: 0,
rawTransactions: [],
ready: false,
loading: false,
total: 0,
sortDesc: false,
currentPage: 1,
perPage: 51,
locale: 'en-US',
api: null,
nameLoading: false,
isEmpty: false
}
},
created() {
this.ready = true;
let parts = window.location.pathname.split('/');
this.accountId = parseInt(parts[parts.length - 1]);
this.perPage = this.listPageSize ?? 51;
let params = new URLSearchParams(window.location.search);
this.currentPage = params.get('page') ? parseInt(params.get('page')) : 1;
this.getTransactions();
this.updatePageTitle();
},
components: {TransactionListLarge},
methods: {
updatePageTitle: function () {
if (this.showReady && !this.nameLoading) {
// update page title.
this.nameLoading = true;
configureAxios().then(async (api) => {
let url = './api/v1/accounts/' + this.accountId;
api.get(url)
.then(response => {
let start = new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(this.start);
let end = new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(this.end);
document.getElementById('page-subTitle').innerText = this.$t('firefly.journals_in_period_for_account_js', {
start: start,
end: end,
title: response.data.data.attributes.name
});
});
});
}
},
refreshedKey: function () {
this.loading = false;
this.getTransactions();
this.updatePageTitle();
},
getTransactions: function () {
// console.log('getTransactions()');
if (this.showReady && !this.loading) {
// console.log('Show ready, not loading, go for download');
this.loading = true;
this.rawTransactions = [];
configureAxios().then(async (api) => {
// console.log('Now getTransactions() x Start');
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
let url = './api/v1/accounts/' + this.accountId + '/transactions?page=' + this.currentPage + '&limit=' + this.perPage + '&start=' + startStr + '&end=' + endStr + '&cache=' + this.cacheKey;
api.get(url)
.then(response => {
// console.log('Now getTransactions() DONE!');
this.total = parseInt(response.data.meta.pagination.total);
if (0 === this.total) {
this.isEmpty = true;
}
// let transactions = response.data.data;
// console.log('Have downloaded ' + transactions.length + ' transactions');
// console.log(response.data);
this.rawTransactions = response.data.data;
this.loading = false;
}
);
});
}
},
jumpToPage: function (event) {
// console.log('noticed a change in account/show!');
this.currentPage = event.page;
this.getTransactions();
},
},
watch: {
start: function () {
this.getTransactions();
this.updatePageTitle();
},
end: function () {
this.getTransactions();
this.updatePageTitle();
},
}
}
</script>

View File

@ -1,334 +0,0 @@
<!--
- Create.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<Alert :message="errorMessage" type="danger"/>
<Alert :message="successMessage" type="success"/>
<form @submit="submitForm" autocomplete="off">
<div class="col">
<!-- tabs -->
<ul class="nav nav-tabs ml-auto p-2" id="subscriptionTabs">
<li>
<li class="nav-item"><a class="nav-link active" href="#subscription" data-toggle="pill">
Subscription
</a>
</li>
<li>
<li class="nav-item"><a class="nav-link" href="#rule" data-toggle="pill">
Rule
</a>
</li>
</ul>
</div>
<div class="tab-content" id="subscriptionTabContent">
<div class="tab-pane show active" id="subscription" role="tabpanel" aria-labelledby="subscription-tab">
<div class="row">
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">
{{ $t('firefly.mandatoryFields') }}
</h3>
</div>
<div class="card-body">
<GenericTextInput :disabled="submitting" v-model="name" field-name="name" :errors="errors.name" :title="$t('form.name')"
v-on:set-field="storeField($event)"/>
<GenericCurrency :disabled="submitting" v-model="currency_id" :errors="errors.currency_id" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" field-type="number" field-step="any" v-model="amount_min"
field-name="amount_min" :errors="errors.amount_min" :title="$t('form.amount_min')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" field-type="number" field-step="any" v-model="amount_max"
field-name="amount_max" :errors="errors.amount_max" :title="$t('form.amount_max')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" field-type="date" v-model="date" field-name="date"
:errors="errors.date" :title="$t('form.startdate')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" field-type="date" v-model="end_date" field-name="end_date"
:errors="errors.end_date" :title="$t('form.end_date')" v-on:set-field="storeField($event)"/>
<GenericTextInput :disabled="submitting" field-type="date" v-model="extension_date" field-name="extension_date"
:errors="errors.extension_date" :title="$t('form.extension_date')" v-on:set-field="storeField($event)"/>
<RepeatFrequencyPeriod :disabled="submitting" v-model="repeat_freq" :errors="errors.repeat_freq"
v-on:set-field="storeField($event)"/>
</div>
</div>
</div>
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">
{{ $t('firefly.optionalFields') }}
</h3>
</div>
<div class="card-body">
<GenericTextarea :disabled="submitting" field-name="notes" :title="$t('form.notes')" v-model="notes" :errors="errors.notes"
v-on:set-field="storeField($event)"/>
<GenericAttachments :disabled="submitting" :title="$t('form.attachments')" field-name="attachments" :errors="errors.attachments"
v-on:selected-attachments="selectedAttachments($event)"
v-on:selected-no-attachments="selectedNoAttachments($event)"
v-on:uploaded-attachments="uploadedAttachments($event)"
:upload-trigger="uploadTrigger"
:upload-object-type="uploadObjectType"
:upload-object-id="uploadObjectId"
/>
<GenericTextInput :disabled="submitting" v-model="skip" field-name="skip" :errors="errors.skip" :title="$t('form.skip')"
v-on:set-field="storeField($event)"/>
<GenericGroup :disabled="submitting" v-model="group_title" field-name="group_title" :errors="errors.group_title"
:title="$t('form.object_group')"
v-on:set-field="storeField($event)"/>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12 offset-xl-6 offset-lg-6">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-6 offset-lg-6">
<button :disabled=submitting type="button" @click="submitForm" class="btn btn-success btn-block">{{
$t('firefly.store_new_bill')
}}
</button>
<div class="form-check">
<input id="createAnother" v-model="createAnother" class="form-check-input" type="checkbox">
<label class="form-check-label" for="createAnother">
<span class="small">{{ $t('firefly.create_another') }}</span>
</label>
</div>
<div class="form-check">
<input id="resetFormAfter" v-model="resetFormAfter" :disabled="!createAnother" class="form-check-input" type="checkbox">
<label class="form-check-label" for="resetFormAfter">
<span class="small">{{ $t('firefly.reset_after') }}</span>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane show" id="rule" role="tabpanel" aria-labelledby="rule-tab">
<div class="row">
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">
Title
</h3>
</div>
<div class="card-body">
<p>
In the future here you can set rule info for the new subscription.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</template>
<script>
import RepeatFrequencyPeriod from "./RepeatFrequencyPeriod";
import Alert from '../partials/Alert';
import GenericTextInput from "../form/GenericTextInput";
import GenericCurrency from "../form/GenericCurrency";
import GenericTextarea from "../form/GenericTextarea";
import GenericAttachments from "../form/GenericAttachments";
import GenericGroup from "../form/GenericGroup";
import format from "date-fns/format";
const lodashClonedeep = require('lodash.clonedeep');
export default {
name: "Create",
components: {RepeatFrequencyPeriod, GenericAttachments, GenericGroup, GenericTextarea, Alert, GenericTextInput, GenericCurrency},
data() {
return {
submitting: false,
successMessage: '',
errorMessage: '',
createAnother: false,
resetFormAfter: false,
returnedId: 0,
returnedTitle: '',
// fields
name: '',
currency_id: null,
amount_min: '',
amount_max: '',
date: '',
end_date: '',
extension_date: '',
repeat_freq: 'monthly',
// optional fields
notes: '',
skip: '0',
group_title: '',
// has attachments to upload?
hasAttachments: false,
uploadTrigger: false,
uploadObjectId: 0,
uploadObjectType: 'Bill',
// optional fields:
location: {},
// errors
errors: {
currency_id: [],
repeat_freq: [],
group_title: [],
},
defaultErrors: {
name: [],
group_title: [],
currency_id: [],
amount_min: [],
amount_max: [],
date: [],
end_date: [],
extension_date: [],
repeat_freq: [],
}
}
},
created() {
this.date = format(new Date, 'yyyy-MM-dd');
},
methods: {
storeField: function (payload) {
// console.log(payload);
if ('location' === payload.field) {
if (true === payload.value.hasMarker) {
this.location = payload.value;
return;
}
this.location = {};
return;
}
this[payload.field] = payload.value;
},
selectedAttachments: function (e) {
this.hasAttachments = true;
},
selectedNoAttachments: function (e) {
this.hasAttachments = false;
},
submitForm: function (e) {
e.preventDefault();
this.submitting = true;
let submission = this.getSubmission();
// console.log('Will submit:');
// console.log(submission);
let url = './api/v1/bills';
axios.post(url, submission)
.then(response => {
this.errors = lodashClonedeep(this.defaultErrors);
this.returnedId = parseInt(response.data.data.id);
this.returnedTitle = response.data.data.attributes.name;
if (this.hasAttachments) {
// upload attachments. Do a callback to a finish up method.
//alert('must now upload!');
this.uploadObjectId = this.returnedId;
this.uploadTrigger = true;
}
if (!this.hasAttachments) {
this.finishSubmission();
}
})
.catch(error => {
this.submitting = false;
this.parseErrors(error.response.data);
// display errors!
});
},
uploadedAttachments: function (e) {
this.finishSubmission();
},
finishSubmission: function () {
this.successMessage = this.$t('firefly.stored_new_bill_js', {ID: this.returnedId, name: this.returnedTitle});
// stay here is false?
if (false === this.createAnother) {
window.location.href = (window.previousURL ?? '/') + '?bill_id=' + this.returnedId + '&message=created';
return;
}
this.submitting = false;
if (this.resetFormAfter) {
// console.log('reset!');
this.name = '';
}
},
parseErrors: function (errors) {
this.errors = lodashClonedeep(this.defaultErrors);
// console.log(errors);
for (let i in errors.errors) {
if (errors.errors.hasOwnProperty(i)) {
this.errors[i] = errors.errors[i];
}
}
},
getSubmission: function () {
let submission = {
name: this.name,
currency_id: this.currency_id,
amount_min: this.amount_min,
amount_max: this.amount_max,
date: this.date,
repeat_freq: this.repeat_freq,
skip: this.skip,
active: true,
object_group_title: this.group_title
};
if (Object.keys(this.location).length >= 3) {
submission.longitude = this.location.lng;
submission.latitude = this.location.lat;
submission.zoom_level = this.location.zoomLevel;
}
if ('' !== this.end_date) {
submission.end_date = this.end_date;
}
if ('' !== this.extension_date) {
submission.extension_date = this.extension_date;
}
if ('' !== this.notes) {
submission.notes = this.notes;
}
return submission;
}
}
}
</script>

View File

@ -1,360 +0,0 @@
<!--
- Index.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<a href="./subscriptions/create" class="btn btn-sm mb-2 float-right btn-success"><span class="fas fa-plus"></span> {{
$t('firefly.create_new_bill')
}}</a>
<button @click="newCacheKey" class="btn btn-sm mb-2 mr-2 float-right btn-info"><span class="fas fa-sync"></span></button>
</div>
</div>
<div class="row" v-for="group in sortedGroups">
<div v-if="group[1].bills.length > 0" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">
{{ group[1].title }}
</h3>
</div>
<div class="card-body p-0">
<b-table id="my-table" striped hover responsive="md" primary-key="id" :no-local-sorting="false"
:items="group[1].bills"
sort-icon-left
:fields="fields"
:busy.sync="loading"
>
<template #cell(name)="data">
<a :href="'./bills/show/' + data.item.id">{{ data.item.name }}</a>
<br/>
<small v-if="true === data.item.active && 0 === data.item.skip">{{ $t('firefly.bill_repeats_' + data.item.repeat_freq) }}</small>
<small v-if="true === data.item.active && 1 === data.item.skip">{{ $t('firefly.bill_repeats_' + data.item.repeat_freq + '_other') }}</small>
<small v-if="true === data.item.active && data.item.skip > 1">{{
$t('firefly.bill_repeats_' + data.item.repeat_freq + '_skip', {skip: data.item.skip + 1})
}}</small>
<small v-if="false === data.item.active">{{ $t('firefly.inactive') }}</small>
<!-- (rules, recurring) -->
</template>
<template #cell(expected_info)="data">
<span v-if="true === data.item.active && data.item.paid_dates.length > 0 && data.item.pay_dates.length > 0">
{{
new Intl.DateTimeFormat(locale, {
month: 'long',
year: 'numeric',
day: 'numeric'
}).format(new Date(data.item.next_expected_match.substring(0, 10)))
}}
<br>
</span>
<!--
not paid, not expected and active
-->
<span v-if="0 === data.item.paid_dates.length && 0 === data.item.pay_dates.length && true === data.item.active">
{{ $t('firefly.not_expected_period') }}
</span>
<!--
not paid but expected
-->
<span :title="new Intl.DateTimeFormat(locale, {
month: 'long',
year: 'numeric',
day: 'numeric'
}).format(new Date(data.item.pay_dates[0].substring(0,10)))"
class="text-danger" v-if="0 === data.item.paid_dates.length && data.item.pay_dates.length > 0 && true === data.item.active">
{{ $t('firefly.bill_expected_date_js', {date: data.item.next_expected_match_diff}) }}
</span>
<!--
bill is not active
-->
<span v-if="false === data.item.active">
~
</span>
</template>
<template #cell(start_date)="data">
{{
new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(new Date(data.item.date.substring(0, 10)))
}}
</template>
<template #cell(end_date)="data">
<span v-if="null !== data.item.end_date">
{{
new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(new Date(data.item.end_date.substring(0, 10)))
}}
</span>
<span v-if="null === data.item.end_date">{{ $t('firefly.forever') }}</span>
<span v-if="null !== data.item.extension_date"><br/>
<small>
{{
$t('firefly.extension_date_is', {
date: new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(new Date(data.item.extension_date.substring(0, 10)))
})
}}
</small>
</span>
</template>
<template #cell(amount)="data">
~ <span class="text-info">{{
Intl.NumberFormat(locale, {style: 'currency', currency: data.item.currency_code}).format((data.item.amount_min + data.item.amount_max) / 2)
}}
</span>
</template>
<template #cell(payment_info)="data">
<!--
paid_dates >= 0 (bill is paid X times).
Don't care about pay_dates.
-->
<span v-if="data.item.paid_dates.length > 0 && true === data.item.active">
<span v-for="currentPaid in data.item.paid_dates">
<a :href="'./transactions/show/' + currentPaid.transaction_group_id">
{{
new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(new Date(currentPaid.date.substring(0, 10)))
}}
</a>
<br/>
</span>
</span>
<!--
bill is not active
-->
<span v-if="false === data.item.active">
~
</span>
</template>
<template #cell(menu)="data">
<div class="btn-group btn-group-sm">
<div class="dropdown">
<button class="btn btn-light btn-sm dropdown-toggle" type="button" :id="'dropdownMenuButton' + data.item.id" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
{{ $t('firefly.actions') }}
</button>
<div class="dropdown-menu" :aria-labelledby="'dropdownMenuButton' + data.item.id">
<a class="dropdown-item" :href="'./subscriptions/edit/' + data.item.id"><span class="fa fas fa-pencil-alt"></span> {{
$t('firefly.edit')
}}</a>
<a class="dropdown-item" :href="'./subscriptions/delete/' + data.item.id"><span class="fa far fa-trash"></span> {{
$t('firefly.delete')
}}</a>
</div>
</div>
</div>
</template>
</b-table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<a href="./subscriptions/create" class="btn btn-sm mt-2 float-right btn-success"><span class="fas fa-plus"></span> {{
$t('firefly.create_new_bill')
}}</a>
<button @click="newCacheKey" class="btn btn-sm mt-2 mr-2 float-right btn-info"><span class="fas fa-sync"></span></button>
</div>
</div>
</div>
</template>
<script>
import {mapGetters, mapMutations} from "vuex";
import {configureAxios} from "../../shared/forageStore";
import format from "date-fns/format";
export default {
name: "Index",
data() {
return {
groups: {},
downloaded: false,
loading: false,
locale: 'en-US',
sortedGroups: [],
fields: [],
fnsLocale: null,
ready: false
}
},
watch: {
start: function () {
this.downloadBills(1);
},
end: function () {
this.downloadBills(1);
},
},
computed: {
...mapGetters('root', ['listPageSize', 'cacheKey']),
...mapGetters('dashboard/index', ['start', 'end',]),
'indexReady': function () {
return null !== this.start && null !== this.end && null !== this.listPageSize && this.ready;
},
},
created() {
this.locale = localStorage.locale ?? 'en-US';
this.updateFieldList();
this.ready = true;
},
methods: {
...mapMutations('root', ['refreshCacheKey',]),
formatDate: function (date, frm) {
return format(date, frm, {locale: {code: this.locale}});
},
updateFieldList: function () {
this.fields = [];
this.fields.push({key: 'name', label: this.$t('list.name')});
this.fields.push({key: 'expected_info', label: this.$t('list.expected_info')});
this.fields.push({key: 'start_date', label: this.$t('list.start_date')});
this.fields.push({key: 'end_date', label: this.$t('list.end_date')});
this.fields.push({key: 'amount', label: this.$t('list.amount')});
this.fields.push({key: 'payment_info', label: this.$t('list.payment_info')});
this.fields.push({key: 'menu', label: ' ', sortable: false});
},
newCacheKey: function () {
this.refreshCacheKey();
this.downloaded = false;
this.accounts = [];
this.downloadBills(1);
},
resetGroups: function () {
this.groups = {};
this.groups[0] =
{
id: 0,
title: this.$t('firefly.default_group_title_name'),
order: 1,
bills: []
};
},
downloadBills: function (page) {
// console.log('downloadBills');
// console.log(this.indexReady);
// console.log(this.loading);
// console.log(this.downloaded);
this.resetGroups();
// console.log('getAccountList()');
if (this.indexReady && !this.loading && !this.downloaded) {
this.loading = true;
configureAxios().then(async (api) => {
// get date from session.
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
api.get('./api/v1/bills?page=' + page + '&key=' + this.cacheKey + '&start='+startStr+'&end=' + endStr)
.then(response => {
// pages
let currentPage = parseInt(response.data.meta.pagination.current_page);
let totalPage = parseInt(response.data.meta.pagination.total_pages);
this.parseBills(response.data.data);
if (currentPage < totalPage) {
let nextPage = currentPage + 1;
this.downloadBills(nextPage);
}
if (currentPage >= totalPage) {
this.downloaded = true;
}
this.sortGroups();
}
);
});
}
},
sortGroups: function () {
const sortable = Object.entries(this.groups);
//console.log('sortable');
//console.log(sortable);
sortable.sort(function (a, b) {
return a.order - b.order;
});
this.sortedGroups = sortable;
this.loading = false;
//console.log(this.sortedGroups);
},
parseBills: function (data) {
for (let key in data) {
if (data.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
let current = data[key];
let bill = {};
// create group of necessary.
let groupId = null === current.attributes.object_group_id ? 0 : parseInt(current.attributes.object_group_id);
if (0 !== groupId && !(groupId in this.groups)) {
this.groups[groupId] = {
id: groupId,
title: current.attributes.object_group_title,
order: parseInt(current.attributes.object_group_order),
bills: []
}
}
bill.id = parseInt(current.id);
bill.order = parseInt(current.attributes.order);
bill.name = current.attributes.name;
bill.repeat_freq = current.attributes.repeat_freq;
bill.skip = current.attributes.skip;
bill.active = current.attributes.active;
bill.date = current.attributes.date;
bill.end_date = current.attributes.end_date;
bill.extension_date = current.attributes.extension_date;
bill.amount_max = parseFloat(current.attributes.amount_max);
bill.amount_min = parseFloat(current.attributes.amount_min);
bill.currency_code = current.attributes.currency_code;
bill.currency_id = parseInt(current.attributes.currency_id);
bill.currency_decimal_places = parseInt(current.attributes.currency_decimal_places);
bill.currency_symbol = current.attributes.currency_symbol;
bill.next_expected_match = current.attributes.next_expected_match;
bill.next_expected_match_diff = current.attributes.next_expected_match_diff;
bill.notes = current.attributes.notes;
bill.paid_dates = current.attributes.paid_dates;
bill.pay_dates = current.attributes.pay_dates;
this.groups[groupId].bills.push(bill);
}
}
}
}
}
</script>

View File

@ -1,101 +0,0 @@
<!--
- InterestPeriod.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('form.repeat_freq') }}
</div>
<div class="input-group" v-if="loading">
<span class="fas fa-spinner fa-spin"></span>
</div>
<div class="input-group" v-if="!loading">
<select
ref="repeat_freq"
v-model="repeat_freq"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:title="$t('form.repeat_freq')"
autocomplete="off"
:disabled=disabled
name="repeat_freq"
>
<option v-for="period in this.periodList" :label="period.title" :value="period.slug">{{ period.title }}</option>
</select>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
import {configureAxios} from "../../shared/forageStore";
import {mapGetters, mapMutations} from "vuex";
export default {
name: "RepeatFrequencyPeriod",
props: {
value: {},
errors: {},
disabled: {
type: Boolean,
default: false
},
},
computed: {
...mapGetters('root', [ 'cacheKey']),
},
data() {
return {
periodList: [],
repeat_freq: this.value,
loading: true
}
},
methods: {
...mapMutations('root', ['refreshCacheKey',]),
loadPeriods: function () {
configureAxios().then(async (api) => {
api.get('./api/v1/configuration/firefly.bill_periods?key=' + this.cacheKey)
.then(response => {
let content = response.data.data.value;
for (let i in content) {
if (content.hasOwnProperty(i)) {
let current = content[i];
this.periodList.push({slug: current, title: this.$t('firefly.repeat_freq_' + current)})
}
}
this.loading = false;
}
);
});
}
},
watch: {
repeat_freq: function (value) {
this.$emit('set-field', {field: 'repeat_freq', value: value});
},
},
created() {
this.loadPeriods()
}
}
</script>

View File

@ -1,111 +0,0 @@
<!--
- Index.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<p>
<span class="d-block">(all)</span>
<span class="d-none d-xl-block">xl</span>
<span class="d-none d-lg-block d-xl-none">lg</span>
<span class="d-none d-md-block d-lg-none">md</span>
<span class="d-none d-sm-block d-md-none">sm</span>
<span class="d-block d-sm-none">xs</span>
</p>
<div class="row">
<div class="col-xl-2 col-lg-4 col-md-4 col-sm-4 col-6">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Budgets</h3>
</div>
<div class="card-body">
Budget X<br>
Budget Y<br>
Budget X<br>
Budget Y<br>
Budget X<br>
Budget Y<br>
Budget X<br>
Budget Y<br>
</div>
</div>
</div>
<div class="col-xl-10 col-lg-8 col-md-8 col-sm-8 col-6">
<div class="container-fluid" style="overflow:scroll;">
<div class="d-flex flex-row flex-nowrap">
<div class="card card-body-budget" v-for="n in 5">
<div class="card-header">
<h3 class="card-title">Maand yXz</h3>
</div>
<div class="card-body">
Some text<br>
Some text<br>
Some text<br>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Index"
}
</script>
<style scoped>
.card-body-budget {
min-width: 300px;
margin-right: 5px;
}
.holder-titles {
display: flex;
flex-direction: column-reverse;
}
.title-block {
border: 1px red solid;
}
.holder-blocks {
display: flex;
flex-direction: column-reverse;
}
.budget-block {
border: 1px blue solid;
}
.budget-block-unused {
border: 1px green solid;
}
.budget-block-unset {
border: 1px purple solid;
}
</style>

View File

@ -1,187 +0,0 @@
<!--
- DataConverter.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<script>
export default {
name: "DataConverter",
data() {
return {
dataSet: null,
newDataSet: null,
locale: localStorage.local,
}
},
methods: {
convertChart(dataSet) {
this.dataSet = dataSet;
this.newDataSet = {
//count: 0,
labels: [],
datasets: []
}
this.getLabels();
this.getDataSets();
//this.newDataSet.count = this.newDataSet.datasets.length;
return this.newDataSet;
},
colorizeBarData(dataSet) {
this.dataSet = dataSet;
this.newDataSet = {
//count: 0,
labels: [],
datasets: []
};
// colors
let colourSet = [
[53, 124, 165],
[0, 141, 76], // green
[219, 139, 11],
[202, 25, 90], // paars rood-ish #CA195A
[85, 82, 153],
[66, 133, 244],
[219, 68, 55], // red #DB4437
[244, 180, 0],
[15, 157, 88],
[171, 71, 188],
[0, 172, 193],
[255, 112, 67],
[158, 157, 36],
[92, 107, 192],
[240, 98, 146],
[0, 121, 107],
[194, 24, 91]
];
let fillColors = [];
//let strokePointHighColors = [];
//for (let i = 0; i < colourSet.length; i++) {
for (let value of colourSet) {
fillColors.push("rgba(" + value[0] + ", " + value[1] + ", " + value[2] + ", 0.5)");
//strokePointHighColors.push("rgba(" + colourSet[i][0] + ", " + colourSet[i][1] + ", " + colourSet[i][2] + ", 0.9)");
}
this.newDataSet.labels = this.dataSet.labels;
//this.newDataSet.count = this.dataSet.count;
for (let setKey in this.dataSet.datasets) {
if (this.dataSet.datasets.hasOwnProperty(setKey)) {
var dataset = this.dataSet.datasets[setKey];
dataset.fill = false;
dataset.backgroundColor = dataset.borderColor = fillColors[setKey];
this.newDataSet.datasets.push(dataset);
}
}
return this.newDataSet;
},
colorizeLineData(dataSet) {
this.dataSet = dataSet;
this.newDataSet = {
//count: 0,
labels: [],
datasets: []
};
// colors
let colourSet = [
[53, 124, 165],
[0, 141, 76], // green
[219, 139, 11],
[202, 25, 90], // paars rood-ish #CA195A
[85, 82, 153],
[66, 133, 244],
[219, 68, 55], // red #DB4437
[244, 180, 0],
[15, 157, 88],
[171, 71, 188],
[0, 172, 193],
[255, 112, 67],
[158, 157, 36],
[92, 107, 192],
[240, 98, 146],
[0, 121, 107],
[194, 24, 91]
];
let fillColors = [];
//let strokePointHighColors = [];
//for (let i = 0; i < colourSet.length; i++) {
for (let value of colourSet) {
fillColors.push("rgba(" + value[0] + ", " + value[1] + ", " + value[2] + ", 0.5)");
//strokePointHighColors.push("rgba(" + colourSet[i][0] + ", " + colourSet[i][1] + ", " + colourSet[i][2] + ", 0.9)");
}
this.newDataSet.labels = this.dataSet.labels;
//this.newDataSet.count = this.dataSet.count;
for (let setKey in this.dataSet.datasets) {
if (this.dataSet.datasets.hasOwnProperty(setKey)) {
let dataset = this.dataSet.datasets[setKey];
dataset.fill = false;
dataset.backgroundColor = dataset.borderColor = fillColors[setKey];
this.newDataSet.datasets.push(dataset);
}
}
return this.newDataSet;
},
convertLabelsToDate(dataSet) {
for (let labelKey in dataSet.labels) {
if (dataSet.labels.hasOwnProperty(labelKey)) {
const unixTimeZero = Date.parse(dataSet.labels[labelKey]);
dataSet.labels[labelKey] = new Intl.DateTimeFormat(this.locale).format(unixTimeZero);
}
}
return dataSet;
},
getLabels() {
let firstSet = this.dataSet[0];
if (typeof firstSet !== 'undefined') {
for (const entryLabel in firstSet.entries) {
if (firstSet.entries.hasOwnProperty(entryLabel)) {
this.newDataSet.labels.push(entryLabel);
}
}
}
},
getDataSets() {
for (const setKey in this.dataSet) {
if (this.dataSet.hasOwnProperty(setKey)) {
let newSet = {};
let oldSet = this.dataSet[setKey];
if (typeof oldSet !== 'undefined') {
newSet.label = oldSet.label;
newSet.type = oldSet.type;
newSet.currency_symbol = oldSet.currency_symbol;
newSet.currency_code = oldSet.currency_code;
//newSet.yAxisID = oldSet.yAxisID;
newSet.data = [];
for (const entryLabel in oldSet.entries) {
if (oldSet.entries.hasOwnProperty(entryLabel)) {
newSet.data.push(oldSet.entries[entryLabel]);
}
}
this.newDataSet.datasets.push(newSet);
}
}
}
}
}
}
</script>

View File

@ -1,113 +0,0 @@
<!--
- DefaultLineOptions.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
</template>
<script>
import FormatLabel from "../charts/FormatLabel";
export default {
name: "DefaultBarOptions",
data() {
return {}
},
methods: {
getDefaultOptions() {
return {
type: 'bar',
layout: {
padding: {
left: 50,
right: 50,
top: 0,
bottom: 0
},
},
stacked: true,
elements: {
line: {
cubicInterpolationMode: 'monotone'
}
},
legend: {
display: false,
},
animation: {
duration: 0,
},
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [
{
stacked: true,
gridLines: {
display: false
},
ticks: {
// break ticks when too long.
callback: function (value, index, values) {
return FormatLabel.methods.formatLabel(value, 20);
//return value;
}
}
}
],
yAxes: [{
stacked: false,
display: true,
drawOnChartArea: false,
offset: true,
beginAtZero: true,
ticks: {
callback: function (tickValue) {
"use strict";
let currencyCode = this.chart.data.datasets[0] ? this.chart.data.datasets[0].currency_code : 'EUR';
return new Intl.NumberFormat(localStorage.locale, {style: 'currency', currency: currencyCode}).format(tickValue);
},
}
}]
},
tooltips: {
mode: 'label',
callbacks: {
label: function (tooltipItem, data) {
"use strict";
let currencyCode = data.datasets[tooltipItem.datasetIndex] ? data.datasets[tooltipItem.datasetIndex].currency_code : 'EUR';
let nrString = new Intl.NumberFormat(localStorage.locale, {
style: 'currency',
currency: currencyCode
}).format(tooltipItem.yLabel);
return data.datasets[tooltipItem.datasetIndex].label + ': ' + nrString;
}
}
}
};
}
}
}
</script>

View File

@ -1,175 +0,0 @@
<!--
- DefaultLineOptions.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
</template>
<script>
export default {
name: "DefaultLineOptions",
data() {
return {}
},
methods: {
/**
* Takes a string phrase and breaks it into separate phrases no bigger than 'maxwidth', breaks are made at complete words.
* https://stackoverflow.com/questions/21409717/chart-js-and-long-labels
*
* @param str
* @param maxwidth
* @returns {Array}
*/
formatLabel(str, maxwidth) {
var sections = [];
str = String(str);
var words = str.split(" ");
var temp = "";
words.forEach(function (item, index) {
if (temp.length > 0) {
var concat = temp + ' ' + item;
if (concat.length > maxwidth) {
sections.push(temp);
temp = "";
} else {
if (index === (words.length - 1)) {
sections.push(concat);
return;
} else {
temp = concat;
return;
}
}
}
if (index === (words.length - 1)) {
sections.push(item);
return;
}
if (item.length < maxwidth) {
temp = item;
} else {
sections.push(item);
}
});
return sections;
},
getDefaultOptions() {
return {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false,
},
},
animations: false,
elements: {
line: {
cubicInterpolationMode: 'monotone'
}
},
scales: {
x: {
//type: 'linear'
grid: {
display: false,
},
ticks: {
callback: function (value, index, values) {
let dateObj = new Date(this.getLabelForValue(value).split('T')[0]);
return new Intl.DateTimeFormat(localStorage.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(dateObj);
//return str;
// // //console.log();
// // //return self.formatLabel(value, 20);
// // return self.formatLabel(str, 20);
}
}
},
y: {
suggestedMin: 0
//type: 'linear'
}
},
// scales: {
// xAxes: [
// {
// gridLines: {
// display: false
// },
// ticks: {
// // break ticks when too long
// callback: function (value, index, values) {
// return value;
// // date format
// // let dateObj = new Date(value);
// // let options = {year: 'numeric', month: 'long', day: 'numeric'};
// // let str = new Intl.DateTimeFormat(localStorage.locale, options).format(dateObj);
// // //console.log();
// // //return self.formatLabel(value, 20);
// // return self.formatLabel(str, 20);
// }
// }
// }
// ],
// yAxes: [{
// display: true,
// ticks: {
// callback: function (tickValue) {
// "use strict";
// return tickValue;
// // let currencyCode = this.chart.data.datasets[0].currency_code ? this.chart.data.datasets[0].currency_code : 'EUR';
// // return new Intl.NumberFormat(localStorage.locale, {style: 'currency', currency: currencyCode}).format(tickValue);
// },
// beginAtZero: true
// }
//
// }]
// },
// tooltips: {
// mode: 'index',
// // callbacks: {
// // label: function (tooltipItem, data) {
// // "use strict";
// // let currencyCode = data.datasets[tooltipItem.datasetIndex].currency_code ? data.datasets[tooltipItem.datasetIndex].currency_code : 'EUR';
// // let nrString =
// // new Intl.NumberFormat(localStorage.locale, {style: 'currency', currency: currencyCode}).format(tooltipItem.yLabel)
// //
// // return data.datasets[tooltipItem.datasetIndex].label + ': ' + nrString;
// // }
// // }
// }
};
}
}
}
</script>

View File

@ -1,30 +0,0 @@
<!--
- DefaultPieOptions.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
</template>
<script>
export default {
name: "DefaultPieOptions"
}
</script>

View File

@ -1,73 +0,0 @@
<!--
- FormatLabel.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<script>
export default {
name: "FormatLabel",
methods: {
/**
* Takes a string phrase and breaks it into separate phrases no bigger than 'maxwidth', breaks are made at complete words.
* https://stackoverflow.com/questions/21409717/chart-js-and-long-labels
*
* @param str
* @param maxwidth
* @returns {Array}
*/
formatLabel(str, maxwidth) {
var sections = [];
str = String(str);
var words = str.split(" ");
var temp = "";
words.forEach(function (item, index) {
if (temp.length > 0) {
var concat = temp + ' ' + item;
if (concat.length > maxwidth) {
sections.push(temp);
temp = "";
} else {
if (index === (words.length - 1)) {
sections.push(concat);
return;
} else {
temp = concat;
return;
}
}
}
if (index === (words.length - 1)) {
sections.push(item);
return;
}
if (item.length < maxwidth) {
temp = item;
} else {
sections.push(item);
}
});
return sections;
},
}
}
</script>

View File

@ -1,115 +0,0 @@
<!--
- BudgetRow.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<tr>
<td style="width:25%;">
<a :href="'./budgets/show/' + budgetLimit.budget_id">{{ budgetLimit.budget_name }}</a>
</td>
<td style="vertical-align: middle">
<div class="progress progress active">
<div :aria-valuenow="budgetLimit.pctGreen" :style="'width: '+ budgetLimit.pctGreen + '%;'"
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-success" role="progressbar">
<span v-if="budgetLimit.pctGreen > 35">
{{ $t('firefly.spent_x_of_y', {amount: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent), total: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.amount)}) }}
</span>
</div>
<div :aria-valuenow="budgetLimit.pctOrange" :style="'width: '+ budgetLimit.pctOrange + '%;'"
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-warning" role="progressbar">
<span v-if="budgetLimit.pctRed <= 50 && budgetLimit.pctOrange > 35">
{{ $t('firefly.spent_x_of_y', {amount: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent), total: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.amount)}) }}
</span>
</div>
<div :aria-valuenow="budgetLimit.pctRed" :style="'width: '+ budgetLimit.pctRed + '%;'"
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-danger" role="progressbar">
<span v-if="budgetLimit.pctOrange <= 50 && budgetLimit.pctRed > 35" class="text-white">
{{ $t('firefly.spent_x_of_y', {amount: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent), total: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.amount)}) }}
</span>
</div>
<!-- amount if bar is very small -->
<span v-if="budgetLimit.pctGreen <= 35 && 0 === budgetLimit.pctOrange && 0 === budgetLimit.pctRed && 0 !== budgetLimit.pctGreen" style="line-height: 16px;">
&nbsp; {{ $t('firefly.spent_x_of_y', {amount: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent), total: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.amount)}) }}
</span>
</div>
<small class="d-none d-lg-block">
{{ new Intl.DateTimeFormat(locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(budgetLimit.start) }}
&rarr;
{{ new Intl.DateTimeFormat(locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(budgetLimit.end) }}
</small>
</td>
<td class="align-middle d-none d-lg-table-cell" style="width:10%;">
<span v-if="parseFloat(budgetLimit.amount) + parseFloat(budgetLimit.spent) > 0" class="text-success">
{{
Intl.NumberFormat(locale, {
style: 'currency',
currency: budgetLimit.currency_code
}).format(parseFloat(budgetLimit.amount) + parseFloat(budgetLimit.spent))
}}
</span>
<span v-if="0.0 === parseFloat(budgetLimit.amount) + parseFloat(budgetLimit.spent)" class="text-muted">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(0) }}
</span>
<span v-if="parseFloat(budgetLimit.amount) + parseFloat(budgetLimit.spent) < 0" class="text-danger">
{{
Intl.NumberFormat(locale, {
style: 'currency',
currency: budgetLimit.currency_code
}).format(parseFloat(budgetLimit.amount) + parseFloat(budgetLimit.spent))
}}
</span>
</td>
</tr>
</template>
<script>
export default {
name: "BudgetLimitRow",
created() {
this.locale = localStorage.locale ?? 'en-US';
},
data() {
return {
locale: 'en-US',
}
},
props: {
budgetLimit: {
type: Object,
default: function () {
return {};
}
},
budget: {
type: Object,
default: function () {
return {};
}
}
}
}
</script>

View File

@ -1,62 +0,0 @@
<!--
- BudgetListGroup.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title">{{ title }}</h3>
</div>
<div class="card-body table-responsive p-0">
<table class="table table-sm">
<caption style="display:none;">{{ title }}</caption>
<thead>
<tr>
<th scope="col">{{ $t('firefly.budget') }}</th>
<th scope="col">{{ $t('firefly.spent') }}</th>
<th scope="col">{{ $t('firefly.left') }}</th>
</tr>
</thead>
<tbody>
<BudgetLimitRow v-for="(budgetLimit, key) in budgetLimits" v-bind:key="key" :budgetLimit="budgetLimit"/>
<BudgetRow v-for="(budget, key) in budgets" v-bind:key="key" :budget="budget"/>
</tbody>
</table>
</div>
<div class="card-footer">
<a class="btn btn-default button-sm" href="./budgets"><span class="far fa-money-bill-alt"></span> {{ $t('firefly.go_to_budgets') }}</a>
</div>
</div>
</template>
<script>
import BudgetLimitRow from "./BudgetLimitRow";
import BudgetRow from "./BudgetRow";
export default {
name: "BudgetListGroup",
components: {BudgetLimitRow, BudgetRow},
props: {
title: String,
budgetLimits: Array,
budgets: Array,
},
}
</script>

View File

@ -1,55 +0,0 @@
<!--
- BudgetRow.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<tr>
<td style="width:25%;">
<a :href="'./budgets/show/' + budget.id">{{ budget.name }}</a>
</td>
<td>&nbsp;</td>
<td class="align-middle text-right">
<span class="text-danger">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: budget.currency_code}).format(parseFloat(budget.spent)) }}
</span>
</td>
</tr>
</template>
<script>
export default {
name: "BudgetRow",
created() {
this.locale = localStorage.locale ?? 'en-US';
},
data() {
return {
locale: 'en-US',
}
},
props: {
budget: {
type: Object,
default: {}
}
}
}
</script>

View File

@ -1,602 +0,0 @@
<!--
- Calendar.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<div class="row">
<div class="col">Start</div>
<div class="col-8">{{ new Intl.DateTimeFormat(locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(range.start) }}</div>
</div>
<div class="row">
<div class="col">End</div>
<div class="col-8">{{ new Intl.DateTimeFormat(locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(range.end) }}</div>
</div>
<date-picker
v-model="range"
:rows="2"
is-range
mode="date"
>
<template v-slot="{ inputValue, inputEvents, isDragging, togglePopover }">
<div class="row">
<div class="col">
<div class="btn-group btn-group-sm d-flex">
<button
:title="$t('firefly.custom_period')" class="btn btn-secondary btn-sm"
@click="togglePopover({ placement: 'auto-start', positionFixed: true })"
><span class="fas fa-calendar-alt"></span></button>
<button :title="$t('firefly.reset_to_current')"
class="btn btn-secondary"
@click="resetDate"
><span class="fas fa-history"></span></button>
<button id="dropdownMenuButton" :title="$t('firefly.select_period')" aria-expanded="false" aria-haspopup="true"
class="btn btn-secondary dropdown-toggle"
data-toggle="dropdown"
type="button">
<span class="fas fa-list"></span>
</button>
<div aria-labelledby="dropdownMenuButton" class="dropdown-menu">
<a v-for="period in periods" class="dropdown-item" href="#" @click="customDate(period.start, period.end)">{{ period.title }}</a>
</div>
</div>
<input v-on="inputEvents.start"
:class="isDragging ? 'text-gray-600' : 'text-gray-900'"
:value="inputValue.start"
type="hidden"
/>
<input v-on="inputEvents.end"
:class="isDragging ? 'text-gray-600' : 'text-gray-900'"
:value="inputValue.end"
type="hidden"
/>
</div>
</div>
</template>
</date-picker>
</div>
</template>
<script>
import {createNamespacedHelpers} from "vuex";
import Vue from "vue";
import DatePicker from "v-calendar/lib/components/date-picker.umd";
import subDays from 'date-fns/subDays';
import addDays from 'date-fns/addDays';
import addMonths from 'date-fns/addMonths';
import startOfDay from 'date-fns/startOfDay';
import endOfDay from 'date-fns/endOfDay';
import startOfWeek from 'date-fns/startOfWeek';
import endOfWeek from 'date-fns/endOfWeek';
import endOfMonth from 'date-fns/endOfMonth';
import format from 'date-fns/format';
import startOfQuarter from 'date-fns/startOfQuarter';
import subMonths from 'date-fns/subMonths';
import endOfQuarter from 'date-fns/endOfQuarter';
import subQuarters from 'date-fns/subQuarters';
import addQuarters from 'date-fns/addQuarters';
import startOfMonth from 'date-fns/startOfMonth';
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
Vue.component('date-picker', DatePicker)
export default {
name: "Calendar",
created() {
// console.log('Now in calendar created');
this.ready = true;
this.locale = localStorage.locale ?? 'en-US';
},
data() {
return {
locale: 'en-US',
ready: false,
range: {
start: null,
end: null,
},
defaultRange: {
start: null,
end: null,
},
periods: []
};
},
methods: {
...mapMutations(
[
'setEnd',
'setStart',
],
),
resetDate: function () {
// console.log('Reset date to');
// console.log(this.defaultStart);
// console.log(this.defaultEnd);
this.range.start = this.defaultStart;
this.range.end = this.defaultEnd;
this.setStart(this.defaultStart);
this.setEnd(this.defaultEnd);
},
customDate: function (startStr, endStr) {
let start = new Date(startStr);
let end = new Date(endStr);
this.setStart(start);
this.setEnd(end);
this.range.start = start;
this.range.end = end;
this.generatePeriods()
return false;
},
generateDaily: function () {
let today = new Date(this.range.start);
// yesterday
this.periods.push(
{
start: startOfDay(subDays(today, 1)).toDateString(),
end: endOfDay(subDays(today, 1)).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(subDays(today, 1))
}
);
// today
this.periods.push(
{
start: startOfDay(today).toDateString(),
end: endOfDay(today).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(today)
}
);
// tomorrow:
this.periods.push(
{
start: startOfDay(addDays(today, 1)).toDateString(),
end: endOfDay(addDays(today, 1)).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(addDays(today, 1))
}
);
// The Day After Tomorrow dun-dun-dun!
this.periods.push(
{
start: startOfDay(addDays(today, 2)).toDateString(),
end: endOfDay(addDays(today, 2)).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(addDays(today, 2))
}
);
},
generateWeekly: function () {
//console.log('weekly');
let today = new Date(this.range.start);
//console.log('Today is ' + today);
let start = startOfDay(startOfWeek(subDays(today, 7), {weekStartsOn: 1}));
let end = endOfDay(endOfWeek(subDays(today, 7), {weekStartsOn: 1}));
let dateFormat = this.$t('config.week_in_year_fns');
//console.log('Date format: "'+dateFormat+'"');
let title = format(start, dateFormat);
// last week
// console.log('Last week');
// console.log(start);
// console.log(end);
// console.log(title);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this week
start = startOfDay(startOfWeek(today, {weekStartsOn: 1}));
end = endOfDay(endOfWeek(today, {weekStartsOn: 1}));
title = format(start, dateFormat);
// console.log('This week');
// console.log(start);
// console.log(end);
// console.log(title);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// next week
start = startOfDay(startOfWeek(addDays(today, 7), {weekStartsOn: 1}));
end = endOfDay(endOfWeek(addDays(today, 7), {weekStartsOn: 1}));
title = format(start, dateFormat);
// console.log('Next week');
// console.log(start);
// console.log(end);
// console.log(title);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
},
generateMonthly: function () {
let today = new Date(this.range.start);
// previous month
let start = startOfDay(startOfMonth(subMonths(today, 1)));
let end = endOfDay(endOfMonth(subMonths(today, 1)));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long'}).format(start)
}
);
// this month
start = startOfDay(startOfMonth(today));
end = endOfDay(endOfMonth(today));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long'}).format(start)
}
);
// next month
start = startOfDay(startOfMonth(addMonths(today, 1)));
end = endOfDay(endOfMonth(addMonths(today, 1)));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long'}).format(start)
}
);
},
generateQuarterly: function () {
let today = new Date(this.range.start);
// last quarter
let start = startOfDay(startOfQuarter(subQuarters(today, 1)));
let end = endOfDay(endOfQuarter(subQuarters(today, 1)));
let dateFormat = this.$t('config.quarter_fns');
let title = format(start, dateFormat);
// last week
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this quarter
start = startOfDay(startOfQuarter(today));
end = endOfDay(endOfQuarter(today));
title = format(start, dateFormat);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// next quarter
start = startOfDay(startOfQuarter(addQuarters(today, 1)));
end = endOfDay(endOfQuarter(addQuarters(today, 1)));
title = format(start, dateFormat);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
},
generateHalfYearly: function () {
let today = new Date(this.range.start);
let start;
let end;
let title = 'tbd';
let half = 1;
// its currently first half of year:
if (today.getMonth() <= 5) {
// previous year, last half:
start = today;
start.setFullYear(start.getFullYear() - 1);
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
half = 2;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this year, first half:
start = today;
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = today;
end.setMonth(5);
end.setDate(30);
end = endOfDay(start);
half = 1;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this year, second half:
start = today;
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
half = 2;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
return;
}
// this year, first half:
start = today;
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(5);
end.setDate(30);
end = endOfDay(end);
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this year, current (second) half:
start = today;
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = today;
end.setMonth(11);
end.setDate(31);
end = endOfDay(start);
half = 2;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// next year, first half:
start = today;
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(5);
end.setDate(30);
end = endOfDay(end);
half = 1;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
},
generateYearly: function () {
let today = new Date(this.range.start);
let start;
let end;
// last year
start = new Date(today);
start.setFullYear(start.getFullYear() - 1);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setFullYear(end.getFullYear() - 1);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: start.getFullYear()
}
);
// this year
start = new Date(today);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: start.getFullYear()
}
);
// next year
start = new Date(today);
start.setFullYear(start.getFullYear() + 1);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setFullYear(end.getFullYear() + 1);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: start.getFullYear()
}
);
},
generatePeriods: function () {
this.periods = [];
// console.log('The view range is "' + this.viewRange + '".');
switch (this.viewRange) {
case '1D':
this.generateDaily();
break;
case '1W':
this.generateWeekly();
break;
case '1M':
this.generateMonthly();
break;
case '3M':
this.generateQuarterly();
break;
case '6M':
this.generateHalfYearly();
break;
case '1Y':
this.generateYearly();
break;
}
// last 7 days
let today = new Date;
let end = new Date;
end.setDate(end.getDate() - 7);
this.periods.push(
{
start: end.toDateString(),
end: today.toDateString(),
title: this.$t('firefly.last_seven_days')
}
);
// last 30 days:
end.setDate(end.getDate() - 23);
this.periods.push(
{
start: end.toDateString(),
end: today.toDateString(),
title: this.$t('firefly.last_thirty_days')
}
);
// last 30 days
// everything
}
},
computed: {
...mapGetters([
'viewRange',
'start',
'end',
'defaultStart',
'defaultEnd'
]),
'datesReady': function () {
return null !== this.start && null !== this.end && this.ready;
},
},
watch: {
datesReady: function (value) {
if (false === value) {
return;
}
this.range.start = new Date(this.start);
this.range.end = new Date(this.end);
this.generatePeriods();
},
range: function (value) {
//console.log('User updated range');
this.setStart(value.start);
this.setEnd(value.end);
}
}
}
</script>
<style scoped>
.dropdown-item {
color: #212529;
}
.dropdown-item:hover {
color: #212529;
}
</style>

View File

@ -1,66 +0,0 @@
<!--
- ExampleComponent.vue
- Copyright (c) 2019 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<top-boxes/>
<div class="row">
<div class="col">
<main-account/>
</div>
</div>
<main-account-list/>
<div class="row">
<div class="col">
<main-budget-list/>
</div>
</div>
<div class="row">
<div class="col">
<main-category-list/>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<main-debit-list/>
</div>
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<main-credit-list/>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<main-piggy-list/>
</div>
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<main-bills-list/>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Dashboard",
}
</script>

View File

@ -1,108 +0,0 @@
<!--
- DashboardListLarge.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<table class="table table-striped table-sm">
<caption style="display:none;">{{ $t('firefly.transaction_table_description') }}</caption>
<thead>
<tr>
<th class="text-left" scope="col">{{ $t('firefly.description') }}</th>
<th scope="col">{{ $t('firefly.opposing_account') }}</th>
<th class="text-right" scope="col">{{ $t('firefly.amount') }}</th>
<th scope="col">{{ $t('firefly.category') }}</th>
<th scope="col">{{ $t('firefly.budget') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="transaction in this.transactions">
<td>
<a :href="'transactions/show/' + transaction.id " :title="transaction.date">
<span v-if="transaction.attributes.transactions.length > 1">{{ transaction.attributes.group_title }}</span>
<span v-if="1===transaction.attributes.transactions.length">{{ transaction.attributes.transactions[0].description }}</span>
</a>
</td>
<td>
<span v-for="tr in transaction.attributes.transactions">
<a v-if="'withdrawal' === tr.type" :href="'accounts/show/' + tr.destination_id">{{ tr.destination_name }}</a>
<a v-if="'deposit' === tr.type" :href="'accounts/show/' + tr.source_id">{{ tr.source_name }}</a>
<a v-if="'transfer' === tr.type && parseInt(tr.source_id) === account_id" :href="'accounts/show/' + tr.destination_id">{{ tr.destination_name }}</a>
<a v-if="'transfer' === tr.type && parseInt(tr.destination_id) === account_id" :href="'accounts/show/' + tr.source_id">{{ tr.source_name }}</a>
<br/>
</span>
</td>
<td style="text-align:right;">
<span v-for="tr in transaction.attributes.transactions">
<span v-if="'withdrawal' === tr.type" class="text-danger">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1) }}<br>
</span>
<span v-if="'deposit' === tr.type" class="text-success">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount) }}<br>
</span>
<span v-if="'transfer' === tr.type && parseInt(tr.source_id) === account_id" class="text-info">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1) }}<br>
</span>
<span v-if="'transfer' === tr.type && parseInt(tr.destination_id) === account_id" class="text-info">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount) }}<br>
</span>
</span>
</td>
<td>
<span v-for="tr in transaction.attributes.transactions">
<a v-if="0!==tr.category_id" :href="'categories/show/' + tr.category_id">{{ tr.category_name }}</a><br/>
</span>
</td>
<td>
<span v-for="tr in transaction.attributes.transactions">
<a v-if="0!==tr.budget_id" :href="'budgets/show/' + tr.budget_id">{{ tr.budget_name }}</a><br/>
</span>
</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
name: "DashboardListLarge",
data() {
return {
locale: 'en-US'
}
},
created() {
this.locale = localStorage.locale ?? 'en-US';
},
props: {
transactions: {
type: Array,
default: function () {
return [];
}
},
account_id: {
type: Number,
default: function () {
return 0;
}
},
}
}
</script>

View File

@ -1,96 +0,0 @@
<!--
- DashboardListMedium.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<table class="table table-striped table-sm">
<caption style="display:none;">{{ $t('firefly.transaction_table_description') }}</caption>
<thead>
<tr>
<th class="text-left" scope="col">{{ $t('firefly.description') }}</th>
<th scope="col">{{ $t('firefly.opposing_account') }}</th>
<th class="text-right" scope="col">{{ $t('firefly.amount') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="transaction in this.transactions">
<td>
<a :href="'transactions/show/' + transaction.id " :title="transaction.date">
<span v-if="transaction.attributes.transactions.length > 1">{{ transaction.attributes.group_title }}</span>
<span v-if="1===transaction.attributes.transactions.length">{{ transaction.attributes.transactions[0].description }}</span>
</a>
</td>
<td>
<span v-for="tr in transaction.attributes.transactions">
<a v-if="'withdrawal' === tr.type" :href="'accounts/show/' + tr.destination_id">{{ tr.destination_name }}</a>
<a v-if="'deposit' === tr.type" :href="'accounts/show/' + tr.source_id">{{ tr.source_name }}</a>
<a v-if="'transfer' === tr.type && parseInt(tr.source_id) === account_id" :href="'accounts/show/' + tr.destination_id">{{ tr.destination_name }}</a>
<a v-if="'transfer' === tr.type && parseInt(tr.destination_id) === account_id" :href="'accounts/show/' + tr.source_id">{{ tr.source_name }}</a>
<br/>
</span>
</td>
<td style="text-align:right;">
<span v-for="tr in transaction.attributes.transactions">
<span v-if="'withdrawal' === tr.type" class="text-danger">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1) }}<br>
</span>
<span v-if="'deposit' === tr.type" class="text-success">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount) }}<br>
</span>
<span v-if="'transfer' === tr.type && parseInt(tr.source_id) === account_id" class="text-info">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1) }}<br>
</span>
<span v-if="'transfer' === tr.type && parseInt(tr.destination_id) === account_id" class="text-info">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount) }}<br>
</span>
</span>
</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
name: "DashboardListMedium",
data() {
return {
locale: 'en-US'
}
},
created() {
this.locale = localStorage.locale ?? 'en-US';
},
props: {
transactions: {
type: Array,
default: function () {
return [];
}
},
account_id: {
type: Number,
default: function () {
return 0;
}
},
}
}
</script>

View File

@ -1,88 +0,0 @@
<!--
- DashboardListSmall.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<table class="table table-striped table-sm">
<caption style="display:none;">{{ $t('firefly.transaction_table_description') }}</caption>
<thead>
<tr>
<th class="text-left" scope="col">{{ $t('firefly.description') }}</th>
<th class="text-right" scope="col">{{ $t('firefly.amount') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="transaction in this.transactions">
<td>
<a :href="'transactions/show/' + transaction.id "
:title="new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'long', day: 'numeric' }).format(new Date(transaction.attributes.transactions[0].date))">
<span v-if="transaction.attributes.transactions.length > 1">{{ transaction.attributes.group_title }}</span>
<span v-if="1===transaction.attributes.transactions.length">{{ transaction.attributes.transactions[0].description }}</span>
</a>
</td>
<td style="text-align:right;">
<span v-for="tr in transaction.attributes.transactions">
<span v-if="'withdrawal' === tr.type" class="text-danger">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1) }}<br>
</span>
<span v-if="'deposit' === tr.type" class="text-success">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount) }}<br>
</span>
<span v-if="'transfer' === tr.type && parseInt(tr.source_id) === account_id" class="text-info">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1) }}<br>
</span>
<span v-if="'transfer' === tr.type && parseInt(tr.destination_id) === account_id" class="text-info">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount) }}<br>
</span>
</span>
</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
name: "DashboardListSmall",
data() {
return {
locale: 'en-US'
}
},
created() {
this.locale = localStorage.locale ?? 'en-US';
},
methods: {},
props: {
transactions: {
type: Array,
default: function () {
return [];
}
},
account_id: {
type: Number,
default: function () {
return 0;
}
},
}
}
</script>

View File

@ -1,146 +0,0 @@
<!--
- MainAccount.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title">{{ $t('firefly.yourAccounts') }}</h3>
</div>
<div class="card-body">
<div>
<canvas id="canvas" ref="canvas" width="400" height="400"></canvas>
</div>
<div v-if="loading && !error" class="text-center">
<span class="fas fa-spinner fa-spin"></span>
</div>
<div v-if="error" class="text-center">
<span class="fas fa-exclamation-triangle text-danger"></span>
</div>
</div>
<div class="card-footer">
<a class="btn btn-default button-sm" href="./accounts/asset"><span class="far fa-money-bill-alt"></span> {{ $t('firefly.go_to_asset_accounts') }}</a>
</div>
</div>
</template>
<script>
import DataConverter from "../charts/DataConverter";
import DefaultLineOptions from "../charts/DefaultLineOptions";
import {mapGetters} from "vuex";
import * as ChartJs from 'chart.js'
import format from "date-fns/format";
ChartJs.Chart.register.apply(null, Object.values(ChartJs).filter((chartClass) => (chartClass.id)));
export default {
name: "MainAccount",
components: {}, // MainAccountChart
data() {
return {
loading: true,
error: false,
ready: false,
initialised: false,
dataCollection: {},
chartOptions: {},
_chart: null,
}
},
created() {
this.chartOptions = DefaultLineOptions.methods.getDefaultOptions();
this.ready = true;
},
computed: {
...mapGetters('dashboard/index', ['start', 'end']),
'datesReady': function () {
return null !== this.start && null !== this.end && this.ready;
}
},
watch: {
datesReady: function (value) {
if (true === value) {
this.initialiseChart();
}
},
start: function () {
this.updateChart();
},
end: function () {
this.updateChart();
},
},
methods: {
initialiseChart: function () {
this.loading = true;
this.error = false;
//let startStr = this.start.toISOString().split('T')[0];
//let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
let url = './api/v1/chart/account/overview?start=' + startStr + '&end=' + endStr;
axios.get(url)
.then(response => {
let chartData = DataConverter.methods.convertChart(response.data);
chartData = DataConverter.methods.colorizeLineData(chartData);
this.dataCollection = chartData;
this.loading = false;
this.drawChart();
})
.catch(error => {
console.error('Has error!');
console.error(error);
this.error = true;
});
},
drawChart: function () {
//console.log('drawChart');
if ('undefined' !== typeof this._chart) {
// console.log('update!');
this._chart.data = this.dataCollection;
this._chart.update();
this.initialised = true;
}
if ('undefined' === typeof this._chart) {
// console.log('new!');
this._chart = new ChartJs.Chart(this.$refs.canvas.getContext('2d'), {
type: 'line',
data: this.dataCollection,
options: this.chartOptions
}
);
this.initialised = true;
}
},
updateChart: function () {
// console.log('updateChart');
if (this.initialised) {
// console.log('MUST Update chart!');
// reset some vars so it wont trigger again:
this.initialised = false;
this.initialiseChart();
}
}
},
}
</script>

View File

@ -1,35 +0,0 @@
<!--
- MainAccountChart.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<script>
//import {Line, mixins} from 'vue-chartjs'
const {reactiveProp} = mixins
export default {
extends: Line,
mixins: [reactiveProp],
props: ['options'],
mounted() {
this.renderChart(this.chartData, this.options)
}
}
</script>

View File

@ -1,181 +0,0 @@
<!--
- MainAccountList.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<!-- row if loading -->
<div v-if="loading && !error" class="row">
<div class="col">
<div class="card">
<div class="card-body">
<div class="text-center">
<span class="fas fa-spinner fa-spin"></span>
</div>
</div>
</div>
</div>
</div>
<!-- row if error -->
<div v-if="error" class="row">
<div class="col">
<div class="card">
<div class="card-body">
<div class="text-center">
<span class="fas fa-exclamation-triangle text-danger"></span>
</div>
</div>
</div>
</div>
</div>
<!-- row if normal -->
<div v-if="!loading && !error" class="row">
<div
v-for="account in accounts"
v-bind:class="{ 'col-lg-12': 1 === accounts.length, 'col-lg-6': 2 === accounts.length, 'col-lg-4': accounts.length > 2 }">
<div class="card">
<div class="card-header">
<h3 class="card-title"><a :href="account.url">{{ account.title }}</a></h3>
<div class="card-tools">
<span :class="parseFloat(account.current_balance) < 0 ? 'text-danger' : 'text-success'">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: account.currency_code}).format(parseFloat(account.current_balance)) }}
</span>
</div>
</div>
<div class="card-body table-responsive p-0">
<div>
<dashboard-list-large v-if="1===accounts.length" :account_id="account.id" :transactions="account.transactions"/>
<dashboard-list-medium v-if="2===accounts.length" :account_id="account.id" :transactions="account.transactions"/>
<dashboard-list-small v-if="accounts.length > 2" :account_id="account.id" :transactions="account.transactions"/>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
export default {
name: "MainAccountList",
data() {
return {
loading: true,
error: false,
ready: false,
accounts: [],
locale: 'en-US'
}
},
created() {
this.locale = localStorage.locale ?? 'en-US';
this.ready = true;
},
computed: {
...mapGetters([
'start',
'end'
]),
'datesReady': function () {
return null !== this.start && null !== this.end && this.ready;
},
},
watch: {
datesReady: function (value) {
if (true === value) {
this.initialiseList();
}
},
start: function () {
if (false === this.loading) {
this.initialiseList();
}
},
end: function () {
if (false === this.loading) {
this.initialiseList();
}
},
},
methods: {
initialiseList: function () {
this.loading = true;
this.accounts = [];
axios.get('./api/v1/preferences/frontPageAccounts')
.then(response => {
this.loadAccounts(response);
}
);
},
loadAccounts(response) {
let accountIds = response.data.data.attributes.data;
for (let key in accountIds) {
if (accountIds.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
this.accounts.push({
id: accountIds[key],
title: '',
url: '',
include: false,
current_balance: '0',
currency_code: 'EUR',
transactions: []
});
this.loadSingleAccount(key, accountIds[key]);
}
}
},
loadSingleAccount(key, accountId) {
axios.get('./api/v1/accounts/' + accountId)
.then(response => {
let account = response.data.data;
if ('asset' === account.attributes.type || 'liabilities' === account.attributes.type) {
this.accounts[key].title = account.attributes.name;
this.accounts[key].url = './accounts/show/' + account.id;
this.accounts[key].current_balance = account.attributes.current_balance;
this.accounts[key].currency_code = account.attributes.currency_code;
this.accounts[key].include = true;
this.loadTransactions(key, accountId);
}
}
);
},
loadTransactions(key, accountId) {
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/accounts/' + accountId + '/transactions?page=1&limit=10&start=' + startStr + '&end=' + endStr)
.then(response => {
this.accounts[key].transactions = response.data.data;
this.loading = false;
this.error = false;
}
);
},
}
}
</script>

View File

@ -1,164 +0,0 @@
<!--
- MainBills.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title">{{ $t('firefly.bills') }}</h3>
</div>
<!-- body if loading -->
<div v-if="loading && !error" class="card-body">
<div class="text-center">
<span class="fas fa-spinner fa-spin"></span>
</div>
</div>
<!-- body if error -->
<div v-if="error" class="card-body">
<div class="text-center">
<span class="fas fa-exclamation-triangle text-danger"></span>
</div>
</div>
<!-- body if normal -->
<div v-if="!loading && !error" class="card-body table-responsive p-0">
<table class="table table-striped">
<caption style="display:none;">{{ $t('firefly.bills') }}</caption>
<thead>
<tr>
<th scope="col" style="width:35%;">{{ $t('list.name') }}</th>
<th scope="col" style="width:25%;">{{ $t('list.next_expected_match') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="bill in this.bills">
<td><a :href="'./bills/show/' + bill.id" :title="bill.attributes.name">{{ bill.attributes.name }}</a>
(~ <span class="text-danger">{{
Intl.NumberFormat(locale, {style: 'currency', currency: bill.attributes.currency_code}).format((parseFloat(bill.attributes.amount_min) +
parseFloat(bill.attributes.amount_max)) / -2)
}}</span>)
<small v-if="bill.attributes.object_group_title" class="text-muted">
<br/>
{{ bill.attributes.object_group_title }}
</small>
</td>
<td>
<span v-for="paidDate in bill.attributes.paid_dates">
<span v-html="renderPaidDate(paidDate)"/><br/>
</span>
<span v-for="payDate in bill.attributes.pay_dates" v-if="0===bill.attributes.paid_dates.length">
{{ new Intl.DateTimeFormat(locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(new Date(payDate)) }}
<br/>
</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer">
<a class="btn btn-default button-sm" href="./bills"><span class="far fa-money-bill-alt"></span> {{ $t('firefly.go_to_bills') }}</a>
</div>
</div>
</template>
<script>
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
export default {
name: "MainBillsList",
data() {
return {
bills: [],
locale: 'en-US',
ready: false,
loading: true,
error: false
}
},
computed: {
...mapGetters([
'start',
'end'
]),
'datesReady': function () {
return null !== this.start && null !== this.end && this.ready;
}
},
watch: {
datesReady: function (value) {
if (true === value) {
this.initialiseBills();
}
},
start: function () {
if (false === this.loading) {
this.initialiseBills();
}
},
end: function () {
if (false === this.loading) {
this.initialiseBills();
}
},
},
created() {
this.ready = true;
this.locale = localStorage.locale ?? 'en-US';
},
components: {},
methods: {
initialiseBills: function () {
this.loading = true;
this.bills = [];
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/bills?start=' + startStr + '&end=' + endStr)
.then(response => {
this.loadBills(response.data.data);
}
).catch(error => {
this.error = true;
this.loading = false;
});
},
renderPaidDate: function (obj) {
let dateStr = new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(new Date(obj.date));
let str = this.$t('firefly.bill_paid_on', {date: dateStr});
return '<a href="./transactions/show/' + obj.transaction_group_id + '" title="' + str + '">' + str + '</a>';
},
loadBills: function (data) {
for (let key in data) {
if (data.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
let bill = data[key];
let active = bill.attributes.active;
if (bill.attributes.pay_dates.length > 0 && active) {
this.bills.push(bill);
}
}
}
this.error = false;
this.loading = false;
}
}
}
</script>

View File

@ -1,278 +0,0 @@
<!--
- MainBudgetList.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<!-- daily budgets (will be the exception, I expect) -->
<div v-if="!loading" class="row">
<div v-if="budgetLimits.daily.length > 0" class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12">
<BudgetListGroup :budgetLimits=budgetLimits.daily :title="$t('firefly.daily_budgets')"
/>
</div>
<div v-if="budgetLimits.weekly.length > 0" class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12">
<BudgetListGroup :budgetLimits=budgetLimits.weekly :title="$t('firefly.weekly_budgets')"
/>
</div>
<div v-if="budgetLimits.monthly.length > 0" class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12">
<BudgetListGroup :budgetLimits=budgetLimits.monthly :title="$t('firefly.monthly_budgets')"
/>
</div>
<div v-if="budgetLimits.quarterly.length > 0" class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12">
<BudgetListGroup :budgetLimits=budgetLimits.quarterly :title="$t('firefly.quarterly_budgets')"
/>
</div>
<div v-if="budgetLimits.half_year.length > 0" class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12">
<BudgetListGroup :budgetLimits=budgetLimits.half_year :title="$t('firefly.half_year_budgets')"
/>
</div>
<div v-if="budgetLimits.yearly.length > 0" class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12">
<BudgetListGroup :budgetLimits=budgetLimits.yearly :title="$t('firefly.yearly_budgets')"
/>
</div>
<div v-if="budgetLimits.other.length > 0 || rawBudgets.length > 0" class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12">
<BudgetListGroup :budgetLimits=budgetLimits.other :budgets="rawBudgets" :title="$t('firefly.other_budgets')"
/>
</div>
</div>
<div v-if="loading && !error" class="row">
<div class="col">
<div class="card">
<div class="card-body">
<div class="text-center">
<span class="fas fa-spinner fa-spin"></span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import BudgetListGroup from "./BudgetListGroup";
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
export default {
name: "MainBudgetList",
components: {BudgetListGroup},
data() {
return {
budgetList: ['daily', 'weekly', 'monthly', 'quarterly', 'half_year', 'yearly', 'other'],
budgetLimits: {
daily: [],
weekly: [],
monthly: [],
quarterly: [],
half_year: [],
yearly: [],
other: [],
},
budgets: {}, // used to collect some meta data.
rawBudgets: [],
locale: 'en-US',
ready: false,
loading: true,
error: false
}
},
created() {
this.ready = true;
this.locale = localStorage.locale ?? 'en-US';
},
watch: {
datesReady: function (value) {
if (true === value) {
this.getBudgets();
}
},
start: function () {
if (false === this.loading) {
this.getBudgets();
}
},
end: function () {
if (false === this.loading) {
this.getBudgets();
}
},
},
computed: {
...mapGetters(['start', 'end']),
'datesReady': function () {
return null !== this.start && null !== this.end && this.ready;
}
},
methods:
{
getBudgets: function () {
this.budgets = {};
this.rawBudgets = [];
this.budgetLimits = {
daily: [],
weekly: [],
monthly: [],
quarterly: [],
half_year: [],
yearly: [],
other: [],
};
this.loading = true;
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/budgets?start=' + startStr + '&end=' + endStr)
.then(response => {
this.parseBudgets(response.data);
}
);
},
parseBudgets(data) {
for (let i in data.data) {
if (data.data.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = data.data[i];
if (false === current.attributes.active) {
// skip inactive budgets
continue;
}
for (let ii in current.attributes.spent) {
if (current.attributes.spent.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
let spentData = current.attributes.spent[ii];
this.rawBudgets.push(
{
id: parseInt(current.id),
name: current.attributes.name,
currency_id: parseInt(spentData.currency_id),
currency_code: spentData.currency_code,
spent: spentData.sum
}
);
//console.log('Added budget ' + current.attributes.name + ' (' + spentData.currency_code + ')');
}
}
}
}
this.getBudgetLimits();
},
getBudgetLimits() {
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/budget-limits?start=' + startStr + '&end=' + endStr)
.then(response => {
this.parseBudgetLimits(response.data);
this.loading = false;
}
);
},
parseBudgetLimits(data) {
// collect budget meta data.
for (let i in data.included) {
if (data.included.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = data.included[i];
let currentId = parseInt(current.id);
this.budgets[currentId] =
{
id: currentId,
name: current.attributes.name,
};
//console.log('Collected meta data: budget #' + currentId + ' is named ' + current.attributes.name);
}
}
for (let i in data.data) {
if (data.data.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = data.data[i];
let currentId = parseInt(current.id);
let budgetId = parseInt(current.attributes.budget_id);
let currencyId = parseInt(current.attributes.currency_id);
let spentFloat = parseFloat(current.attributes.spent);
let spentFloatPos = parseFloat(current.attributes.spent) * -1;
let amount = parseFloat(current.attributes.amount);
let period = current.attributes.period ?? 'other';
let pctGreen = 0;
let pctOrange = 0;
let pctRed = 0;
//console.log('Collected "' + period + '" budget limit #' + currentId + ' (part of budget #' + budgetId + ')');
// console.log('Spent ' + spentFloatPos + ' of ' + amount);
// remove budget info from rawBudgets if it's there:
this.filterBudgets(budgetId, currencyId);
// let name = this.budgets[current.attributes.budget_id].name;
// spent within budget:
if (0.0 !== spentFloat && spentFloatPos < amount) {
// console.log('Spent ' + name + ' in budget');
pctGreen = (spentFloatPos / amount) * 100;
// console.log('pctGreen is ' + pctGreen);
}
// spent over budget
if (0.0 !== spentFloatPos && spentFloatPos > amount) {
//console.log('Spent ' + name + ' OVER budget');
pctOrange = (amount / spentFloatPos) * 100;
pctRed = 100 - pctOrange;
//console.log('orange is ' + pctOrange);
//console.log('red is ' + pctRed);
}
// spent exactly on budget
if (0.0 !== spentFloatPos && spentFloatPos === amount) {
pctOrange = 100;
pctRed = 0;
}
let obj = {
id: currentId,
amount: current.attributes.amount,
budget_id: budgetId,
budget_name: this.budgets[current.attributes.budget_id].name,
currency_id: currencyId,
currency_code: current.attributes.currency_code,
period: current.attributes.period,
start: new Date(current.attributes.start),
end: new Date(current.attributes.end),
spent: current.attributes.spent,
pctGreen: pctGreen,
pctOrange: pctOrange,
pctRed: pctRed,
};
this.budgetLimits[period].push(obj);
}
}
},
filterBudgets(budgetId, currencyId) {
for (let i in this.rawBudgets) {
if (this.rawBudgets.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
if (this.rawBudgets[i].currency_id === currencyId && this.rawBudgets[i].id === budgetId) {
//console.log('Budget ' + this.rawBudgets[i].name + ' with currency ' + this.rawBudgets[i].currency_code + ' will be removed in favor of a budget limit.');
this.rawBudgets.splice(parseInt(i), 1);
}
}
}
}
}
}
</script>

View File

@ -1,250 +0,0 @@
<!--
- MainCategoryList.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title">{{ $t('firefly.categories') }}</h3>
</div>
<!-- body if loading -->
<div v-if="loading && !error" class="card-body">
<div class="text-center">
<span class="fas fa-spinner fa-spin"></span>
</div>
</div>
<!-- body if error -->
<div v-if="error" class="card-body">
<div class="text-center">
<span class="fas fa-exclamation-triangle text-danger"></span>
</div>
</div>
<!-- body if normal -->
<div v-if="!loading && !error" class="card-body table-responsive p-0">
<table class="table table-sm">
<caption style="display:none;">{{ $t('firefly.categories') }}</caption>
<thead>
<tr>
<th scope="col">{{ $t('firefly.category') }}</th>
<th scope="col">{{ $t('firefly.spent') }} / {{ $t('firefly.earned') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="category in sortedList">
<td style="width:20%;">
<a :href="'./categories/show/' + category.id">{{ category.name }}</a>
</td>
<td class="align-middle">
<!-- SPENT -->
<div v-if="category.spentPct > 0" class="progress">
<div :aria-valuenow="category.spentPct" :style="{ width: category.spentPct + '%'}" aria-valuemax="100"
aria-valuemin="0" class="progress-bar progress-bar-striped bg-danger"
role="progressbar">
<span v-if="category.spentPct > 20">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: category.currency_code}).format(category.spent) }}
</span>
</div>
<span v-if="category.spentPct <= 20" class="progress-label" style="line-height: 16px;">&nbsp;
{{ Intl.NumberFormat(locale, {style: 'currency', currency: category.currency_code}).format(category.spent) }}
</span>
</div>
<!-- EARNED -->
<div v-if="category.earnedPct > 0" class="progress justify-content-end" title="hello2">
<span v-if="category.earnedPct <= 20" style="line-height: 16px;">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: category.currency_code}).format(category.earned) }}
&nbsp;</span>
<div :aria-valuenow="category.earnedPct" :style="{ width: category.earnedPct + '%'}" aria-valuemax="100"
aria-valuemin="0" class="progress-bar progress-bar-striped bg-success"
role="progressbar" title="hello">
<span v-if="category.earnedPct > 20">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: category.currency_code}).format(category.earned) }}
</span>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
export default {
name: "MainCategoryList",
created() {
this.locale = localStorage.locale ?? 'en-US';
this.ready = true;
},
data() {
return {
locale: 'en-US',
categories: [],
sortedList: [],
spent: 0,
earned: 0,
loading: true,
error: false
}
},
computed: {
...mapGetters(['start', 'end']),
'datesReady': function () {
return null !== this.start && null !== this.end && this.ready;
}
},
watch: {
datesReady: function (value) {
if (true === value) {
this.getCategories();
}
},
start: function () {
if (false === this.loading) {
this.getCategories();
}
},
end: function () {
if (false === this.loading) {
this.getCategories();
}
},
},
methods:
{
getCategories: function () {
this.categories = [];
this.sortedList = [];
this.spent = 0;
this.earned = 0;
this.loading = true;
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
this.getCategoryPage(startStr, endStr, 1);
},
getCategoryPage: function (start, end, page) {
axios.get('./api/v1/categories?start=' + start + '&end=' + end + '&page=' + page)
.then(response => {
let categories = response.data.data;
let currentPage = parseInt(response.data.meta.pagination.current_page);
let totalPages = parseInt(response.data.meta.pagination.total_pages);
this.parseCategories(categories);
if (currentPage < totalPages) {
let nextPage = currentPage + 1;
this.getCategoryPage(start, end, nextPage);
}
if (currentPage === totalPages) {
this.loading = false;
this.sortCategories();
}
}
).catch(error => {
this.error = true;
});
},
parseCategories(data) {
for (let i in data) {
if (data.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = data[i];
let entryKey = null;
let categoryId = parseInt(current.id);
// loop spent info:
for (let ii in current.attributes.spent) {
if (current.attributes.spent.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
let spentData = current.attributes.spent[ii];
entryKey = spentData.currency_id + '-' + current.id;
// does the categories list thing have this combo? if not, create it.
this.categories[entryKey] = this.categories[entryKey] ??
{
id: categoryId,
name: current.attributes.name,
currency_code: spentData.currency_code,
currency_symbol: spentData.currency_symbol,
spent: 0,
earned: 0,
spentPct: 0,
earnedPct: 0,
};
this.categories[entryKey].spent = parseFloat(spentData.sum);
this.spent = parseFloat(spentData.sum) < this.spent ? parseFloat(spentData.sum) : this.spent;
}
}
// loop earned info
for (let ii in current.attributes.earned) {
if (current.attributes.earned.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
let earnedData = current.attributes.earned[ii];
entryKey = earnedData.currency_id + '-' + current.id;
// does the categories list thing have this combo? if not, create it.
this.categories[entryKey] = this.categories[entryKey] ??
{
id: categoryId,
name: current.attributes.name,
currency_code: earnedData.currency_code,
currency_symbol: earnedData.currency_symbol,
spent: 0,
earned: 0,
spentPct: 0,
earnedPct: 0,
};
this.categories[entryKey].earned = parseFloat(earnedData.sum);
this.earned = parseFloat(earnedData.sum) > this.earned ? parseFloat(earnedData.sum) : this.earned;
}
}
}
}
},
sortCategories() {
// no longer care about keys:
let array = [];
for (let i in this.categories) {
if (this.categories.hasOwnProperty(i)) {
array.push(this.categories[i]);
}
}
array.sort(function (one, two) {
return (one.spent + one.earned) - (two.spent + two.earned);
});
for (let i in array) {
if (array.hasOwnProperty(i)) {
let current = array[i];
current.spentPct = (current.spent / this.spent) * 100;
current.earnedPct = (current.earned / this.earned) * 100;
this.sortedList.push(current);
}
}
}
}
}
</script>

View File

@ -1,169 +0,0 @@
<!--
- MainCreditList.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title">{{ $t('firefly.revenue_accounts') }}</h3>
</div>
<!-- body if loading -->
<div v-if="loading && !error" class="card-body">
<div class="text-center">
<span class="fas fa-spinner fa-spin"></span>
</div>
</div>
<!-- body if error -->
<div v-if="error" class="card-body">
<div class="text-center">
<span class="fas fa-exclamation-triangle text-danger"></span>
</div>
</div>
<!-- body if normal -->
<div v-if="!loading && !error" class="card-body table-responsive p-0">
<table class="table table-sm">
<caption style="display:none;">{{ $t('firefly.revenue_accounts') }}</caption>
<thead>
<tr>
<th scope="col">{{ $t('firefly.account') }}</th>
<th scope="col">{{ $t('firefly.earned') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in income">
<td style="width:20%;"><a :href="'./accounts/show/' + entry.id">{{ entry.name }}</a></td>
<td class="align-middle">
<div v-if="entry.pct > 0" class="progress">
<div :aria-valuenow="entry.pct" :style="{ width: entry.pct + '%'}" aria-valuemax="100"
aria-valuemin="0" class="progress-bar bg-success"
role="progressbar">
<span v-if="entry.pct > 20">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float) }}
</span>
</div>
<span v-if="entry.pct <= 20" style="line-height: 16px;">&nbsp;
{{ Intl.NumberFormat(locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float) }}
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer">
<a class="btn btn-default button-sm" href="./transactions/deposit"><span class="far fa-money-bill-alt"></span> {{ $t('firefly.go_to_deposits') }}</a>
</div>
</div>
</template>
<script>
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
// See reference nr. 2
export default {
name: "MainCreditList",
data() {
return {
locale: 'en-US',
income: [],
max: 0,
loading: true,
error: false
}
},
created() {
this.locale = localStorage.locale ?? 'en-US';
this.ready = true;
},
computed: {
...mapGetters([
'start',
'end'
]),
'datesReady': function () {
return null !== this.start && null !== this.end && this.ready;
}
},
watch: {
datesReady: function (value) {
if (true === value) {
this.getIncome();
}
},
start: function () {
if (false === this.loading) {
this.getIncome();
}
},
end: function () {
if (false === this.loading) {
this.getIncome();
}
},
},
methods: {
getIncome() {
this.loading = true;
this.income = [];
this.error = false;
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/insight/income/revenue?start=' + startStr + '&end=' + endStr)
.then(response => {
// do something with response.
this.parseIncome(response.data);
this.loading = false;
}).catch(error => {
this.error = true
});
},
parseIncome(data) {
for (let i in data) {
if (data.hasOwnProperty(i)) {
let mainKey = parseInt(i);
// contains currency info and entries.
let current = data[mainKey];
current.pct = 0;
this.max = current.difference_float > this.max ? current.difference_float : this.max;
this.income.push(current);
}
}
if (0 === this.max) {
this.max = 1;
}
// now sort + pct:
for (let i in this.income) {
if (this.income.hasOwnProperty(i)) {
let current = this.income[i];
current.pct = (current.difference_float / this.max) * 100;
this.income[i] = current;
}
}
this.income.sort((a,b) => (a.pct > b.pct) ? -1 : ((b.pct > a.pct) ? 1 : 0));
}
}
}
</script>

View File

@ -1,169 +0,0 @@
<!--
- MainDebitList.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title">{{ $t('firefly.expense_accounts') }}</h3>
</div>
<!-- body if loading -->
<div v-if="loading && !error" class="card-body">
<div class="text-center">
<span class="fas fa-spinner fa-spin"></span>
</div>
</div>
<!-- body if error -->
<div v-if="error" class="card-body">
<div class="text-center">
<span class="fas fa-exclamation-triangle text-danger"></span>
</div>
</div>
<!-- body if normal -->
<div v-if="!loading && !error" class="card-body table-responsive p-0">
<table class="table table-sm">
<caption style="display:none;">{{ $t('firefly.expense_accounts') }}</caption>
<thead>
<tr>
<th scope="col">{{ $t('firefly.account') }}</th>
<th scope="col">{{ $t('firefly.spent') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in expenses">
<td style="width:20%;"><a :href="'./accounts/show/' + entry.id">{{ entry.name }}</a></td>
<td class="align-middle">
<div v-if="entry.pct > 0" class="progress">
<div :aria-valuenow="entry.pct" :style="{ width: entry.pct + '%'}" aria-valuemax="100"
aria-valuemin="0" class="progress-bar bg-danger"
role="progressbar">
<span v-if="entry.pct > 20">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float) }}
</span>
</div>
<span v-if="entry.pct <= 20" style="line-height: 16px;">&nbsp;
{{ Intl.NumberFormat(locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float) }}
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer">
<a class="btn btn-default button-sm" href="./transactions/withdrawal"><span class="far fa-money-bill-alt"></span> {{ $t('firefly.go_to_withdrawals') }}</a>
</div>
</div>
</template>
<script>
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
export default {
name: "MainDebitList",
data() {
return {
locale: 'en-US',
expenses: [],
min: 0,
loading: true,
error: false
}
},
created() {
this.locale = localStorage.locale ?? 'en-US';
this.ready = true;
},
computed: {
...mapGetters([
'start',
'end'
]),
'datesReady': function () {
return null !== this.start && null !== this.end && this.ready;
}
},
watch: {
datesReady: function (value) {
if (true === value) {
this.getExpenses();
}
},
start: function () {
if (false === this.loading) {
this.getExpenses();
}
},
end: function () {
if (false === this.loading) {
this.getExpenses();
}
},
},
methods: {
getExpenses() {
this.loading = true;
this.error = false;
this.expenses = [];
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/insight/expense/expense?start=' + startStr + '&end=' + endStr)
.then(response => {
// do something with response.
this.parseExpenses(response.data);
this.loading = false
}).catch(error => {
this.error = true
});
},
parseExpenses(data) {
for (let mainKey in data) {
if (data.hasOwnProperty(mainKey) && /^0$|^[1-9]\d*$/.test(mainKey) && mainKey <= 4294967294) {
let current = data[mainKey];
current.pct = 0;
this.min = current.difference_float < this.min ? current.difference_float : this.min;
this.expenses.push(current);
}
}
if (0 === this.min) {
this.min = -1;
}
// now sort + pct:
for (let i in this.expenses) {
if (this.expenses.hasOwnProperty(i)) {
let current = this.expenses[i];
current.pct = (current.difference_float*-1 / this.min*-1) * 100;
this.expenses[i] = current;
}
}
this.expenses.sort((a,b) => (a.pct > b.pct) ? -1 : ((b.pct > a.pct) ? 1 : 0));
}
}
}
</script>

View File

@ -1,129 +0,0 @@
<!--
- MainPiggyList.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title">{{ $t('firefly.piggy_banks') }}</h3>
</div>
<!-- body if loading -->
<div v-if="loading && !error" class="card-body">
<div class="text-center">
<span class="fas fa-spinner fa-spin"></span>
</div>
</div>
<!-- body if error -->
<div v-if="error" class="card-body">
<div class="text-center">
<span class="fas fa-exclamation-triangle text-danger"></span>
</div>
</div>
<!-- body if normal -->
<div v-if="!loading && !error" class="card-body table-responsive p-0">
<table class="table table-striped">
<caption style="display:none;">{{ $t('firefly.piggy_banks') }}</caption>
<thead>
<tr>
<th scope="col" style="width:35%;">{{ $t('list.piggy_bank') }}</th>
<th scope="col" style="width:40%;">{{ $t('list.percentage') }} <small>/ {{ $t('list.amount') }}</small></th>
</tr>
</thead>
<tbody>
<tr v-for="piggy in this.piggy_banks">
<td>
<a :href="'./piggy-banks/show/' + piggy.id" :title="piggy.attributes.name">{{ piggy.attributes.name }}</a>
<small v-if="piggy.attributes.object_group_title" class="text-muted">
<br/>
{{ piggy.attributes.object_group_title }}
</small>
</td>
<td>
<div class="progress-group">
<div class="progress progress-sm">
<div v-if="piggy.attributes.pct < 100" :style="{'width': piggy.attributes.pct + '%'}" class="progress-bar primary"></div>
<div v-if="100 === piggy.attributes.pct" :style="{'width': piggy.attributes.pct + '%'}"
class="progress-bar progress-bar-striped bg-success"></div>
</div>
</div>
<span class="text-success">
{{
Intl.NumberFormat(locale, {style: 'currency', currency: piggy.attributes.currency_code}).format(piggy.attributes.current_amount)
}}
</span>
of
<span class="text-success">{{
Intl.NumberFormat(locale, {
style: 'currency',
currency: piggy.attributes.currency_code
}).format(piggy.attributes.target_amount)
}}</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer">
<a class="btn btn-default button-sm" href="./piggy-banks"><span class="far fa-money-bill-alt"></span> {{ $t('firefly.go_to_piggies') }}</a>
</div>
</div>
</template>
<script>
export default {
name: "MainPiggyList",
data() {
return {
piggy_banks: [],
loading: true,
error: false,
locale: 'en-US'
}
},
created() {
this.locale = localStorage.locale ?? 'en-US';
axios.get('./api/v1/piggy_banks')
.then(response => {
this.loadPiggyBanks(response.data.data);
this.loading = false;
}
).catch(error => {
this.error = true
});
},
methods: {
loadPiggyBanks(data) {
for (let key in data) {
if (data.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
let piggy = data[key];
if (0.0 !== parseFloat(piggy.attributes.left_to_save)) {
piggy.attributes.pct = (parseFloat(piggy.attributes.current_amount) / parseFloat(piggy.attributes.target_amount)) * 100;
this.piggy_banks.push(piggy);
}
}
}
this.piggy_banks.sort(function (a, b) {
return b.attributes.pct - a.attributes.pct;
});
}
}
}
</script>

View File

@ -1,313 +0,0 @@
<!--
- TopBoxes.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="row">
<div class="col" v-if="0 !== prefCurrencyBalances.length || 0 !== notPrefCurrencyBalances.length">
<div class="info-box">
<span class="info-box-icon"><span class="far fa-bookmark text-info"></span></span>
<div class="info-box-content">
<span v-if="!loading && !error" class="info-box-text">{{ $t("firefly.balance") }}</span>
<span v-if="loading && !error" class="info-box-text"><span class="fas fa-spinner fa-spin"></span></span>
<span v-if="error" class="info-box-text"><span class="fas fa-exclamation-triangle text-danger"></span></span>
<!-- balance in preferred currency -->
<span v-for="balance in prefCurrencyBalances" :title="balance.sub_title" class="info-box-number">{{ balance.value_parsed }}</span>
<span v-if="0 === prefCurrencyBalances.length" class="info-box-number">&nbsp;</span>
<div class="progress bg-info">
<div class="progress-bar" style="width: 0"></div>
</div>
<!-- balance in not preferred currency -->
<span class="progress-description">
<span v-for="(balance, index) in notPrefCurrencyBalances" :title="balance.sub_title">
{{ balance.value_parsed }}<span v-if="index+1 !== notPrefCurrencyBalances.length">, </span>
</span>
<span v-if="0===notPrefCurrencyBalances.length">&nbsp;</span>
</span>
</div>
</div>
</div>
<div class="col" v-if="0!==prefBillsUnpaid.length || 0 !== notPrefBillsUnpaid.length">
<div class="info-box">
<span class="info-box-icon"><span class="far fa-calendar-alt text-teal"></span></span>
<div class="info-box-content">
<span v-if="!loading && !error" class="info-box-text">{{ $t('firefly.bills_to_pay') }}</span>
<span v-if="loading && !error" class="info-box-text"><span class="fas fa-spinner fa-spin"></span></span>
<span v-if="error" class="info-box-text"><span class="fas fa-exclamation-triangle text-danger"></span></span>
<!-- bills unpaid, in preferred currency. -->
<span v-for="balance in prefBillsUnpaid" class="info-box-number">{{ balance.value_parsed }}</span>
<div class="progress bg-teal">
<div class="progress-bar" style="width: 0"></div>
</div>
<!-- bills unpaid, in other currencies. -->
<span class="progress-description">
<span v-for="(bill, index) in notPrefBillsUnpaid">
{{ bill.value_parsed }}<span v-if="index+1 !== notPrefBillsUnpaid.length">, </span>
</span>
<span v-if="0===notPrefBillsUnpaid.length">&nbsp;</span>
</span>
</div>
</div>
</div>
<!-- left to spend -->
<div class="col" v-if="0 !== prefLeftToSpend.length || 0 !== notPrefLeftToSpend.length">
<div class="info-box">
<span class="info-box-icon"><span class="fas fa-money-bill text-success"></span></span>
<div class="info-box-content">
<span v-if="!loading && !error" class="info-box-text">{{ $t('firefly.left_to_spend') }}</span>
<span v-if="loading && !error" class="info-box-text"><span class="fas fa-spinner fa-spin"></span></span>
<span v-if="error" class="info-box-text"><span class="fas fa-exclamation-triangle text-danger"></span></span>
<!-- left to spend in preferred currency -->
<span v-for="left in prefLeftToSpend" :title="left.sub_title" class="info-box-number">{{ left.value_parsed }}</span>
<span v-if="0 === prefLeftToSpend.length" class="info-box-number">&nbsp;</span>
<div class="progress bg-success">
<div class="progress-bar" style="width: 0"></div>
</div>
<!-- other currencies-->
<span class="progress-description">
<span v-for="(left, index) in notPrefLeftToSpend">
{{ left.value_parsed }}<span v-if="index+1 !== notPrefLeftToSpend.length">, </span>
</span>
<span v-if="0===notPrefLeftToSpend.length">&nbsp;</span>
</span>
</div>
</div>
</div>
<!-- net worth -->
<div class="col" v-if="0 !== notPrefNetWorth.length || 0 !== prefNetWorth.length">
<div class="info-box">
<span class="info-box-icon"><span class="fas fa-money-bill text-success"></span></span>
<div class="info-box-content">
<span v-if="!loading && !error" class="info-box-text">{{ $t('firefly.net_worth') }}</span>
<span v-if="loading && !error" class="info-box-text"><span class="fas fa-spinner fa-spin"></span></span>
<span v-if="error" class="info-box-text"><span class="fas fa-exclamation-triangle text-danger"></span></span>
<span v-for="nw in prefNetWorth" :title="nw.sub_title" class="info-box-number">{{ nw.value_parsed }}</span>
<span v-if="0===prefNetWorth.length">&nbsp;</span>
<div class="progress bg-success">
<div class="progress-bar" style="width: 0"></div>
</div>
<span class="progress-description">
<span v-for="(nw, index) in notPrefNetWorth">
{{ nw.value_parsed }}<span v-if="index+1 !== notPrefNetWorth.length">, </span>
</span>
<span v-if="0===notPrefNetWorth.length">&nbsp;</span>
</span>
</div>
</div>
</div>
</div>
</template>
<script>
import {createNamespacedHelpers} from "vuex";
import format from 'date-fns/format';
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
export default {
name: "TopBoxes",
props: {},
data() {
return {
summary: [],
balances: [],
billsPaid: [],
billsUnpaid: [],
leftToSpend: [],
netWorth: [],
loading: true,
error: false,
ready: false
}
},
computed: {
...mapGetters([
'start',
'end'
]),
'datesReady': function () {
return null !== this.start && null !== this.end && this.ready;
},
// contains only balances with preferred currency.
prefCurrencyBalances: function () {
return this.filterOnCurrency(this.balances);
},
notPrefCurrencyBalances: function () {
return this.filterOnNotCurrency(this.balances);
},
// contains only bills unpaid in preferred currency or first one.
prefBillsUnpaid: function () {
return this.filterOnCurrency(this.billsUnpaid);
},
notPrefBillsUnpaid: function () {
return this.filterOnNotCurrency(this.billsUnpaid);
},
// left to spend
prefLeftToSpend: function () {
return this.filterOnCurrency(this.leftToSpend);
},
notPrefLeftToSpend: function () {
return this.filterOnNotCurrency(this.leftToSpend);
},
// net worth
prefNetWorth: function () {
return this.filterOnCurrency(this.netWorth);
},
notPrefNetWorth: function () {
return this.filterOnNotCurrency(this.netWorth);
},
currencyCode() {
return this.$store.getters.currencyCode;
},
currencyId() {
return this.$store.getters.currencyId;
}
},
watch: {
datesReady: function (value) {
if (true === value) {
this.prepareComponent();
}
},
start: function () {
if (false === this.loading) {
this.prepareComponent();
}
},
end: function () {
if (false === this.loading) {
this.prepareComponent();
}
},
},
created() {
this.ready = true;
},
methods: {
filterOnCurrency(array) {
let ret = [];
for (const key in array) {
if (array.hasOwnProperty(key)) {
// console.log('Currency ID seems to be ' + this.currencyId);
if (array[key].currency_id === this.currencyId) {
ret.push(array[key]);
}
}
}
// or just the first one:
if (0 === ret.length && array.hasOwnProperty(0)) {
ret.push(array[0]);
}
return ret;
},
filterOnNotCurrency(array) {
let ret = [];
for (const key in array) {
if (array.hasOwnProperty(key)) {
if (array[key].currency_id !== this.currencyId) {
ret.push(array[key]);
}
}
}
return ret;
},
/**
* Prepare the component.
*/
prepareComponent() {
this.error = false;
this.loading = true;
this.summary = [];
this.balances = [];
this.billsPaid = [];
this.billsUnpaid = [];
this.leftToSpend = [];
this.netWorth = [];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
//let startStr = this.start.toISOString().split('T')[0];
//let endStr = this.end.toISOString().split('T')[0];
//console.log(startStr);
//console.log(endStr);
axios.get('./api/v1/summary/basic?start=' + startStr + '&end=' + endStr)
.then(response => {
this.summary = response.data;
this.buildComponent();
this.loading = false
}).catch(error => {
this.error = true
});
},
buildComponent() {
this.getBalanceEntries();
this.getBillsEntries();
this.getLeftToSpend();
this.getNetWorth();
},
hasCurrency: function (array) {
for (const key in array) {
if (array.hasOwnProperty(key)) {
if (array[key].currency_id === this.currencyId) {
return true;
}
}
}
return false;
},
getBalanceEntries() {
this.balances = this.getKeyedEntries('balance-in-');
},
getNetWorth() {
this.netWorth = this.getKeyedEntries('net-worth-in-');
},
getLeftToSpend() {
this.leftToSpend = this.getKeyedEntries('left-to-spend-in-');
},
getBillsEntries() {
this.billsPaid = this.getKeyedEntries('bills-paid-in-');
this.billsUnpaid = this.getKeyedEntries('bills-unpaid-in-');
},
getKeyedEntries(expected) {
let result = [];
for (const key in this.summary) {
if (this.summary.hasOwnProperty(key)) {
if (expected === key.substr(0, expected.length)) {
result.push(this.summary[key]);
}
}
}
return result;
}
}
}
</script>

View File

@ -1,172 +0,0 @@
<!--
- GenericAttachments.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ title }}
</div>
<div class="input-group">
<input
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="title"
:name="fieldName"
multiple
ref="att"
@change="selectedFile"
type="file"
:disabled=disabled
/>
<span class="input-group-btn">
<button
class="btn btn-default"
type="button"
v-on:click="clearAtt"><span class="far fa-trash-alt"></span></button>
</span>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "GenericAttachments",
props: {
title: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
},
fieldName: {
type: String,
default: ''
},
errors: {
type: Array,
default: function () {
return [];
}
},
uploadTrigger: {
type: Boolean,
default: false
},
uploadObjectType: {
type: String,
default: ''
},
uploadObjectId: {
type: Number,
default: 0
}
},
data() {
return {
localValue: this.value,
uploaded: 0,
uploads: 0,
}
},
watch: {
uploadTrigger: function (value) {
if (true === value) {
// this.createAttachment().then(response => {
// this.uploadAttachment(response.data.data.id, new Blob([evt.target.result]));
// });
// new code
// console.log('start of new');
let files = this.$refs.att.files;
this.uploads = files.length;
// loop all files and create attachments.
for (let i in files) {
if (files.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
// console.log('Now at file ' + (parseInt(i) + 1) + ' / ' + files.length);
// read file into file reader:
let current = files[i];
let fileReader = new FileReader();
let theParent = this; // dont ask me why i need to do this.
fileReader.onloadend = evt => {
if (evt.target.readyState === FileReader.DONE) {
// console.log('I am done reading file ' + (parseInt(i) + 1));
this.createAttachment(current.name).then(response => {
// console.log('Created attachment. Now upload (1)');
return theParent.uploadAttachment(response.data.data.id, new Blob([evt.target.result]));
}).then(theParent.countAttachment);
}
}
fileReader.readAsArrayBuffer(current);
}
}
if (0 === files.length) {
// console.log('No files to upload. Emit event!');
this.$emit('uploaded-attachments', this.transaction_journal_id);
}
// Promise.all(promises).then(response => {
// console.log('All files uploaded. Emit event!');
// this.$emit('uploaded-attachments', this.transaction_journal_id);
// });
// end new code
}
},
},
methods: {
countAttachment: function () {
this.uploaded++;
// console.log('Uploaded ' + this.uploaded + ' / ' + this.uploads);
if (this.uploaded >= this.uploads) {
// console.log('All files uploaded. Emit event for ' + this.uploadObjectId);
this.$emit('uploaded-attachments', this.uploadObjectId);
}
},
uploadAttachment: function (attachmentId, data) {
this.created++;
// console.log('Now in uploadAttachment()');
const uploadUri = './api/v1/attachments/' + attachmentId + '/upload';
return axios.post(uploadUri, data)
},
createAttachment: function (name) {
const uri = './api/v1/attachments';
const data = {
filename: name,
attachable_type: this.uploadObjectType,
attachable_id: this.uploadObjectId,
};
return axios.post(uri, data);
},
selectedFile: function () {
this.$emit('selected-attachments');
},
clearAtt: function () {
this.$refs.att.value = '';
this.$emit('selected-no-attachments');
},
}
}
</script>

View File

@ -1,83 +0,0 @@
<!--
- GenericTextInput.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ title }}
</div>
<div class="input-group">
<div class="form-check">
<input class="form-check-input" :disabled=disabled type="checkbox" v-model="localValue" :id="fieldName">
<label class="form-check-label" :for="fieldName">
{{ description }}
</label>
</div>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "GenericCheckbox",
props: {
title: {
type: String,
default: ''
},
description: {
type: String,
default: ''
},
value: {
type: Boolean,
default: false
},
fieldName: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
},
errors: {
type: Array,
default: function () {
return [];
}
},
},
data() {
return {
localValue: this.value
}
},
watch: {
localValue: function (value) {
this.$emit('set-field', {field: this.fieldName, value: value});
},
}
}
</script>

View File

@ -1,120 +0,0 @@
<!--
- Currency.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('form.currency_id') }}
</div>
<div class="input-group" v-if="loading">
<span class="fas fa-spinner fa-spin"></span>
</div>
<div class="input-group" v-if="!loading">
<select
ref="currency_id"
v-model="currency_id"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:title="$t('form.currency_id')"
autocomplete="off"
:disabled=disabled
name="currency_id"
>
<option v-for="currency in this.currencyList" :label="currency.name" :value="currency.id" :selected="value === currency.id">{{ currency.name }}</option>
</select>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "GenericCurrency",
props: {
value: 0,
errors: [],
disabled: {
type: Boolean,
default: false
},
},
data() {
return {
loading: true,
currency_id: this.value,
currencyList: []
}
},
methods: {
loadCurrencies: function () {
this.loadCurrencyPage(1);
},
loadCurrencyPage: function (page) {
axios.get('./api/v1/currencies?page=' + page)
.then(response => {
let totalPages = parseInt(response.data.meta.pagination.total_pages);
let currentPage = parseInt(response.data.meta.pagination.current_page);
let currencies = response.data.data;
for (let i in currencies) {
if (currencies.hasOwnProperty(i)) {
let current = currencies[i];
if (true === current.attributes.default && null === this.value) {
this.currency_id = parseInt(current.id);
}
if (false === current.attributes.enabled) {
continue;
}
let currency = {
id: parseInt(current.id),
name: current.attributes.name,
};
this.currencyList.push(currency);
}
}
if (currentPage < totalPages) {
this.loadCurrencyPage(currentPage++);
}
if (currentPage >= totalPages) {
this.loading = false;
}
}
);
}
},
watch: {
currency_id: function (value) {
this.$emit('set-field', {field: 'currency_id', value: value});
},
value: function(value) {
this.currency_id = value;
}
},
created() {
this.loadCurrencies();
if (typeof this.value === 'number' && 0 !== this.value) {
this.currency_id = tthis.value;
}
}
}
</script>

View File

@ -1,116 +0,0 @@
<!--
- GenericTextInput.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ title }}
</div>
<vue-typeahead-bootstrap
v-model="localValue"
:data="groupTitles"
:inputClass="errors.length > 0 ? 'is-invalid' : ''"
:minMatchingChars="3"
:placeholder="title"
:serializer="item => item.title"
:showOnFocus=true
autofocus
inputName="description[]"
@input="lookupGroupTitle"
>
<template slot="append">
<div class="input-group-append">
<button class="btn btn-outline-secondary" tabindex="-1" type="button" v-on:click="clearGroupTitle"><span class="far fa-trash-alt"></span></button>
</div>
</template>
</vue-typeahead-bootstrap>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
import VueTypeaheadBootstrap from 'vue-typeahead-bootstrap';
import {debounce} from "lodash";
<script>
import VueTypeaheadBootstrap from "vue-typeahead-bootstrap";
import {debounce} from "lodash";
export default {
name: "GenericGroup",
components: {VueTypeaheadBootstrap},
props: {
title: {
type: String,
default: ''
},
description: {
type: String,
default: ''
},
value: {
type: String,
default: ''
},
fieldName: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
},
errors: {
type: Array,
default: function () {
return [];
}
},
},
methods: {
clearGroupTitle: function () {
this.localValue = '';
},
getACURL: function (query) {
// update autocomplete URL:
return document.getElementsByTagName('base')[0].href + 'api/v1/autocomplete/object-groups?query=' + query;
},
lookupGroupTitle: debounce(function () {
// update autocomplete URL:
axios.get(this.getACURL(this.value))
.then(response => {
this.groupTitles = response.data;
})
}, 300)
},
data() {
return {
localValue: this.value,
groupTitles: [],
}
},
watch: {
localValue: function (value) {
this.$emit('set-field', {field: this.fieldName, value: value});
},
}
}
</script>

View File

@ -1,187 +0,0 @@
<!--
- GenericLocation.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group" v-if="enableExternalMap">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ title }}
</div>
<div style="width:100%;height:300px;">
<LMap
ref="myMap"
:center="center"
:zoom="zoom" style="width:100%;height:300px;"
@ready="prepMap"
@update:zoom="zoomUpdated"
@update:center="centerUpdated"
@update:bounds="boundsUpdated"
>
<l-tile-layer :url="url"></l-tile-layer>
<l-marker :lat-lng="marker" :visible="hasMarker"></l-marker>
</LMap>
<span>
<button class="btn btn-default btn-xs" @click="clearLocation">{{ $t('firefly.clear_location') }}</button>
</span>
</div>
<p>&nbsp;</p>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
// If you need to reference 'L', such as in 'L.icon', then be sure to
// explicitly import 'leaflet' into your component
// import L from 'leaflet';
// OLD
// import {LMap, LMarker, LTileLayer} from 'vue2-leaflet';
// import 'leaflet/dist/leaflet.css';
//
// import L from 'leaflet';
//
// delete L.Icon.Default.prototype._getIconUrl;
//
// L.Icon.Default.mergeOptions({
// iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
// iconUrl: require('leaflet/dist/images/marker-icon.png'),
// shadowUrl: require('leaflet/dist/images/marker-shadow.png')
// });
import {LMap, LMarker, LTileLayer} from 'vue2-leaflet';
import 'leaflet/dist/leaflet.css';
export default {
name: "GenericLocation",
components: {LMap, LTileLayer, LMarker,},
props: {
title: {},
disabled: {
type: Boolean,
default: false
},
value: {
type: Object,
required: true,
default: function () {
return {
// some defaults here.
};
}
},
errors: {},
customFields: {},
},
data() {
return {
availableFields: this.customFields,
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
zoom: 3,
center: [0, 0],
bounds: null,
map: null,
enableExternalMap: false,
hasMarker: false,
marker: [0, 0],
}
},
created() {
// enable_external_map
this.verifyMapEnabled();
},
methods: {
verifyMapEnabled: function () {
axios.get('./api/v1/configuration/firefly.enable_external_map').then(response => {
this.enableExternalMap = response.data.data.value;
if (true === this.enableExternalMap) {
this.loadMap();
}
});
},
loadMap: function () {
if (null === this.value || typeof this.value === 'undefined' || 0 === Object.keys(this.value).length) {
axios.get('./api/v1/configuration/firefly.default_location').then(response => {
this.zoom = parseInt(response.data.data.value.zoom_level);
this.center =
[
parseFloat(response.data.data.value.latitude),
parseFloat(response.data.data.value.longitude),
]
;
});
return;
}
if (null !== this.value.zoom_level && null !== this.value.latitude && null !== this.value.longitude) {
this.zoom = this.value.zoom_level;
this.center = [
parseFloat(this.value.latitude),
parseFloat(this.value.longitude),
];
this.hasMarker = true;
}
},
prepMap: function () {
this.map = this.$refs.myMap.mapObject;
this.map.on('contextmenu', this.setObjectLocation);
this.map.on('zoomend', this.saveZoomLevel);
},
setObjectLocation: function (event) {
this.marker = [event.latlng.lat, event.latlng.lng];
this.hasMarker = true;
this.emitEvent();
},
saveZoomLevel: function () {
this.emitEvent();
},
clearLocation: function (e) {
e.preventDefault();
this.hasMarker = false;
this.emitEvent();
},
emitEvent() {
this.$emit('set-field', {
field: "location",
value: {
zoomLevel: this.zoom,
lat: this.marker[0],
lng: this.marker[1],
hasMarker: this.hasMarker
}
}
);
},
zoomUpdated(zoom) {
this.zoom = zoom;
},
centerUpdated(center) {
this.center = center;
},
boundsUpdated(bounds) {
this.bounds = bounds;
}
},
}
</script>

View File

@ -1,102 +0,0 @@
<!--
- GenericTextInput.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ title }}
</div>
<div class="input-group">
<input
v-model="localValue"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="title"
:name="fieldName"
ref="textInput"
:type="fieldType"
:disabled=disabled
:step="fieldStep"
/>
<div class="input-group-append">
<button class="btn btn-outline-secondary" v-on:click="clearText" tabindex="-1" type="button"><span class="far fa-trash-alt"></span></button>
</div>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "GenericTextInput",
props: {
title: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
},
value: {
type: String,
default: ''
},
fieldName: {
type: String,
default: ''
},
fieldType: {
type: String,
default: 'text'
},
fieldStep: {
type: String,
default: ''
},
errors: {
type: Array,
default: function () {
return [];
}
},
},
data() {
return {
localValue: this.value
}
},
methods: {
clearText: function () {
this.localValue = '';
},
},
watch: {
localValue: function (value) {
this.$emit('set-field', {field: this.fieldName, value: value});
},
value: function(value) {
this.localValue = value;
}
}
}
</script>

View File

@ -1,83 +0,0 @@
<!--
- GenericTextarea.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ title }}
</div>
<div class="input-group">
<textarea
v-model="localValue"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="title"
:disabled=disabled
:name="fieldName"
>{{ localValue }}</textarea>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "GenericTextarea",
props: {
title: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
},
value: {
type: String,
default: ''
},
fieldName: {
type: String,
default: ''
},
errors: {
type: Array,
default: function () {
return [];
}
},
},
data() {
return {
localValue: this.value
}
},
watch: {
localValue: function (value) {
this.$emit('set-field', {field: this.fieldName, value: value});
},
value: function(value) {
this.localValue = value;
}
}
}
</script>

View File

@ -1,110 +0,0 @@
<!--
- Index.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="row">
<div class="col">
<div id="accordion">
<!-- we are adding the .class so bootstrap.js collapse plugin detects it -->
<div class="card card-primary">
<div class="card-header">
<h4 class="card-title">
<a data-parent="#accordion" data-toggle="collapse" href="#collapseOne">
Create new accounts
</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse show">
<div class="card-body">
<div class="row">
<div class="col">
<p>Explain</p>
</div>
</div>
<div class="row">
<div class="col-lg-4">
A
</div>
<div class="col-lg-8">
B
</div>
</div>
</div>
</div>
</div>
<div class="card card-secondary">
<div class="card-header">
<h4 class="card-title">
<a data-parent="#accordion" data-toggle="collapse" href="#collapseTwo">
Collapsible Group Danger
</a>
</h4>
</div>
<div id="collapseTwo" class="panel-collapse collapse">
<div class="card-body">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.
3
wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt
laborum
eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee
nulla
assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred
nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft
beer
farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus
labore sustainable VHS.
</div>
</div>
</div>
<div class="card card-secondary">
<div class="card-header">
<h4 class="card-title">
<a data-parent="#accordion" data-toggle="collapse" href="#collapseThree">
Collapsible Group Success
</a>
</h4>
</div>
<div id="collapseThree" class="panel-collapse collapse">
<div class="card-body">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.
3
wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt
laborum
eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee
nulla
assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred
nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft
beer
farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus
labore sustainable VHS.
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Index"
}
</script>

View File

@ -1,37 +0,0 @@
<!--
- Alert.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div v-if="message.length > 0" :class="'alert alert-' + type + ' alert-dismissible'">
<button aria-hidden="true" class="close" data-dismiss="alert" type="button">×</button>
<h5>
<span v-if="'danger' === type" class="icon fas fa-ban"></span>
<span v-if="'success' === type" class="icon fas fa-thumbs-up"></span>
<span v-if="'danger' === type">{{ $t("firefly.flash_error") }}</span>
<span v-if="'success' === type">{{ $t("firefly.flash_success") }}</span>
</h5>
<span v-html="message"></span>
</div>
</template>
<script>
export default {
name: "Alert",
props: ['message', 'type']
}
</script>

View File

@ -1,136 +0,0 @@
/*
* index.js
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Vue from 'vue'
import Vuex, {createLogger} from 'vuex'
import transactions_create from './modules/transactions/create';
import transactions_edit from './modules/transactions/edit';
import dashboard_index from './modules/dashboard/index';
import root_store from './modules/root';
import accounts_index from './modules/accounts/index';
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store(
{
namespaced: true,
modules: {
root: root_store,
transactions: {
namespaced: true,
modules: {
create: transactions_create,
edit: transactions_edit
}
},
accounts: {
namespaced: true,
modules: {
index: accounts_index
},
},
dashboard: {
namespaced: true,
modules: {
index: dashboard_index
}
}
},
strict: debug,
plugins: debug ? [createLogger()] : [],
state: {
currencyPreference: {},
locale: 'en-US',
listPageSize: 50
},
mutations: {
setCurrencyPreference(state, payload) {
state.currencyPreference = payload.payload;
},
initialiseStore(state) {
// console.log('Now in initialiseStore()')
// if locale in local storage:
if (localStorage.locale) {
state.locale = localStorage.locale;
return;
}
// set locale from HTML:
let localeToken = document.head.querySelector('meta[name="locale"]');
if (localeToken) {
state.locale = localeToken.content;
localStorage.locale = localeToken.content;
}
}
},
getters: {
currencyCode: state => {
return state.currencyPreference.code;
},
currencyPreference: state => {
return state.currencyPreference;
},
currencyId: state => {
return state.currencyPreference.id;
},
locale: state => {
return state.locale;
}
},
actions: {
updateCurrencyPreference(context) {
// console.log('Now in updateCurrencyPreference');
if (localStorage.currencyPreference) {
context.commit('setCurrencyPreference', {payload: JSON.parse(localStorage.currencyPreference)});
return;
}
axios.get('./api/v1/currencies/default')
.then(response => {
let currencyResponse = {
id: parseInt(response.data.data.id),
name: response.data.data.attributes.name,
symbol: response.data.data.attributes.symbol,
code: response.data.data.attributes.code,
decimal_places: parseInt(response.data.data.attributes.decimal_places),
};
localStorage.currencyPreference = JSON.stringify(currencyResponse);
//console.log('getCurrencyPreference from server')
//console.log(JSON.stringify(currencyResponse));
context.commit('setCurrencyPreference', {payload: currencyResponse});
}).catch(err => {
// console.log('Got error response.');
console.error(err);
context.commit('setCurrencyPreference', {
payload: {
id: 1,
name: 'Euro',
symbol: '€',
code: 'EUR',
decimal_places: 2
}
});
});
}
}
}
);

View File

@ -1,59 +0,0 @@
/*
* index.js
* Copyright (c) 2021 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// initial state
const state = () => (
{
orderMode: false,
activeFilter: 1
}
)
// getters
const getters = {
orderMode: state => {
return state.orderMode;
},
activeFilter: state => {
return state.activeFilter;
}
}
// actions
const actions = {}
// mutations
const mutations = {
setOrderMode(state, payload) {
state.orderMode = payload;
},
setActiveFilter(state, payload) {
state.activeFilter = payload;
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}

View File

@ -1,222 +0,0 @@
/*
* index.js
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// initial state
import startOfDay from "date-fns/startOfDay";
import endOfDay from 'date-fns/endOfDay'
import startOfWeek from 'date-fns/startOfWeek'
import endOfWeek from 'date-fns/endOfWeek'
import startOfQuarter from 'date-fns/startOfQuarter';
import endOfQuarter from 'date-fns/endOfQuarter';
import endOfMonth from "date-fns/endOfMonth";
import startOfMonth from 'date-fns/startOfMonth';
const state = () => (
{
viewRange: 'default',
start: null,
end: null,
defaultStart: null,
defaultEnd: null,
}
)
// getters
const getters = {
start: state => {
return state.start;
},
end: state => {
return state.end;
},
defaultStart: state => {
return state.defaultStart;
},
defaultEnd: state => {
return state.defaultEnd;
},
viewRange: state => {
return state.viewRange;
}
}
// actions
const actions = {
initialiseStore(context) {
// console.log('initialiseStore for dashboard.');
// restore from local storage:
context.dispatch('restoreViewRange');
axios.get('./api/v1/preferences/viewRange')
.then(response => {
let viewRange = response.data.data.attributes.data;
let oldViewRange = context.getters.viewRange;
context.commit('setViewRange', viewRange);
if (viewRange !== oldViewRange) {
// console.log('View range changed from "' + oldViewRange + '" to "' + viewRange + '"');
context.dispatch('setDatesFromViewRange');
}
if (viewRange === oldViewRange) {
// console.log('Restore view range dates');
context.dispatch('restoreViewRangeDates');
}
}
).catch(() => {
context.commit('setViewRange', '1M');
context.dispatch('setDatesFromViewRange');
});
},
restoreViewRangeDates: function (context) {
// check local storage first?
if (localStorage.viewRangeStart) {
// console.log('view range start set from local storage.');
context.commit('setStart', new Date(localStorage.viewRangeStart));
}
if (localStorage.viewRangeEnd) {
// console.log('view range end set from local storage.');
context.commit('setEnd', new Date(localStorage.viewRangeEnd));
}
// also set default:
if (localStorage.viewRangeDefaultStart) {
// console.log('view range default start set from local storage.');
// console.log(localStorage.viewRangeDefaultStart);
context.commit('setDefaultStart', new Date(localStorage.viewRangeDefaultStart));
}
if (localStorage.viewRangeDefaultEnd) {
// console.log('view range default end set from local storage.');
// console.log(localStorage.viewRangeDefaultEnd);
context.commit('setDefaultEnd', new Date(localStorage.viewRangeDefaultEnd));
}
},
restoreViewRange: function (context) {
// console.log('restoreViewRange');
let viewRange = localStorage.getItem('viewRange');
if (null !== viewRange) {
// console.log('restored restoreViewRange ' + viewRange );
context.commit('setViewRange', viewRange);
}
},
setDatesFromViewRange(context) {
let start;
let end;
let viewRange = context.getters.viewRange;
let today = new Date;
// console.log('Will recreate view range on ' + viewRange);
switch (viewRange) {
case '1D':
// today:
start = startOfDay(today);
end = endOfDay(today);
break;
case '1W':
// this week:
start = startOfDay(startOfWeek(today, {weekStartsOn: 1}));
end = endOfDay(endOfWeek(today, {weekStartsOn: 1}));
break;
case '1M':
// this month:
start = startOfDay(startOfMonth(today));
end = endOfDay(endOfMonth(today));
break;
case '3M':
// this quarter
start = startOfDay(startOfQuarter(today));
end = endOfDay(endOfQuarter(today));
break;
case '6M':
// this half-year
if (today.getMonth() <= 5) {
start = new Date(today);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(5);
end.setDate(30);
end = endOfDay(start);
}
if (today.getMonth() > 5) {
start = new Date(today);
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(11);
end.setDate(31);
end = endOfDay(start);
}
break;
case '1Y':
// this year
start = new Date(today);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
break;
}
// console.log('Range is ' + viewRange);
// console.log('Start is ' + start);
// console.log('End is ' + end);
context.commit('setStart', start);
context.commit('setEnd', end);
context.commit('setDefaultStart', start);
context.commit('setDefaultEnd', end);
}
}
// mutations
const mutations = {
setStart(state, value) {
state.start = value;
window.localStorage.setItem('viewRangeStart', value);
},
setEnd(state, value) {
state.end = value;
window.localStorage.setItem('viewRangeEnd', value);
},
setDefaultStart(state, value) {
state.defaultStart = value;
window.localStorage.setItem('viewRangeDefaultStart', value);
},
setDefaultEnd(state, value) {
state.defaultEnd = value;
window.localStorage.setItem('viewRangeDefaultEnd', value);
},
setViewRange(state, range) {
state.viewRange = range;
window.localStorage.setItem('viewRange', range);
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}

View File

@ -1,138 +0,0 @@
/*
* root.js
* Copyright (c) 2021 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// initial state
const state = () => (
{
listPageSize: 33,
timezone: '',
cacheKey: {
age: 0,
value: 'empty',
},
}
)
// getters
const getters = {
listPageSize: state => {
return state.listPageSize;
},
timezone: state => {
// console.log('Wil return ' + state.listPageSize);
return state.timezone;
},
cacheKey: state => {
return state.cacheKey.value;
},
}
// actions
const actions = {
initialiseStore(context) {
// cache key auto refreshes every day
// console.log('Now in initialize store.')
if (localStorage.cacheKey) {
// console.log('Storage has cache key: ');
// console.log(localStorage.cacheKey);
let object = JSON.parse(localStorage.cacheKey);
if (Date.now() - object.age > 86400000) {
// console.log('Key is here but is old.');
context.commit('refreshCacheKey');
} else {
// console.log('Cache key from local storage: ' + object.value);
context.commit('setCacheKey', object);
}
} else {
// console.log('No key need new one.');
context.commit('refreshCacheKey');
}
if (localStorage.listPageSize) {
state.listPageSize = localStorage.listPageSize;
context.commit('setListPageSize', {length: localStorage.listPageSize});
}
if (!localStorage.listPageSize) {
axios.get('./api/v1/preferences/listPageSize')
.then(response => {
// console.log('from API: listPageSize is ' + parseInt(response.data.data.attributes.data));
context.commit('setListPageSize', {length: parseInt(response.data.data.attributes.data)});
}
);
}
if (localStorage.timezone) {
state.timezone = localStorage.timezone;
context.commit('setTimezone', {timezone: localStorage.timezone});
}
if (!localStorage.timezone) {
axios.get('./api/v1/configuration/app.timezone')
.then(response => {
context.commit('setTimezone', {timezone: response.data.data.value});
}
);
}
}
}
// mutations
const mutations = {
refreshCacheKey(state) {
let age = Date.now();
let N = 8;
let cacheKey = Array(N+1).join((Math.random().toString(36)+'00000000000000000').slice(2, 18)).slice(0, N);
let object = {age: age, value: cacheKey};
// console.log('Store new key in string JSON');
// console.log(JSON.stringify(object));
localStorage.cacheKey = JSON.stringify(object);
state.cacheKey = {age: age, value: cacheKey};
// console.log('Refresh: cachekey is now ' + cacheKey);
},
setCacheKey(state, payload) {
// console.log('Stored cache key in localstorage.');
// console.log(payload);
// console.log(JSON.stringify(payload));
localStorage.cacheKey = JSON.stringify(payload);
state.cacheKey = payload;
},
setListPageSize(state, payload) {
// console.log('Got a payload in setListPageSize');
// console.log(payload);
let number = parseInt(payload.length);
if (0 !== number) {
state.listPageSize = number;
localStorage.listPageSize = number;
}
},
setTimezone(state, payload) {
if ('' !== payload.timezone) {
state.timezone = payload.timezone;
localStorage.timezone = payload.timezone;
}
},
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}

View File

@ -1,153 +0,0 @@
/*
* create.js
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const lodashClonedeep = require('lodash.clonedeep');
import {getDefaultErrors, getDefaultTransaction} from '../../../../shared/transactions';
// initial state
const state = () => ({
transactionType: 'any',
groupTitle: '',
transactions: [],
customDateFields: {
interest_date: false,
book_date: false,
process_date: false,
due_date: false,
payment_date: false,
invoice_date: false,
},
defaultTransaction: getDefaultTransaction(),
defaultErrors: getDefaultErrors()
}
)
// getters
const getters = {
transactions: state => {
return state.transactions;
},
defaultErrors: state => {
return state.defaultErrors;
},
groupTitle: state => {
return state.groupTitle;
},
transactionType: state => {
return state.transactionType;
},
accountToTransaction: state => {
// See reference nr. 1
// possible API point!!
return state.accountToTransaction;
},
defaultTransaction: state => {
return state.defaultTransaction;
},
sourceAllowedTypes: state => {
return state.sourceAllowedTypes;
},
destinationAllowedTypes: state => {
return state.destinationAllowedTypes;
},
allowedOpposingTypes: state => {
return state.allowedOpposingTypes;
},
customDateFields: state => {
return state.customDateFields;
}
// // `getters` is localized to this module's getters
// // you can use rootGetters via 4th argument of getters
// someGetter (state, getters, rootState, rootGetters) {
// getters.someOtherGetter // -> 'foo/someOtherGetter'
// rootGetters.someOtherGetter // -> 'someOtherGetter'
// rootGetters['bar/someOtherGetter'] // -> 'bar/someOtherGetter'
// },
}
// actions
const actions = {}
// mutations
const mutations = {
addTransaction(state) {
let newTransaction = lodashClonedeep(state.defaultTransaction);
newTransaction.errors = lodashClonedeep(state.defaultErrors);
state.transactions.push(newTransaction);
},
resetErrors(state, payload) {
//console.log('resetErrors for index ' + payload.index);
state.transactions[payload.index].errors = lodashClonedeep(state.defaultErrors);
},
resetTransactions(state) {
// console.log('Store: Record call to resetTransactions :(');
state.transactions = [];
},
setGroupTitle(state, payload) {
state.groupTitle = payload.groupTitle;
},
setCustomDateFields(state, payload) {
state.customDateFields = payload;
},
deleteTransaction(state, payload) {
// console.log('Record call to deleteTransaction!');
state.transactions.splice(payload.index, 1);
// console.log('Deleted transaction ' + payload.index);
// console.log(state.transactions);
// if (0 === state.transactions.length) {
// console.log('array is empty!');
// }
},
setTransactionType(state, transactionType) {
state.transactionType = transactionType;
},
setAllowedOpposingTypes(state, allowedOpposingTypes) {
state.allowedOpposingTypes = allowedOpposingTypes;
},
setAccountToTransaction(state, payload) {
state.accountToTransaction = payload;
},
updateField(state, payload) {
state.transactions[payload.index][payload.field] = payload.value;
},
setTransactionError(state, payload) {
//console.log('Will set transactions[' + payload.index + '][errors][' + payload.field + '] to ');
//console.log(payload.errors);
state.transactions[payload.index].errors[payload.field] = payload.errors;
},
setDestinationAllowedTypes(state, payload) {
// console.log('Destination allowed types was changed!');
state.destinationAllowedTypes = payload;
},
setSourceAllowedTypes(state, payload) {
state.sourceAllowedTypes = payload;
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}

View File

@ -1,40 +0,0 @@
/*
* edit.js
* Copyright (c) 2021 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// initial state
const state = () => ({});
// getters
const getters = {};
// actions
const actions = {};
// mutations
const mutations = {};
export default {
namespaced: true,
state,
getters,
actions,
mutations
}

View File

@ -1,905 +0,0 @@
<!--
- Create.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<alert :message="errorMessage" type="danger"/>
<alert :message="successMessage" type="success"/>
<form @submit="submitTransaction" autocomplete="off">
<SplitPills ref="pills" :transactions="transactions" :count="transactions.length"/>
<div class="tab-content">
<SplitForm
v-for="(transaction, index) in this.transactions"
v-bind:key="index"
:count="transactions.length"
:custom-fields="customFields"
:date="date"
ref="splitForms"
:destination-allowed-types="destinationAllowedTypes"
:index="index"
:source-allowed-types="sourceAllowedTypes"
:submitted-transaction="submittedTransaction"
:transaction="transaction"
:transaction-type="transactionType"
v-on:uploaded-attachments="uploadedAttachment($event)"
v-on:selected-attachments="selectedAttachment($event)"
v-on:set-marker-location="storeLocation($event)"
v-on:set-account="storeAccountValue($event)"
v-on:set-date="storeDate($event)"
v-on:set-field="storeField($event)"
v-on:remove-transaction="removeTransaction($event)"
/>
</div>
<div class="row">
<!-- group title -->
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<div v-if="transactions.length > 1" class="card">
<div class="card-body">
<div class="row">
<div class="col">
<TransactionGroupTitle v-model="this.groupTitle" :errors="this.groupTitleErrors" v-on:set-group-title="storeGroupTitle($event)"/>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<!-- buttons -->
<div class="card card-primary">
<div class="card-body">
<div class="row">
<div class="col">
<div class="text-xs d-none d-lg-block d-xl-block">
&nbsp;
</div>
<button type="button" class="btn btn-outline-primary btn-block" @click="addTransactionArray"><span class="far fa-clone"></span> {{
$t('firefly.add_another_split')
}}
</button>
</div>
<div class="col">
<div class="text-xs d-none d-lg-block d-xl-block">
&nbsp;
</div>
<button :disabled="!enableSubmit" class="btn btn-success btn-block" @click="submitTransaction">
<span v-if="enableSubmit"><span class="far fa-save"></span> {{ $t('firefly.store_transaction') }}</span>
<span v-if="!enableSubmit"><span class="fas fa-spinner fa-spin"></span></span>
</button>
</div>
</div>
<div class="row">
<div class="col">
&nbsp;
</div>
<div class="col">
<div class="form-check">
<input id="createAnother" v-model="createAnother" class="form-check-input" type="checkbox">
<label class="form-check-label" for="createAnother">
<span class="small">{{ $t('firefly.create_another') }}</span>
</label>
</div>
<div class="form-check">
<input id="resetFormAfter" v-model="resetFormAfter" :disabled="!createAnother" class="form-check-input" type="checkbox">
<label class="form-check-label" for="resetFormAfter">
<span class="small">{{ $t('firefly.reset_after') }}</span>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</template>
<script>
import Alert from '../partials/Alert';
import SplitPills from "./SplitPills";
import TransactionGroupTitle from "./TransactionGroupTitle";
import SplitForm from "./SplitForm";
import {mapGetters, mapMutations} from "vuex";
export default {
name: "Create",
components: {
SplitForm,
Alert,
SplitPills,
TransactionGroupTitle,
},
/**
* Grab some stuff from the API, add the first transaction.
*/
created() {
// set transaction type:
let pathName = window.location.pathname;
let parts = pathName.split('/');
let type = parts[parts.length - 1];
// set a basic date-time string:
let date = new Date;
this.date = [date.getFullYear(), ('0' + (date.getMonth() + 1)).slice(-2), ('0' + date.getDate()).slice(-2)].join('-') + 'T00:00';
//console.log('Date is set to "' + this.date + '"');
this.setTransactionType(type[0].toUpperCase() + type.substring(1));
this.getExpectedSourceTypes();
this.getAccountToTransaction();
this.getCustomFields();
this.addTransaction();
},
data() {
return {
// error or success message
errorMessage: '',
successMessage: '',
// custom fields to show, useful for components:
customFields: {},
// states for the form (makes sense right)
enableSubmit: true,
createAnother: false,
resetFormAfter: false,
// things the process is done working on (3 phases):
submittedTransaction: false,
submittedLinks: false,
submittedAttachments: -1, // -1 (no attachments), 0 = uploading, 1 = uploaded
// transaction was actually submitted?
inError: false,
// number of uploaded attachments
// its an object because we count per transaction journal (which can have multiple attachments)
// and array doesn't work right.
submittedAttCount: {},
// errors in the group title:
groupTitleErrors: [],
// group ID + title once submitted:
returnedGroupId: 0,
returnedGroupTitle: '',
// meta data for accounts
accountToTransaction: {},
allowedOpposingTypes: {},
sourceAllowedTypes: ['Asset account', 'Loan', 'Debt', 'Mortgage', 'Revenue account'],
destinationAllowedTypes: ['Asset account', 'Loan', 'Debt', 'Mortgage', 'Expense account'],
// date not in the store because it was buggy
date: ''
}
},
computed: {
/**
* Grabbed from the store.
*/
...mapGetters('transactions/create', ['transactionType', 'transactions', 'groupTitle', 'defaultErrors']),
...mapGetters('root', ['listPageSize'])
},
watch: {
submittedAttachments: function () {
this.finaliseSubmission();
}
},
methods: {
/**
* Store related mutators used by this component.
*/
...mapMutations('transactions/create',
[
'setGroupTitle',
'addTransaction',
'deleteTransaction',
'setTransactionError',
'setTransactionType',
'resetErrors',
'updateField',
'resetTransactions',
]
),
addTransactionArray: function (event) {
// console.log('Record call to addTransactionArray');
event.preventDefault();
this.addTransaction();
},
/**
* Removes a split from the array.
*/
removeTransaction: function (payload) {
// console.log('Record call to removeTransaction');
// console.log('Triggered to remove transaction ' + payload.index);
window.$('#tab_split_' + (payload.index - 1)).click();
this.$store.commit('transactions/create/deleteTransaction', payload);
},
submitData: function (url, data) {
return axios.post(url, data);
},
handleSubmissionResponse: function (response) {
// console.log('In handleSubmissionResponse()');
// save some meta data:
this.returnedGroupId = parseInt(response.data.data.id);
this.returnedGroupTitle = null === response.data.data.attributes.group_title ? response.data.data.attributes.transactions[0].description : response.data.data.attributes.group_title;
let journals = [];
// save separate journal ID's (useful ahead in the process):
let result = response.data.data.attributes.transactions
for (let i in result) {
if (result.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
journals.push(parseInt(result[i].transaction_journal_id));
}
}
return Promise.resolve({journals: journals});
},
submitLinks: function (response, submission) {
let promises = [];
// for
for (let i in response.journals) {
if (response.journals.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let journalId = response.journals[i];
let links = submission.transactions[i].links;
for (let ii in links) {
if (links.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
let currentLink = links[ii];
if (0 === currentLink.outward_id) {
currentLink.outward_id = journalId;
}
if (0 === currentLink.inward_id) {
currentLink.inward_id = journalId;
}
promises.push(axios.post('./api/v1/transaction_links', currentLink));
}
}
}
}
if (0 === promises.length) {
return Promise.resolve({response: 'from submitLinks'});
}
return Promise.all(promises);
},
submitAttachments: function (response, submission) {
let anyAttachments = false;
for (let i in response.journals) {
if (response.journals.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let journalId = response.journals[i];
let hasAttachments = submission.transactions[i].attachments;
// console.log('Decided that ' + journalId);
// console.log(hasAttachments);
if (hasAttachments) {
// console.log('upload!');
this.updateField({index: i, field: 'transaction_journal_id', value: journalId});
// set upload trigger?
this.updateField({index: i, field: 'uploadTrigger', value: true});
//this.transactions[i].uploadTrigger = true;
anyAttachments = true;
}
}
}
if (true === anyAttachments) {
this.submittedAttachments = 0;
}
return Promise.resolve({response: 'from submitAttachments'});
},
selectedAttachment: function (payload) {
this.updateField({index: payload.index, field: 'attachments', value: true});
},
finaliseSubmission: function () {
// console.log('finaliseSubmission');
if (0 === this.submittedAttachments) {
// console.log('submittedAttachments = ' + this.submittedAttachments);
return;
}
// console.log('In finaliseSubmission');
if (false === this.createAnother) {
window.location.href = (window.previousURL ?? '/') + '?transaction_group_id=' + this.returnedGroupId + '&message=created';
return;
}
//console.log('Is in error?');
//console.log(this.inError);
if (false === this.inError) {
// show message:
this.errorMessage = '';
this.successMessage = this.$t('firefly.transaction_stored_link', {ID: this.returnedGroupId, title: this.returnedGroupTitle});
}
// console.log('here we are');
// enable flags:
this.enableSubmit = true;
this.submittedTransaction = false;
this.submittedAttachments = -1;
// reset attachments + errors
if (!this.resetFormAfter) {
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
if (this.transactions.hasOwnProperty(i)) {
// console.log('Reset attachment #' + i);
this.updateField({index: i, field: 'transaction_journal_id', value: 0});
this.updateField({index: i, field: 'errors', value: this.defaultErrors})
}
}
}
}
// reset the form:
if (this.resetFormAfter) {
this.resetTransactions();
setTimeout(this.addTransaction, 50);
}
return Promise.resolve({response: 'from finaliseSubmission'});
},
handleSubmissionError: function (error) {
//console.log('in handleSubmissionError');
// oh noes Firefly III has something to bitch about.
this.enableSubmit = true;
// but report an error because error:
this.inError = true;
this.parseErrors(error.response.data);
},
/**
* Actually submit the transaction to Firefly III. This is a fairly complex beast of a thing because multiple things
* need to happen in the right order.
*/
submitTransaction: function (event) {
event.preventDefault();
// console.log('submitTransaction()');
// disable the submit button:
this.enableSubmit = false;
// assume nothing breaks
this.inError = false;
// remove old warnings etc.
this.successMessage = '';
this.errorMessage = '';
// convert the data so its ready to be submitted:
const url = './api/v1/transactions';
const data = this.convertData();
this.submitData(url, data)
.then(this.handleSubmissionResponse)
.then(response => {
return Promise.all([this.submitLinks(response, data), this.submitAttachments(response, data)]);
}
)
.then(this.finaliseSubmission)
.catch(this.handleSubmissionError);
},
/**
* When a attachment component is done uploading it ends up here. We create an object where we count how many
* attachment components have reported back they're done uploading. Of course if they have nothing to upload
* they will be pretty fast in reporting they're done.
*
* Once the number of components matches the number of splits we know all attachments have been uploaded.
*/
uploadedAttachment: function (journalId) {
this.submittedAttachments = 0;
// console.log('Triggered uploadedAttachment(' + journalId + ')');
let key = 'str' + journalId;
this.submittedAttCount[key] = 1;
let count = Object.keys(this.submittedAttCount).length;
// console.log('Count is now ' + count);
// console.log('Length is ' + this.transactions.length);
if (count === this.transactions.length) {
// console.log('Got them all!');
// mark the attachments as stored:
this.submittedAttachments = 1;
}
},
/**
* Responds to changed location.
*/
storeLocation: function (payload) {
let zoomLevel = payload.hasMarker ? payload.zoomLevel : null;
let lat = payload.hasMarker ? payload.lat : null;
let lng = payload.hasMarker ? payload.lng : null;
this.updateField({index: payload.index, field: 'zoom_level', value: zoomLevel});
this.updateField({index: payload.index, field: 'latitude', value: lat});
this.updateField({index: payload.index, field: 'longitude', value: lng});
},
/**
* Responds to changed account.
*/
storeAccountValue: function (payload) {
this.updateField({index: payload.index, field: payload.direction + '_account_id', value: payload.id});
this.updateField({index: payload.index, field: payload.direction + '_account_type', value: payload.type});
this.updateField({index: payload.index, field: payload.direction + '_account_name', value: payload.name});
this.updateField({index: payload.index, field: payload.direction + '_account_currency_id', value: payload.currency_id});
this.updateField({index: payload.index, field: payload.direction + '_account_currency_code', value: payload.currency_code});
this.updateField({index: payload.index, field: payload.direction + '_account_currency_symbol', value: payload.currency_symbol});
//this.calculateTransactionType(payload.index);
if ('source' === payload.direction && true === payload.user_selected) {
this.$refs.splitForms[payload.index].$refs.destinationAccount.giveFocus();
}
if ('destination' === payload.direction && true === payload.user_selected) {
this.$refs.splitForms[payload.index].$refs.amount.giveFocus();
}
},
storeField: function (payload) {
this.updateField(payload);
if('description' === payload.field) {
// jump to account
//this.$refs.splitForms[payload.index].$refs.sourceAccount.giveFocus();
}
},
storeDate: function (payload) {
this.date = payload.date;
},
storeGroupTitle: function (value) {
// console.log('set group title: ' + value);
this.setGroupTitle({groupTitle: value});
},
/**
* Submit transaction links.
*/
submitTransactionLinks(data, response) {
//console.log('submitTransactionLinks()');
let promises = [];
let result = response.data.data.attributes.transactions;
let total = 0;
for (let i in data.transactions) {
if (data.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let submitted = data.transactions[i];
if (result.hasOwnProperty(i)) {
// found matching created transaction.
let received = result[i];
// grab ID from received, loop "submitted" transaction links
for (let ii in submitted.links) {
if (submitted.links.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
let currentLink = submitted.links[ii];
total++;
if (0 === currentLink.outward_id) {
currentLink.outward_id = received.transaction_journal_id;
}
if (0 === currentLink.inward_id) {
currentLink.inward_id = received.transaction_journal_id;
}
// submit transaction link:
promises.push(axios.post('./api/v1/transaction_links', currentLink).then(response => {
// See reference nr. 4
}));
}
}
}
}
}
if (0 === total) {
this.submittedLinks = true;
return;
}
Promise.all(promises).then(function () {
this.submittedLinks = true;
});
},
parseErrors: function (errors) {
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i)) {
this.resetErrors({index: i});
}
}
this.successMessage = '';
this.errorMessage = this.$t('firefly.errors_submission');
if (typeof errors.errors === 'undefined') {
this.successMessage = '';
this.errorMessage = errors.message;
}
let payload;
let transactionIndex;
let fieldName;
// fairly basic way of exploding the error array.
for (const key in errors.errors) {
// console.log('Error index: "' + key + '"');
if (errors.errors.hasOwnProperty(key)) {
if (key === 'group_title') {
this.groupTitleErrors = errors.errors[key];
continue;
}
if (key !== 'group_title') {
// lol dumbest way to explode "transactions.0.something" ever.
transactionIndex = parseInt(key.split('.')[1]);
fieldName = key.split('.')[2];
// set error in this object thing.
// console.log('The errors in key "' + key + '" are');
// console.log(errors.errors[key]);
switch (fieldName) {
case 'amount':
case 'description':
case 'date':
case 'tags':
payload = {index: transactionIndex, field: fieldName, errors: errors.errors[key]};
this.setTransactionError(payload);
break;
case 'budget_id':
payload = {index: transactionIndex, field: 'budget', errors: errors.errors[key]};
this.setTransactionError(payload);
break;
case 'bill_id':
payload = {index: transactionIndex, field: 'bill', errors: errors.errors[key]};
this.setTransactionError(payload);
break;
case 'piggy_bank_id':
payload = {index: transactionIndex, field: 'piggy_bank', errors: errors.errors[key]};
this.setTransactionError(payload);
break;
case 'category_name':
payload = {index: transactionIndex, field: 'category', errors: errors.errors[key]};
this.setTransactionError(payload);
break;
case 'source_name':
case 'source_id':
payload = {index: transactionIndex, field: 'source', errors: errors.errors[key]};
this.setTransactionError(payload);
break;
case 'destination_name':
case 'destination_id':
payload = {index: transactionIndex, field: 'destination', errors: errors.errors[key]};
this.setTransactionError(payload);
break;
case 'foreign_amount':
case 'foreign_currency':
payload = {index: transactionIndex, field: 'foreign_amount', errors: errors.errors[key]};
this.setTransactionError(payload);
break;
}
}
// unique some things
if (typeof this.transactions[transactionIndex] !== 'undefined') {
//this.transactions[transactionIndex].errors.source = Array.from(new Set(this.transactions[transactionIndex].errors.source));
//this.transactions[transactionIndex].errors.destination = Array.from(new Set(this.transactions[transactionIndex].errors.destination));
}
}
}
},
/**
*
*/
convertData: function () {
//console.log('now in convertData');
let data = {
'transactions': []
};
//console.log('Group title is: "' + this.groupTitle + '"');
if (this.groupTitle.length > 0) {
data.group_title = this.groupTitle;
//console.log('1) data.group_title is now "'+data.group_title+'"');
}
for (let i in this.transactions) {
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
data.transactions.push(this.convertSplit(i, this.transactions[i]));
}
}
if (data.transactions.length > 1 && '' !== data.transactions[0].description && (null === data.group_title || '' === data.group_title)) {
data.group_title = data.transactions[0].description;
//console.log('2) data.group_title is now "'+data.group_title+'"');
}
// depending on the transaction type for this thing, we need to
// make sure other splits match the data we submit.
if (data.transactions.length > 1) {
// console.log('This is a split!');
data = this.synchronizeAccounts(data);
}
return data;
},
synchronizeAccounts: function (data) {
// console.log('synchronizeAccounts: ' + this.transactionType);
// make sure all splits have whatever is in split 0.
// since its a transfer we can drop the name and use ID's only.
for (let i in data.transactions) {
if (data.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
// console.log('now at ' + i);
// for transfers, overrule both the source and the destination:
if ('transfer' === this.transactionType.toLowerCase()) {
data.transactions[i].source_name = null;
data.transactions[i].destination_name = null;
if (i > 0) {
data.transactions[i].source_id = data.transactions[0].source_id;
data.transactions[i].destination_id = data.transactions[0].destination_id;
}
}
// for deposits, overrule the destination and ignore the rest.
if ('deposit' === this.transactionType.toLowerCase()) {
data.transactions[i].destination_name = null;
if (i > 0) {
data.transactions[i].destination_id = data.transactions[0].destination_id;
}
}
// for withdrawals, overrule the source and ignore the rest.
if ('withdrawal' === this.transactionType.toLowerCase()) {
data.transactions[i].source_name = null;
if (i > 0) {
data.transactions[i].source_id = data.transactions[0].source_id;
}
}
}
}
return data;
},
/**
*
* @param key
* @param array
*/
convertSplit: function (key, array) {
if ('' === array.destination_account_name) {
array.destination_account_name = null;
}
if (0 === array.destination_account_id) {
array.destination_account_name = null;
}
if ('' === array.source_account_name) {
array.source_account_name = null;
}
if (0 === array.source_account_id) {
array.source_account_id = null;
}
let currentSplit = {
// basic
description: array.description,
date: this.date,
type: this.transactionType.toLowerCase(),
// account
source_id: array.source_account_id ?? null,
source_name: array.source_account_name ?? null,
destination_id: array.destination_account_id ?? null,
destination_name: array.destination_account_name ?? null,
// amount:
currency_id: array.currency_id,
amount: array.amount,
// meta data
budget_id: array.budget_id,
category_name: array.category,
// optional date fields (6x):
interest_date: array.interest_date,
book_date: array.book_date,
process_date: array.process_date,
due_date: array.due_date,
payment_date: array.payment_date,
invoice_date: array.invoice_date,
// other optional fields:
internal_reference: array.internal_reference,
external_url: array.external_url,
notes: array.notes,
external_id: array.external_id,
// location:
zoom_level: array.zoom_level,
longitude: array.longitude,
latitude: array.latitude,
tags: [],
// from thing:
order: 0,
reconciled: false,
attachments: array.attachments,
};
if (0 !== array.tags.length) {
for (let i in array.tags) {
if (array.tags.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
// array.tags
let current = array.tags[i];
if (typeof current === 'object' && null !== current) {
currentSplit.tags.push(current.text);
// console.log('Add tag "' + current.text + '" from object.');
continue;
}
if (typeof current === 'string') {
currentSplit.tags.push(current);
// console.log('Add tag "' + current + '" from string.');
continue;
}
// console.log('Is neither.');
}
}
}
// console.log('Current split tags is now: ');
// console.log(currentSplit.tags);
// bills and piggy banks
if (0 !== array.piggy_bank_id) {
currentSplit.piggy_bank_id = array.piggy_bank_id;
}
if (0 !== array.bill_id) {
currentSplit.bill_id = array.bill_id;
}
// foreign amount:
if (0 !== array.foreign_currency_id && '' !== array.foreign_amount) {
currentSplit.foreign_currency_id = array.foreign_currency_id;
}
if ('' !== array.foreign_amount) {
currentSplit.foreign_amount = array.foreign_amount;
}
// do transaction type
// let transactionType;
// let firstSource;
// let firstDestination;
// get transaction type from first transaction
//transactionType = this.transactionType ? this.transactionType.toLowerCase() : 'any';
//console.log('Transaction type is now ' + transactionType);
// if the transaction type is invalid, might just be that we can deduce it from
// the presence of a source or destination account
//firstSource = this.transactions[0].source_account_type;
//firstDestination = this.transactions[0].destination_account_type;
//console.log(this.transactions[0].source_account);
//console.log(this.transactions[0].destination_account);
//console.log('Type of first source is ' + firstSource);
//console.log('Type of first destination is ' + firstDestination);
// default to source:
currentSplit.currency_id = array.source_account_currency_id;
// if ('any' === transactionType && ['asset', 'Asset account', 'Loan', 'Debt', 'Mortgage'].includes(firstSource)) {
// transactionType = 'withdrawal';
// }
if ('Deposit' === this.transactionType) {
// transactionType = 'deposit';
currentSplit.currency_id = array.destination_account_currency_id;
}
//currentSplit.type = transactionType;
//console.log('Final type is ' + transactionType);
let links = [];
for (let i in array.links) {
if (array.links.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = array.links[i];
let linkTypeParts = current.link_type_id.split('-');
let inwardId = 'outward' === linkTypeParts[1] ? 0 : parseInt(current.transaction_journal_id);
let outwardId = 'inward' === linkTypeParts[1] ? 0 : parseInt(current.transaction_journal_id);
let newLink = {
link_type_id: parseInt(linkTypeParts[0]),
inward_id: inwardId,
outward_id: outwardId,
};
links.push(newLink);
}
}
currentSplit.links = links;
if (null === currentSplit.source_id) {
delete currentSplit.source_id;
}
if (null === currentSplit.source_name) {
delete currentSplit.source_name;
}
if (null === currentSplit.destination_id) {
delete currentSplit.destination_id;
}
if (null === currentSplit.destination_name) {
delete currentSplit.destination_name;
}
// console.log('Current split is: ');
// console.log(currentSplit);
// return it.
return currentSplit;
},
/**
* Get API value.
*/
getAllowedOpposingTypes: function () {
axios.get('./api/v1/configuration/firefly.allowed_opposing_types')
.then(response => {
// console.log('opposing types things.');
// console.log(response.data.data.value);
this.allowedOpposingTypes = response.data.data.value;
});
},
getExpectedSourceTypes: function () {
axios.get('./api/v1/configuration/firefly.expected_source_types')
.then(response => {
//console.log('getExpectedSourceTypes.');
this.sourceAllowedTypes = response.data.data.value.source[this.transactionType];
this.destinationAllowedTypes = response.data.data.value.destination[this.transactionType];
// console.log('sourceAllowedTypes');
// console.log(this.sourceAllowedTypes);
// console.log('Source allowed types for ' + this.transactionType + ' is: ');
// console.log(this.sourceAllowedTypes);
// console.log('Destination allowed types for ' + this.transactionType + ' is: ');
// console.log(this.destinationAllowedTypes);
//this.allowedOpposingTypes = response.data.data.value;
});
},
/**
* Get API value.
*/
getAccountToTransaction: function () {
axios.get('./api/v1/configuration/firefly.account_to_transaction')
.then(response => {
this.accountToTransaction = response.data.data.value;
});
},
/**
* This method grabs the users preferred custom transaction fields. It's used when configuring the
* custom date selects that will be available. It could be something the component does by itself,
* thereby separating concerns. This is on my list. If it changes to a per-component thing, then
* it should be done via the create.js Vue store because multiple components are interested in the
* user's custom transaction fields.
*/
getCustomFields: function () {
axios.get('./api/v1/preferences/transaction_journal_optional_fields').then(response => {
this.customFields = response.data.data.attributes.data;
});
},
setDestinationAllowedTypes: function (value) {
// console.log('Create::setDestinationAllowedTypes');
// console.log(value);
if (0 === value.length) {
this.destinationAllowedTypes = this.defaultDestinationAllowedTypes;
//console.log('empty so back to defaults');
return;
}
this.destinationAllowedTypes = value;
},
setSourceAllowedTypes(value) {
// console.log('Create::setSourceAllowedTypes');
// console.log(value);
if (0 === value.length) {
this.sourceAllowedTypes = this.defaultSourceAllowedTypes;
// console.log('empty so back to defaults');
return;
}
this.sourceAllowedTypes = value;
}
},
}
</script>

File diff suppressed because it is too large Load Diff

View File

@ -1,177 +0,0 @@
<!--
- Index.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<!-- charts here (see file history) -->
<!-- page is ignored for the time being -->
<TransactionListLarge
:entries="rawTransactions"
:page="currentPage"
:total="total"
:per-page="perPage"
:sort-desc="sortDesc"
v-on:jump-page="jumpToPage($event)"
v-on:refreshed-cache-key="refreshedKey"
/>
</div>
</template>
<script>
import {mapGetters, mapMutations} from "vuex";
import format from "date-fns/format";
import sub from "date-fns/sub";
import startOfMonth from "date-fns/startOfMonth";
import endOfMonth from "date-fns/endOfMonth";
import {configureAxios} from "../../shared/forageStore";
import TransactionListLarge from "./TransactionListLarge";
export default {
name: "Index",
components: {TransactionListLarge},
data() {
return {
rawTransactions: [],
type: 'all',
downloaded: false,
loading: false,
ready: false,
currentPage: 1,
perPage: 5,
total: 1,
sortBy: 'order',
sortDesc: false,
api: null,
sortableOptions: {
disabled: false,
chosenClass: 'is-selected',
onEnd: null
},
sortable: null,
locale: 'en-US',
ranges: [],
urlStart: null,
urlEnd: null,
}
},
watch: {
storeReady: function () {
this.getTransactionList();
},
start: function () {
this.getTransactionList();
},
end: function () {
this.getTransactionList();
},
activeFilter: function (value) {
this.filterAccountList();
}
},
computed: {
...mapGetters('root', ['listPageSize', 'cacheKey']),
...mapGetters('dashboard/index', ['start', 'end',]),
'indexReady': function () {
return null !== this.start && null !== this.end && null !== this.listPageSize && this.ready;
},
cardTitle: function () {
return this.$t('firefly.' + this.type + '_transactions');
}
},
created() {
this.locale = localStorage.locale ?? 'en-US';
let pathName = window.location.pathname;
let parts = pathName.split('/');
this.type = parts[parts.length - 1];
this.perPage = this.listPageSize ?? 51;
if (5 === parts.length) {
this.urlStart = new Date(parts[3]);
this.urlEnd = new Date(parts[4]);
this.type = parts[parts.length - 3];
}
let params = new URLSearchParams(window.location.search);
this.currentPage = params.get('page') ? parseInt(params.get('page')) : 1;
this.ready = true;
},
methods: {
...mapMutations('root', ['refreshCacheKey',]),
refreshedKey: function () {
this.downloaded = false;
this.rawTransactions = [];
this.getTransactionList();
},
jumpToPage: function (event) {
// console.log('noticed a change in transactions/index.vue!');
this.currentPage = event.page;
this.downloadTransactionList(event.page);
},
getTransactionList: function () {
if (this.indexReady && !this.loading && !this.downloaded) {
this.loading = true;
this.perPage = this.listPageSize ?? 51;
this.rawTransactions = [];
this.downloadTransactionList(this.currentPage);
this.calculateDateRanges();
}
},
calculateDateRanges: function () {
let yearAgo = sub(this.start, {years: 1});
let currentDate = this.start;
while (currentDate > yearAgo) {
let st = startOfMonth(currentDate);
let en = endOfMonth(currentDate);
this.ranges.push({start: st, end: en});
currentDate = sub(currentDate, {months: 1});
}
},
formatDate: function (date, frm) {
return format(date, frm);
},
downloadTransactionList: function (page) {
configureAxios().then(async (api) => {
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
if (null !== this.urlEnd && null !== this.urlStart) {
startStr = format(this.urlStart, 'y-MM-dd');
endStr = format(this.urlEnd, 'y-MM-dd');
}
let url = './api/v1/transactions?type=' + this.type + '&page=' + page + "&start=" + startStr + "&end=" + endStr + '&cache=' + this.cacheKey;
api.get(url)
.then(response => {
this.total = parseInt(response.data.meta.pagination.total);
this.rawTransactions = response.data.data;
this.loading = false;
}
);
});
},
},
}
</script>

View File

@ -1,464 +0,0 @@
<!--
- SplitForm.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div :id="'split_' + index" :class="'tab-pane' + (0 === index ? ' active' : '')">
<div class="row">
<div class="col">
<div class="card">
<div class="card-header">
<h3 class="card-title">
{{ $t('firefly.basic_journal_information') }}
<span v-if="count > 1">({{ index + 1 }} / {{ count }}) </span>
</h3>
<div v-if="count>1" class="card-tools">
<button type="button" class="btn btn-danger btn-xs" @click="removeTransaction"><span class="fas fa-trash-alt"></span></button>
</div>
</div>
<div class="card-body">
<!-- start of body -->
<div class="row">
<div class="col">
<TransactionDescription
v-model="transaction.description"
v-on="$listeners"
:errors="transaction.errors.description"
:index="index"
></TransactionDescription>
</div>
</div>
<!-- source and destination -->
<div class="row">
<div class="col-xl-5 col-lg-5 col-md-10 col-sm-12 col-xs-12">
<!-- SOURCE -->
<TransactionAccount
v-model="sourceAccount"
v-on="$listeners"
:destination-allowed-types="destinationAllowedTypes"
:errors="transaction.errors.source"
:index="index"
:source-allowed-types="sourceAllowedTypes"
:transaction-type="transactionType"
direction="source"
ref="sourceAccount"
v-on:selected-account="triggerNextAccount($event)"
/>
</div>
<!-- switcharoo! -->
<div class="col-xl-2 col-lg-2 col-md-2 col-sm-12 text-center d-none d-sm-block">
<SwitchAccount
v-if="0 === index && allowSwitch"
v-on="$listeners"
:index="index"
:transaction-type="transactionType"
/>
</div>
<!-- destination -->
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12">
<!-- DESTINATION -->
<TransactionAccount
v-model="destinationAccount"
v-on="$listeners"
:destination-allowed-types="destinationAllowedTypes"
:errors="transaction.errors.destination"
:index="index"
ref="destinationAccount"
:transaction-type="transactionType"
:source-allowed-types="sourceAllowedTypes"
direction="destination"
/>
</div>
</div>
<!-- amount -->
<div class="row">
<div class="col-xl-5 col-lg-5 col-md-10 col-sm-12 col-xs-12">
<!-- AMOUNT -->
<TransactionAmount
v-on="$listeners"
ref="amount"
:amount="transaction.amount"
:destination-currency-symbol="this.transaction.destination_account_currency_symbol"
:errors="transaction.errors.amount"
:index="index"
:source-currency-symbol="this.transaction.source_account_currency_symbol"
:transaction-type="this.transactionType"
/>
</div>
<div class="col-xl-2 col-lg-2 col-md-2 col-sm-12 text-center d-none d-sm-block">
<TransactionForeignCurrency
v-model="transaction.foreign_currency_id"
v-on="$listeners"
:destination-currency-id="this.transaction.destination_account_currency_id"
:index="index"
:selected-currency-id="this.transaction.foreign_currency_id"
:source-currency-id="this.transaction.source_account_currency_id"
:transaction-type="this.transactionType"
/>
</div>
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12">
<!--
The reason that TransactionAmount gets the symbols and
TransactionForeignAmount gets the ID's of the currencies is
because ultimately TransactionAmount doesn't decide which
currency id is submitted to Firefly III.
-->
<TransactionForeignAmount
v-model="transaction.foreign_amount"
v-on="$listeners"
:destination-currency-id="this.transaction.destination_account_currency_id"
:errors="transaction.errors.foreign_amount"
:index="index"
:selected-currency-id="this.transaction.foreign_currency_id"
:source-currency-id="this.transaction.source_account_currency_id"
:transaction-type="this.transactionType"
/>
</div>
</div>
<!-- dates -->
<div class="row">
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12">
<TransactionDate
v-on="$listeners"
:date="splitDate"
:errors="transaction.errors.date"
:index="index"
/>
</div>
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12 offset-xl-2 offset-lg-2">
<TransactionCustomDates
v-on="$listeners"
:book-date="transaction.book_date"
:custom-fields.sync="customFields"
:due-date="transaction.due_date"
:errors="transaction.errors.custom_dates"
:index="index"
:interest-date="transaction.interest_date"
:invoice-date="transaction.invoice_date"
:payment-date="transaction.payment_date"
:process-date="transaction.process_date"
/>
</div>
</div>
<!-- end of body -->
</div>
</div>
</div>
</div> <!-- end of basic card -->
<!-- card for meta -->
<div class="row">
<div class="col">
<div class="card">
<div class="card-header">
<h3 class="card-title">
{{ $t('firefly.transaction_journal_meta') }}
<span v-if="count > 1">({{ index + 1 }} / {{ count }}) </span>
</h3>
</div>
<div class="card-body">
<!-- start of body -->
<!-- meta -->
<div class="row">
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<TransactionBudget
v-if="!('Transfer' === transactionType || 'Deposit' === transactionType)"
v-model="transaction.budget_id"
v-on="$listeners"
:errors="transaction.errors.budget"
:index="index"
/>
<TransactionCategory
v-model="transaction.category"
v-on="$listeners"
:errors="transaction.errors.category"
:index="index"
/>
</div>
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<TransactionBill
v-if="!('Transfer' === transactionType || 'Deposit' === transactionType)"
v-model="transaction.bill_id"
v-on="$listeners"
:errors="transaction.errors.bill"
:index="index"
/>
<TransactionTags
v-model="transaction.tags"
v-on="$listeners"
ref="tags"
:errors="transaction.errors.tags"
:index="index"
/>
<TransactionPiggyBank
v-if="!('Withdrawal' === transactionType || 'Deposit' === transactionType)"
v-model="transaction.piggy_bank_id"
v-on="$listeners"
:errors="transaction.errors.piggy_bank"
:index="index"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- end card for meta -->
<!-- card for extra -->
<div v-if="hasMetaFields" class="row">
<div class="col">
<div class="card">
<div class="card-header">
<h3 class="card-title">
{{ $t('firefly.transaction_journal_extra') }}
<span v-if="count > 1">({{ index + 1 }} / {{ count }}) </span>
</h3>
</div>
<div class="card-body">
<!-- start of body -->
<div class="row">
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<TransactionInternalReference
v-model="transaction.internal_reference"
v-on="$listeners"
:custom-fields.sync="customFields"
:errors="transaction.errors.internal_reference"
:index="index"
/>
<TransactionExternalUrl
v-model="transaction.external_url"
v-on="$listeners"
:custom-fields.sync="customFields"
:errors="transaction.errors.external_url"
:index="index"
/>
<TransactionNotes
v-model="transaction.notes"
v-on="$listeners"
:custom-fields.sync="customFields"
:errors="transaction.errors.notes"
:index="index"
/>
</div>
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<TransactionAttachments
ref="attachments"
v-model="transaction.attachments"
v-on="$listeners"
:custom-fields.sync="customFields"
:index="index"
:transaction_journal_id="transaction.transaction_journal_id"
:upload-trigger="transaction.uploadTrigger"
:clear-trigger="transaction.clearTrigger"
/>
<TransactionLocation
v-model="transaction.location"
v-on="$listeners"
:custom-fields.sync="customFields"
:errors="transaction.errors.location"
:index="index"
/>
<TransactionLinks
v-model="transaction.links"
v-on="$listeners"
:custom-fields.sync="customFields"
:index="index"
/>
</div>
</div>
<!-- end of body -->
</div>
</div>
</div>
</div>
<!-- end card for extra -->
<!-- end of card -->
</div>
</template>
<script>
import TransactionDescription from "./TransactionDescription";
import TransactionDate from "./TransactionDate";
import TransactionBudget from "./TransactionBudget";
import TransactionAccount from "./TransactionAccount";
import SwitchAccount from "./SwitchAccount";
import TransactionAmount from "./TransactionAmount";
import TransactionForeignAmount from "./TransactionForeignAmount";
import TransactionForeignCurrency from "./TransactionForeignCurrency";
import TransactionCustomDates from "./TransactionCustomDates";
import TransactionCategory from "./TransactionCategory";
import TransactionBill from "./TransactionBill";
import TransactionTags from "./TransactionTags";
import TransactionPiggyBank from "./TransactionPiggyBank";
import TransactionInternalReference from "./TransactionInternalReference";
import TransactionExternalUrl from "./TransactionExternalUrl";
import TransactionNotes from "./TransactionNotes";
import TransactionLinks from "./TransactionLinks";
import TransactionAttachments from "./TransactionAttachments";
import SplitPills from "./SplitPills";
import TransactionLocation from "./TransactionLocation";
export default {
name: "SplitForm",
props: {
transaction: {
type: Object,
required: true
},
count: {
type: Number,
required: true
},
customFields: {
type: Object,
required: false
},
index: {
type: Number,
required: true
},
date: {
type: String,
required: true
},
transactionType: {
type: String,
required: true
},
sourceAllowedTypes: {
type: Array,
required: false,
default: function () {
return [];
}
}, // allowed source account types.
destinationAllowedTypes: {
type: Array,
required: false,
default: function () {
return [];
}
},
// allow switch?
allowSwitch: {
type: Boolean,
required: false,
default: false
}
},
created() {
// console.log('SplitForm(' + this.index + ')');
},
methods: {
removeTransaction: function () {
// console.log('Will remove transaction ' + this.index);
this.$emit('remove-transaction', {index: this.index});
},
triggerNextAccount: function (e) {
//alert(e);
if ('source' === e) {
// console.log('Jump to destination!');
this.$refs.destinationAccount.giveFocus();
}
},
},
computed: {
splitDate: function () {
return this.date;
},
sourceAccount: function () {
//console.log('computed::sourceAccount(' + this.index + ')');
return {
id: this.transaction.source_account_id,
name: this.transaction.source_account_name,
type: this.transaction.source_account_type,
};
//console.log(JSON.stringify(value));
//return value;
},
destinationAccount: function () {
//console.log('computed::destinationAccount(' + this.index + ')');
return {
id: this.transaction.destination_account_id,
name: this.transaction.destination_account_name,
type: this.transaction.destination_account_type,
};
//console.log(JSON.stringify(value));
//return value;
},
hasMetaFields: function () {
let requiredFields = [
'internal_reference',
'notes',
'attachments',
'external_uri',
'location',
'links',
];
for (let field in this.customFields) {
if (this.customFields.hasOwnProperty(field)) {
if (requiredFields.includes(field)) {
if (true === this.customFields[field]) {
return true;
}
}
}
}
return false;
}
},
components: {
TransactionLocation,
SplitPills,
TransactionAttachments,
TransactionNotes,
TransactionExternalUrl,
TransactionInternalReference,
TransactionPiggyBank,
TransactionTags,
TransactionLinks,
TransactionBill,
TransactionCategory,
TransactionCustomDates,
TransactionForeignCurrency,
TransactionForeignAmount,
TransactionAmount,
SwitchAccount,
TransactionAccount,
TransactionBudget,
TransactionDescription,
TransactionDate
},
}
</script>

View File

@ -1,57 +0,0 @@
<!--
- SplitPills.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<div v-if="transactions.length > 1" class="row">
<div class="col">
<!-- tabs -->
<ul class="nav nav-pills ml-auto p-2" id="transactionTabs">
<li v-for="(transaction, index) in this.transactions" class="nav-item"><a :class="'nav-link' + (0 === index ? ' active' : '')"
:href="'#split_' + index"
:id="'tab_split_' + index"
data-toggle="pill">
<span v-if="'' !== transaction.description">{{ transaction.description }}</span>
<span v-if="'' === transaction.description">Split {{ index + 1 }}</span>
</a></li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
name: "SplitPills",
props: {
transactions: {
type: Array,
required: true,
default: function () {
return [];
}
},
count: {
type: Number,
required: true
},
}
}
</script>

View File

@ -1,43 +0,0 @@
<!--
- SwitchAccount.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
<span v-if="'any' !== this.transactionType" class="text-muted">
{{ $t('firefly.' + this.transactionType) }}
</span>
<span v-if="'any' === this.transactionType" class="text-muted">&nbsp;</span>
</div>
</div>
</template>
<script>
export default {
name: "SwitchAccount",
props: ['index', 'transactionType'],
methods: {
// switchAccounts() {
// this.$emit('switch-accounts', this.index);
// }
}
}
</script>

View File

@ -1,309 +0,0 @@
<!--
- TransactionAccount.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div v-if="visible" class="text-xs d-none d-lg-block d-xl-block">
<span v-if="0 === this.index">{{ $t('firefly.' + this.direction + '_account') }}</span>
<span v-if="this.index > 0" class="text-warning">{{ $t('firefly.first_split_overrules_' + this.direction) }}</span>
</div>
<div v-if="!visible" class="text-xs d-none d-lg-block d-xl-block">
&nbsp;
</div>
<vue-typeahead-bootstrap
v-if="visible"
v-model="accountName"
:data="accounts"
:inputClass="errors.length > 0 ? 'is-invalid' : ''"
:inputName="direction + '[]'"
:minMatchingChars="3"
:placeholder="$t('firefly.' + direction + '_account')"
:serializer="item => item.name_with_balance"
:showOnFocus=true
ref="inputThing"
aria-autocomplete="none"
autocomplete="off"
@hit="userSelectedAccount"
@input="lookupAccount"
>
<template slot="suggestion" slot-scope="{ data, htmlText }">
<div :title="data.type" class="d-flex">
<span v-html="htmlText"></span><br>
</div>
</template>
<template slot="append">
<div class="input-group-append">
<button class="btn btn-outline-secondary" tabindex="-1" type="button" v-on:click="clearAccount"><span class="far fa-trash-alt"></span></button>
</div>
</template>
</vue-typeahead-bootstrap>
<div v-if="!visible" class="form-control-static">
<span class="small text-muted"><em>{{ $t('firefly.first_split_decides') }}</em></span>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
import VueTypeaheadBootstrap from 'vue-typeahead-bootstrap';
import {debounce} from 'lodash';
export default {
name: "TransactionAccount",
components: {VueTypeaheadBootstrap},
props: {
index: {
type: Number,
},
direction: {
type: String,
},
value: {
type: Object,
default: () => ({})
},
errors: {
type: Array,
default: () => ([])
},
sourceAllowedTypes: {
type: Array,
default: () => ([])
},
destinationAllowedTypes: {
type: Array,
default: () => ([])
},
transactionType: {
type: String,
default: 'any'
},
},
data() {
return {
query: '',
accounts: [],
accountTypes: [],
initialSet: [],
selectedAccount: {},
accountName: '',
selectedAccountTrigger: false,
}
},
created() {
this.accountName = this.value.name ?? '';
// console.log('TransactionAccount::created() direction=' + this.direction + ', type=' + this.transactionType + ' , name="' + this.accountName + '"');
this.selectedAccountTrigger = true;
},
mounted: function () {
this.$nextTick(function () {
this.$refs.inputThing.$refs.input.tabIndex = 2;
})
},
methods: {
getACURL: function (types, query) {
return './api/v1/autocomplete/accounts?types=' + types.join(',') + '&query=' + query;
},
giveFocus: function () {
// console.log('I want focus! now OK: ' + this.direction + ' l: ' + this.accounts.length);
//console.log(this.$refs.inputThing.$refs.input.value);
this.$refs.inputThing.$refs.input.focus();
//console.log(this.$refs.inputThing.isFocused);
},
userSelectedAccount: function (event) {
// console.log('userSelectedAccount!');
// console.log('To prevent invalid propogation, set selectedAccountTrigger = true');
this.selectedAccountTrigger = true;
this.selectedAccount = event;
},
systemReturnedAccount: function (event) {
//console.log('systemReturnedAccount!');
//console.log('To prevent invalid propogation, set selectedAccountTrigger = false');
this.selectedAccountTrigger = false;
this.selectedAccount = event;
},
clearAccount: function () {
//// console.log('TransactionAccount::clearAccount()');
this.accounts = this.initialSet;
//this.account = {name: '', type: 'no_type', id: null, currency_id: null, currency_code: null, currency_symbol: null};
this.accountName = '';
},
lookupAccount: debounce(function () {
//// console.log('TransactionAccount::lookupAccount()');
//// console.log('In lookupAccount()');
if (0 === this.accountTypes.length) {
// set the types from the default types for this direction:
this.accountTypes = 'source' === this.direction ? this.sourceAllowedTypes : this.destinationAllowedTypes;
}
// // console.log(this.direction + ': Will search for types:');
// // console.log(this.accountTypes);
// update autocomplete URL:
axios.get(this.getACURL(this.accountTypes, this.accountName))
.then(response => {
//// console.log('Got a response!');
this.accounts = response.data;
//// console.log(response.data);
})
}, 300),
createInitialSet: function () {
//// console.log('TransactionAccount::createInitialSet()');
let types = this.sourceAllowedTypes;
if ('destination' === this.direction) {
types = this.destinationAllowedTypes;
}
// // console.log('createInitialSet() direction=' + this.direction + ' resets to these types:');
// // console.log(types);
axios.get(this.getACURL(types, ''))
.then(response => {
this.accounts = response.data;
this.initialSet = response.data;
});
}
},
watch: {
sourceAllowedTypes: function (value) {
//// console.log('TransactionAccount::sourceAllowedTypes()');
// // console.log(this.direction + ' account noticed change in sourceAllowedTypes');
// // console.log(value);
this.createInitialSet();
},
destinationAllowedTypes: function (value) {
//// console.log('TransactionAccount::destinationAllowedTypes()');
// // console.log(this.direction + ' account noticed change in destinationAllowedTypes');
// // console.log(value);
this.createInitialSet();
},
/**
* Triggered when the user selects an account from the auto-complete.
*
* @param value
*/
selectedAccount: function (value) {
//console.log('TransactionAccount::watch selectedAccount()');
// console.log(value);
if (true === this.selectedAccountTrigger) {
// console.log('$emit alles!');
this.$emit('set-account',
{
index: this.index,
direction: this.direction,
id: value.id,
type: value.type,
name: value.name,
currency_id: value.currency_id,
currency_code: value.currency_code,
currency_symbol: value.currency_symbol,
user_selected: true,
}
// jump to next field somehow.
);
//console.log('watch::selectedAccount() will now set accountName because selectedAccountTrigger = true');
this.accountName = value.name;
}
if (false === this.selectedAccountTrigger) {
//console.log('watch::selectedAccount() will NOT set accountName because selectedAccountTrigger = false');
}
if (false === this.selectedAccountTrigger && this.accountName !== value.name && null !== value.name) {
//console.log('watch::selectedAccount() will set accountName. selectedAccountTrigger = false but name is different ("' + this.accountName + '" > "' + value.name + '")');
this.selectedAccountTrigger = true;
this.accountName = value.name;
}
},
accountName: function (value) {
// console.log('now at watch accountName("' + value + '")');
// console.log(this.selectedAccountTrigger);
if (true === this.selectedAccountTrigger) {
// console.log('Do nothing because selectedAccountTrigger = true');
}
if (false === this.selectedAccountTrigger) {
// console.log('$emit name from watch::accountName() because selectedAccountTrigger = false');
this.$emit('set-account',
{
index: this.index,
direction: this.direction,
id: null,
type: null,
name: value,
currency_id: null,
currency_code: null,
currency_symbol: null,
user_selected: false
}
);
// this.account = {name: value, type: null, id: null, currency_id: null, currency_code: null, currency_symbol: null};
}
// console.log('set selectedAccountTrigger to be FALSE');
this.selectedAccountTrigger = false;
},
value: function (value) {
//console.log('TransactionAccount(' + this.index + ')::watch value(' + JSON.stringify(value) + ')');
this.systemReturnedAccount(value);
// // console.log('Index ' + this.index + ' nwAct: ', value);
// // console.log(this.direction + ' account overruled by external forces.');
// // console.log(value);
//this.account = value;
//this.selectedAccountTrigger = true;
// this.accountName = value.name ?? '';
// if(null !== value.id) {
// return;
// }
// this.selectedAccountTrigger = true;
//
// // console.log('Set selectedAccountTrigger = true');
// this.selectedAccount = value;
}
},
computed: {
accountKey: {
get() {
return 'source' === this.direction ? 'source_account' : 'destination_account';
}
},
visible: {
get() {
// index 0 is always visible:
if (0 === this.index) {
return true;
}
// // console.log('Direction of account ' + this.index + ' is ' + this.direction + '(' + this.transactionType + ')');
// // console.log(this.transactionType);
if ('source' === this.direction) {
return 'any' === this.transactionType || 'Deposit' === this.transactionType || typeof this.transactionType === 'undefined';
}
if ('destination' === this.direction) {
return 'any' === this.transactionType || 'Withdrawal' === this.transactionType || typeof this.transactionType === 'undefined';
}
return false;
}
}
}
}
</script>

View File

@ -1,123 +0,0 @@
<!--
- TransactionAmount.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs">{{ $t('firefly.amount') }}</div>
<div class="input-group">
<div v-if="currencySymbol" class="input-group-prepend">
<div class="input-group-text">{{ currencySymbol }}</div>
</div>
<input
v-model="transactionAmount"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="$t('firefly.amount')"
:title="$t('firefly.amount')"
autocomplete="off"
name="amount[]"
type="number"
ref="input"
step="any"
>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "TransactionAmount",
props: {
index: {
type: Number,
default: 0,
required: true
},
errors: {},
amount: {},
transactionType: {},
sourceCurrencySymbol: {},
destinationCurrencySymbol: {},
fractionDigits: {
default: 2,
required: false
},
},
created() {
if ('' !== this.amount) {
this.emitEvent = false;
this.transactionAmount = this.formatNumber(this.amount);
}
},
mounted: function () {
this.$nextTick(function () {
this.$refs.input.tabIndex = 3;
})
},
methods: {
formatNumber(str) {
return parseFloat(str).toFixed(this.fractionDigits);
},
giveFocus: function () {
this.$refs.input.focus();
},
},
data() {
return {
transactionAmount: this.amount,
currencySymbol: null,
srcCurrencySymbol: this.sourceCurrencySymbol,
dstCurrencySymbol: this.destinationCurrencySymbol,
emitEvent: true
}
},
watch: {
transactionAmount: function (value) {
if (true === this.emitEvent) {
this.$emit('set-field', {field: 'amount', index: this.index, value: value});
}
this.emitEvent = true;
},
amount: function (value) {
this.transactionAmount = value;
},
sourceCurrencySymbol: function (value) {
this.srcCurrencySymbol = value;
},
destinationCurrencySymbol: function (value) {
this.dstCurrencySymbol = value;
},
transactionType: function (value) {
switch (value) {
case 'Transfer':
case 'Withdrawal':
this.currencySymbol = this.srcCurrencySymbol;
break;
case 'Deposit':
this.currencySymbol = this.dstCurrencySymbol;
}
},
},
}
</script>

View File

@ -1,140 +0,0 @@
<!--
- TransactionAttachments.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div v-if="showField" class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.attachments') }}
</div>
<div class="input-group">
<input
ref="att"
class="form-control"
multiple
@change="selectedFile"
name="attachments[]"
type="file"
/>
</div>
</div>
</template>
<script>
export default {
name: "TransactionAttachments",
props: ['transaction_journal_id', 'customFields', 'index', 'uploadTrigger', 'clearTrigger'],
data() {
return {
availableFields: this.customFields,
uploads: 0,
created: 0,
uploaded: 0,
}
},
watch: {
customFields: function (value) {
this.availableFields = value;
},
uploadTrigger: function () {
// console.log('uploadTrigger(' + this.transaction_journal_id + ',' + this.index + ')');
this.doUpload();
},
clearTrigger: function () {
// console.log('clearTrigger(' + this.transaction_journal_id + ',' + this.index + ')');
this.$refs.att.value = null;
},
transaction_journal_id: function (value) {
// console.log('watch transaction_journal_id: ' + value + ' (index ' + this.index + ')');
}
},
computed: {
showField: function () {
if ('attachments' in this.availableFields) {
return this.availableFields.attachments;
}
return false;
}
},
methods: {
selectedFile: function () {
this.$emit('selected-attachments', {index: this.index, id: this.transaction_journal_id});
},
createAttachment: function (name) {
// console.log('Now in createAttachment()');
const uri = './api/v1/attachments';
const data = {
filename: name,
attachable_type: 'TransactionJournal',
attachable_id: this.transaction_journal_id,
};
// create new attachment:
return axios.post(uri, data);
},
uploadAttachment: function (attachmentId, data) {
this.created++;
// console.log('Now in uploadAttachment()');
const uploadUri = './api/v1/attachments/' + attachmentId + '/upload';
return axios.post(uploadUri, data)
},
countAttachment: function () {
this.uploaded++;
// console.log('Uploaded ' + this.uploaded + ' / ' + this.uploads);
if (this.uploaded >= this.uploads) {
// console.log('All files uploaded. Emit event for ' + this.transaction_journal_id + '(' + this.index + ')');
this.$emit('uploaded-attachments', this.transaction_journal_id);
}
},
doUpload: function () {
let files = this.$refs.att.files;
this.uploads = files.length;
// loop all files and create attachments.
for (let i in files) {
if (files.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
// console.log('Now at file ' + (parseInt(i) + 1) + ' / ' + files.length);
// read file into file reader:
let current = files[i];
let fileReader = new FileReader();
let theParent = this; // dont ask me why i need to do this.
fileReader.onloadend = evt => {
if (evt.target.readyState === FileReader.DONE) {
// console.log('I am done reading file ' + (parseInt(i) + 1));
this.createAttachment(current.name).then(response => {
// console.log('Created attachment. Now upload (1)');
return theParent.uploadAttachment(response.data.data.id, new Blob([evt.target.result]));
}).then(theParent.countAttachment);
}
}
fileReader.readAsArrayBuffer(current);
}
}
if (0 === files.length) {
//console.log('No files to upload. Emit event!');
this.$emit('uploaded-attachments', this.transaction_journal_id);
}
// Promise.all(promises).then(response => {
// console.log('All files uploaded. Emit event!');
// this.$emit('uploaded-attachments', this.transaction_journal_id);
// });
}
}
}
</script>

View File

@ -1,105 +0,0 @@
<!--
- TransactionBill.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.bill') }}
</div>
<div class="input-group">
<select
ref="bill"
v-model="bill"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:title="$t('firefly.bill')"
autocomplete="off"
name="bill_id[]"
>
<option v-for="bill in this.billList" :label="bill.name" :value="bill.id">{{ bill.name }}</option>
</select>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
props: ['value', 'index', 'errors'],
name: "TransactionBill",
data() {
return {
billList: [],
bill: this.value,
}
},
mounted: function () {
this.$nextTick(function () {
this.$refs.bill.tabIndex = 9;
})
},
created() {
this.collectData();
},
methods: {
collectData() {
this.billList.push(
{
id: 0,
name: this.$t('firefly.no_bill'),
}
);
this.getBills();
},
getBills() {
axios.get('./api/v1/bills')
.then(response => {
this.parseBills(response.data);
}
);
},
parseBills(data) {
for (let key in data.data) {
if (data.data.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
let current = data.data[key];
this.billList.push(
{
id: parseInt(current.id),
name: current.attributes.name
}
);
}
}
},
},
watch: {
value: function (value) {
this.emitEvent = false;
this.bill = value;
},
bill: function (value) {
this.$emit('set-field', {field: 'bill_id', index: this.index, value: value});
}
},
}
</script>

View File

@ -1,107 +0,0 @@
<!--
- TransactionBudget.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.budget') }}
</div>
<div class="input-group">
<select
ref="budget"
v-model="budget"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:title="$t('firefly.budget')"
autocomplete="off"
name="budget_id[]"
>
<option v-for="budget in this.budgetList" :label="budget.name" :value="budget.id">{{ budget.name }}</option>
</select>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
props: ['index', 'value', 'errors'],
name: "TransactionBudget",
data() {
return {
budgetList: [],
budget: this.value,
emitEvent: true
}
},
mounted: function () {
this.$nextTick(function () {
this.$refs.budget.tabIndex = 8;
})
},
created() {
this.collectData();
},
methods: {
collectData() {
this.budgetList.push(
{
id: 0,
name: this.$t('firefly.no_budget'),
}
);
this.getBudgets();
},
getBudgets() {
axios.get('./api/v1/budgets')
.then(response => {
this.parseBudgets(response.data);
}
);
},
parseBudgets(data) {
for (let key in data.data) {
if (data.data.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
let current = data.data[key];
if(!current.attributes.active) {
continue;
}
this.budgetList.push(
{
id: parseInt(current.id),
name: current.attributes.name
}
);
}
}
},
},
watch: {
value: function (value) {
this.emitEvent = false;
this.budget = value;
},
budget: function (value) {
this.$emit('set-field', {field: 'budget_id', index: this.index, value: value});
}
},
}
</script>

View File

@ -1,120 +0,0 @@
<!--
- TransactionCategory.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.category') }}
</div>
<vue-typeahead-bootstrap
v-model="category"
:data="categories"
:inputClass="errors.length > 0 ? 'is-invalid' : ''"
:minMatchingChars="3"
:placeholder="$t('firefly.category')"
:serializer="item => item.name"
:showOnFocus=true
inputName="category[]"
@hit="selectedCategory = $event"
@input="lookupCategory"
ref="input"
>
<template slot="append">
<div class="input-group-append">
<button class="btn btn-outline-secondary" tabindex="-1" type="button" v-on:click="clearCategory"><span class="far fa-trash-alt"></span></button>
</div>
</template>
</vue-typeahead-bootstrap>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
import VueTypeaheadBootstrap from 'vue-typeahead-bootstrap';
import {debounce} from "lodash";
export default {
props: ['value', 'index', 'errors'],
components: {VueTypeaheadBootstrap},
name: "TransactionCategory",
data() {
return {
categories: [],
initialSet: [],
category: this.value
}
},
mounted: function () {
this.$nextTick(function () {
this.$refs.input.$refs.input.tabIndex = 10;
})
},
created() {
//console.log('Created category(' + this.index + ') "' + this.value + '"');
// initial list of accounts:
axios.get(this.getACURL(''))
.then(response => {
this.categories = response.data;
this.initialSet = response.data;
});
},
methods: {
clearCategory: function () {
this.category = '';
},
getACURL: function (query) {
// update autocomplete URL:
// console.log('getACURL("' + query + '")');
return document.getElementsByTagName('base')[0].href + 'api/v1/autocomplete/categories?query=' + query;
},
lookupCategory: debounce(function () {
// update autocomplete URL:
//console.log('Do a search for "'+this.category+'"');
axios.get(this.getACURL(this.category))
.then(response => {
this.categories = response.data;
})
}, 300)
},
watch: {
value: function (value) {
this.category = value ?? '';
},
category: function (value) {
this.$emit('set-field', {field: 'category', index: this.index, value: value});
}
},
computed: {
selectedCategory: {
get() {
return this.categories[this.index].name;
},
set(value) {
this.category = value.name;
}
}
}
}
</script>

View File

@ -1,108 +0,0 @@
<!--
- TransactionCustomDates.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<div v-for="(enabled, name) in availableFields" class="form-group">
<div v-if="enabled && isDateField(name)" class="text-xs d-none d-lg-block d-xl-block">
{{ $t('form.' + name) }}
</div>
<div v-if="enabled && isDateField(name)" class="input-group">
<input
:ref="name"
:name="name + '[]'"
:placeholder="$t('form.' + name)"
:title="$t('form.' + name)"
:value="getFieldValue(name)"
autocomplete="off"
class="form-control"
type="date"
@change="setFieldValue($event, name)"
>
</div>
</div>
</div>
</template>
<script>
export default {
name: "TransactionCustomDates",
props: [
'index',
'errors',
'customFields',
'interestDate',
'bookDate',
'processDate',
'dueDate',
'paymentDate',
'invoiceDate'
],
data() {
return {
dateFields: ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date'],
availableFields: this.customFields,
dates: {
interest_date: this.interestDate,
book_date: this.bookDate,
process_date: this.processDate,
due_date: this.dueDate,
payment_date: this.paymentDate,
invoice_date: this.invoiceDate,
}
,
}
},
watch: {
customFields: function (value) {
this.availableFields = value;
},
interestDate: function (value) {
this.dates.interest_date = value;
},
bookDate: function (value) {
this.dates.book_date = value;
},
processDate: function (value) {
this.dates.process_date = value;
},
dueDate: function (value) {
this.dates.due_date = value;
},
paymentDate: function (value) {
this.dates.payment_date = value;
},
invoiceDate: function (value) {
this.dates.invoice_date = value;
},
},
methods: {
isDateField: function (name) {
return this.dateFields.includes(name)
},
getFieldValue(field) {
return this.dates[field] ?? '';
},
setFieldValue(event, field) {
this.$emit('set-field', {field: field, index: this.index, value: event.target.value});
},
}
}
</script>

View File

@ -1,99 +0,0 @@
<!--
- TransactionDate.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group" v-if="0===index">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.date_and_time') }}
</div>
<div class="input-group">
<input
ref="date"
v-model="dateStr"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="dateStr"
:title="$t('firefly.date')"
autocomplete="off"
name="date[]"
type="date"
>
<input
ref="time"
v-model="timeStr"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="timeStr"
:title="$t('firefly.time')"
autocomplete="off"
name="time[]"
type="time"
>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
<span class="text-muted small">{{ localTimeZone }}:{{ systemTimeZone }}</span>
</div>
</template>
<script>
import {mapGetters} from "vuex";
export default {
props: ['index', 'errors', 'date'],
name: "TransactionDate",
created() {
this.localTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
this.systemTimeZone = this.timezone;
// console.log('TransactionDate: ' + this.date);
// split date and time:
let parts = this.date.split('T');
this.dateStr = parts[0];
this.timeStr = parts[1];
},
mounted: function () {
this.$nextTick(function () {
this.$refs.date.tabIndex = 6;
this.$refs.time.tabIndex = 7;
});
},
data() {
return {
localDate: this.date,
localTimeZone: '',
systemTimeZone: '',
timeStr: '',
dateStr: '',
}
},
watch: {
dateStr: function (value) {
this.$emit('set-date', {date: value + 'T' + this.timeStr});
},
timeStr: function (value) {
this.$emit('set-date', {date: this.dateStr + 'T' + value});
}
},
methods: {},
computed: {
...mapGetters('root', ['timezone']),
}
}
</script>

View File

@ -1,100 +0,0 @@
<!--
- TransactionDescription.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<vue-typeahead-bootstrap
v-model="description"
:data="descriptions"
:inputClass="errors.length > 0 ? 'is-invalid' : ''"
:minMatchingChars="3"
:placeholder="$t('firefly.description')"
:serializer="item => item.description"
:showOnFocus=true
autofocus
tabindex="1"
ref="autoComplete"
inputName="description[]"
@input="lookupDescription"
>
<template slot="append">
<div class="input-group-append">
<button class="btn btn-outline-secondary" tabindex="-1" type="button" v-on:click="clearDescription"><span class="far fa-trash-alt"></span></button>
</div>
</template>
</vue-typeahead-bootstrap>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
import VueTypeaheadBootstrap from 'vue-typeahead-bootstrap';
import {debounce} from "lodash";
export default {
props: ['index', 'value', 'errors'],
components: {VueTypeaheadBootstrap},
name: "TransactionDescription",
data() {
return {
descriptions: [],
initialSet: [],
description: this.value,
}
},
created() {
axios.get(this.getACURL(''))
.then(response => {
this.descriptions = response.data;
this.initialSet = response.data;
this.$refs.autoComplete.$refs.input.tabIndex = 1;
});
},
methods: {
clearDescription: function () {
this.description = '';
},
getACURL: function (query) {
// update autocomplete URL:
return document.getElementsByTagName('base')[0].href + 'api/v1/autocomplete/transactions?query=' + query;
},
lookupDescription: debounce(function () {
// update autocomplete URL:
axios.get(this.getACURL(this.value))
.then(response => {
this.descriptions = response.data;
})
}, 300)
},
watch: {
value: function (value) {
this.description = value;
},
description: function (value) {
this.$emit('set-field', {field: 'description', index: this.index, value: value});
}
},
}
</script>

View File

@ -1,73 +0,0 @@
<!--
- TransactionInternalReference.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div v-if="showField" class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.external_url') }}
</div>
<div class="input-group">
<input
v-model="url"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="$t('firefly.external_url')"
name="external_url[]"
type="url"
/>
<div class="input-group-append">
<button class="btn btn-outline-secondary" tabindex="-1" type="button"><span class="far fa-trash-alt"></span></button>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['index', 'value', 'errors', 'customFields'],
name: "TransactionExternalUrl",
data() {
return {
url: this.value,
availableFields: this.customFields,
}
},
computed: {
showField: function () {
if ('external_uri' in this.availableFields) {
return this.availableFields.external_uri;
}
return false;
}
},
methods: {},
watch: {
customFields: function (value) {
this.availableFields = value;
},
value: function (value) {
this.url = value;
},
url: function (value) {
this.$emit('set-field', {field: 'external_url', index: this.index, value: value});
}
}
}
</script>

View File

@ -1,102 +0,0 @@
<!--
- TransactionForeignAmount.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<!-- FOREIGN AMOUNT -->
<div v-if="isVisible" class="form-group">
<div class="text-xs">{{ $t('form.foreign_amount') }}</div>
<div class="input-group">
<input
v-model="amount"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="$t('form.foreign_amount')"
:title="$t('form.foreign_amount')"
autocomplete="off"
name="foreign_amount[]"
type="number"
ref="input"
>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
name: "TransactionForeignAmount",
props: {
index: {},
errors: {},
value: {},
transactionType: {},
sourceCurrencyId: {},
destinationCurrencyId: {},
fractionDigits: {
type: Number,
default: 2
}
},
data() {
return {
amount: this.value,
emitEvent: true
}
},
created() {
if ('' !== this.amount) {
this.emitEvent = false;
this.amount = this.formatNumber(this.amount);
}
},
mounted: function () {
this.$nextTick(function () {
this.$refs.input.tabIndex = 5;
})
},
methods: {
formatNumber(str) {
return parseFloat(str).toFixed(this.fractionDigits);
}
},
watch: {
amount: function (value) {
if (true === this.emitEvent) {
this.$emit('set-field', {field: 'foreign_amount', index: this.index, value: value});
}
this.emitEvent = true;
},
value: function (value) {
this.amount = value;
}
},
computed: {
isVisible: {
get() {
return !('transfer' === this.transactionType.toLowerCase() && parseInt(this.sourceCurrencyId) === parseInt(this.destinationCurrencyId));
}
},
}
}
</script>

View File

@ -1,146 +0,0 @@
<!--
- TransactionForeignCurrency.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<!-- FOREIGN Currency -->
<div v-if="isVisible" class="form-group">
<div class="text-xs">&nbsp;</div>
<div class="input-group">
<select v-model="selectedCurrency" class="form-control" ref="input" name="foreign_currency_id[]">
<option v-for="currency in selectableCurrencies" :label="currency.name" :value="currency.id">{{ currency.name }}</option>
</select>
</div>
</div>
</template>
<script>
export default {
name: "TransactionForeignCurrency",
props: [
'index',
'transactionType',
'sourceCurrencyId',
'destinationCurrencyId',
'selectedCurrencyId',
'value'
],
data() {
return {
selectedCurrency: this.value,
allCurrencies: [],
selectableCurrencies: [],
dstCurrencyId: this.destinationCurrencyId,
srcCurrencyId: this.sourceCurrencyId,
lockedCurrency: 0,
emitEvent: true
}
},
watch: {
value: function (value) {
this.selectedCurrency = value;
},
sourceCurrencyId: function (value) {
// console.log('Watch sourceCurrencyId');
this.srcCurrencyId = value;
this.lockCurrency();
},
destinationCurrencyId: function (value) {
// console.log('Watch destinationCurrencyId');
this.dstCurrencyId = value;
this.lockCurrency();
},
selectedCurrency: function (value) {
this.$emit('set-field', {field: 'foreign_currency_id', index: this.index, value: value});
},
transactionType: function (value) {
this.lockCurrency();
},
},
created: function () {
// console.log('Created TransactionForeignCurrency');
this.getAllCurrencies();
},
mounted: function () {
this.$nextTick(function () {
this.$refs.input.tabIndex = 4;
})
},
methods: {
lockCurrency: function () {
// console.log('Lock currency (' + this.transactionType + ')');
this.lockedCurrency = 0;
if ('transfer' === this.transactionType.toLowerCase()) {
// console.log('IS a transfer!');
this.lockedCurrency = parseInt(this.dstCurrencyId);
this.selectedCurrency = parseInt(this.dstCurrencyId);
}
this.filterCurrencies();
},
getAllCurrencies: function () {
axios.get('./api/v1/autocomplete/currencies')
.then(response => {
this.allCurrencies = response.data;
this.filterCurrencies();
}
);
},
filterCurrencies() {
// console.log('filterCurrencies');
// console.log(this.lockedCurrency);
// if a currency is locked only that currency can (and must) be selected:
if (0 !== this.lockedCurrency) {
// console.log('Here we are');
for (let key in this.allCurrencies) {
if (this.allCurrencies.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
let current = this.allCurrencies[key];
if (parseInt(current.id) === this.lockedCurrency) {
this.selectableCurrencies = [current];
this.selectedCurrency = current.id;
}
}
}
// if source + dest ID are the same, skip the whole field.
return;
}
this.selectableCurrencies = [
{
"id": 0,
"name": this.$t('firefly.no_currency')
}
];
for (let key in this.allCurrencies) {
if (this.allCurrencies.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
let current = this.allCurrencies[key];
this.selectableCurrencies.push(current);
}
}
}
},
computed: {
isVisible: function () {
return !('transfer' === this.transactionType.toLowerCase() && parseInt(this.srcCurrencyId) === parseInt(this.dstCurrencyId));
}
}
}
</script>

View File

@ -1,101 +0,0 @@
<!--
- TransactionGroupTitle.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.split_transaction_title') }}
</div>
<vue-typeahead-bootstrap
v-model="title"
:data="descriptions"
:inputClass="errors.length > 0 ? 'is-invalid' : ''"
:minMatchingChars="3"
:placeholder="$t('firefly.split_transaction_title')"
:serializer="item => item.description"
:showOnFocus=true
inputName="group_title"
@input="lookupDescription"
>
<template slot="append">
<div class="input-group-append">
<button class="btn btn-outline-secondary" tabindex="-1" type="button" v-on:click="clearDescription"><span class="far fa-trash-alt"></span></button>
</div>
</template>
</vue-typeahead-bootstrap>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
import VueTypeaheadBootstrap from 'vue-typeahead-bootstrap';
import {debounce} from "lodash";
export default {
props: ['value', 'errors'],
name: "TransactionGroupTitle",
components: {VueTypeaheadBootstrap},
data() {
return {
descriptions: [],
initialSet: [],
title: this.value,
emitEvent: true
}
},
created() {
axios.get(this.getACURL(''))
.then(response => {
this.descriptions = response.data;
this.initialSet = response.data;
});
},
watch: {
value: function (value) {
this.title = value;
},
title: function (value) {
this.$emit('set-group-title', value);
}
},
methods: {
clearDescription: function () {
this.title = '';
},
getACURL: function (query) {
// update autocomplete URL:
return document.getElementsByTagName('base')[0].href + 'api/v1/autocomplete/transactions?query=' + query;
},
lookupDescription: debounce(function () {
// update autocomplete URL:
axios.get(this.getACURL(this.title))
.then(response => {
this.descriptions = response.data;
})
}, 300)
}
}
</script>

View File

@ -1,74 +0,0 @@
<!--
- TransactionInternalReference.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div v-if="showField" class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.internal_reference') }}
</div>
<div class="input-group">
<input
v-model="reference"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="$t('firefly.internal_reference')"
name="internal_reference[]"
type="text"
/>
<div class="input-group-append">
<button class="btn btn-outline-secondary" tabindex="-1" type="button"><span class="far fa-trash-alt"></span></button>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['index', 'value', 'errors', 'customFields'],
name: "TransactionInternalReference",
data() {
return {
reference: this.value,
availableFields: this.customFields,
emitEvent: true
}
},
computed: {
showField: function () {
if ('internal_reference' in this.availableFields) {
return this.availableFields.internal_reference;
}
return false;
}
},
methods: {},
watch: {
customFields: function (value) {
this.availableFields = value;
},
value: function (value) {
this.emitEvent = false;
this.reference = value;
},
reference: function (value) {
this.$emit('set-field', {field: 'internal_reference', index: this.index, value: value});
}
}
}
</script>

View File

@ -1,390 +0,0 @@
<!--
- TransactionLinks.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div v-if="showField">
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.journal_links') }}
</div>
<div class="row">
<div class="col">
<p v-if="links.length === 0">
<button type="button" class="btn btn-default btn-xs" data-target="#linkModal" @click="resetModal" data-toggle="modal"><span class="fas fa-plus"></span> Add transaction link</button>
</p>
<ul v-if="links.length > 0" class="list-group">
<li v-for="(transaction, index) in links" class="list-group-item" v-bind:key="index">
<em>{{ getTextForLinkType(transaction.link_type_id) }}</em>
<a :href='"./transaction/show/" + transaction.transaction_group_id'>{{ transaction.description }}</a>
<span v-if="transaction.type === 'withdrawal'">
(<span class="text-danger">{{
Intl.NumberFormat(locale, {
style: 'currency',
currency: transaction.currency_code
}).format(parseFloat(transaction.amount) * -1)
}}</span>)
</span>
<span v-if="transaction.type === 'deposit'">
(<span class="text-success">{{
Intl.NumberFormat(locale, {
style: 'currency',
currency: transaction.currency_code
}).format(parseFloat(transaction.amount))
}}</span>)
</span>
<span v-if="transaction.type === 'transfer'">
(<span class="text-info">{{
Intl.NumberFormat(locale, {
style: 'currency',
currency: transaction.currency_code
}).format(parseFloat(transaction.amount))
}}</span>)
</span>
<div class="btn-group btn-group-xs float-right">
<button type="button" class="btn btn-xs btn-danger" @click="removeLink(index)" tabindex="-1"><span class="far fa-trash-alt"></span></button>
</div>
</li>
</ul>
<div v-if="links.length > 0" class="form-text">
<button type="button" class="btn btn-default" @click="resetModal" data-target="#linkModal" data-toggle="modal"><span class="fas fa-plus"></span></button>
</div>
</div>
</div>
</div>
<!-- modal -->
<div id="linkModal" class="modal" tabindex="-1" ref="linkModal">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Transaction thing dialog.</h5>
<button aria-label="Close" class="close" data-dismiss="modal" type="button">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="container-fluid">
<div class="row">
<div class="col">
<p>
Use this form to search for transactions you wish to link to this one. When in doubt, use <code>id:*</code> where the ID is the number from
the URL.
</p>
</div>
</div>
<div class="row">
<div class="col">
<form v-on:submit.prevent="search" autocomplete="off">
<div class="input-group">
<input id="query" v-model="query" autocomplete="off" class="form-control" maxlength="255" name="search"
placeholder="Search query" type="text">
<div class="input-group-append">
<button class="btn btn-default" type="submit"><span class="fas fa-search"></span> Search</button>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col">
<span v-if="searching"><span class="fas fa-spinner fa-spin"></span></span>
<h4 v-if="searchResults.length > 0">{{ $t('firefly.search_results') }}</h4>
<table v-if="searchResults.length > 0" class="table table-sm">
<caption style="display:none;">{{ $t('firefly.search_results') }}</caption>
<thead>
<tr>
<th scope="col" colspan="2" style="width:33%">{{ $t('firefly.include') }}</th>
<th scope="col">{{ $t('firefly.transaction') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="result in searchResults">
<td>
<input v-model="result.selected" class="form-control"
type="checkbox"
@change="selectTransaction($event)"
/>
</td>
<td>
<select
v-model="result.link_type_id"
class="form-control"
@change="selectLinkType($event)"
>
<option v-for="linkType in linkTypes" :label="linkType.type" :value="linkType.id + '-' + linkType.direction">{{
linkType.type
}}
</option>
</select>
</td>
<td>
<a :href="'./transactions/show/' + result.transaction_group_id">{{ result.description }}</a>
<span v-if="result.type === 'withdrawal'">
(<span class="text-danger">{{
Intl.NumberFormat(locale, {
style: 'currency',
currency: result.currency_code
}).format(parseFloat(result.amount) * -1)
}}</span>)
</span>
<span v-if="result.type === 'deposit'">
(<span class="text-success">{{
Intl.NumberFormat(locale, {
style: 'currency',
currency: result.currency_code
}).format(parseFloat(result.amount))
}}</span>)
</span>
<span v-if="result.type === 'transfer'">
(<span class="text-info">{{
Intl.NumberFormat(locale, {
style: 'currency',
currency: result.currency_code
}).format(parseFloat(result.amount))
}}</span>)
</span>
<br/>
<em>
<a :href="'./accounts/show/' + result.source_id">{{ result.source_name }}</a>
&rarr;
<a :href="'./accounts/show/' + result.destination_id">{{ result.destination_name }}</a>
</em>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-dismiss="modal" type="button">Close</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
const lodashClonedeep = require('lodash.clonedeep');
// See reference nr. 3
export default {
props: ['index', 'value', 'errors', 'customFields'],
name: "TransactionLinks",
data() {
return {
searchResults: [],
include: [],
locale: 'en-US',
linkTypes: [],
query: '',
searching: false,
links: this.value,
availableFields: this.customFields,
emitEvent: true
}
},
created() {
this.locale = localStorage.locale ?? 'en-US';
this.emitEvent = false;
this.links = lodashClonedeep(this.value);
this.getLinkTypes();
},
computed: {
showField: function () {
if ('links' in this.availableFields) {
return this.availableFields.links;
}
return false;
}
},
watch: {
value: function (value) {
if (null !== value) {
this.emitEvent = false;
this.links = lodashClonedeep(value);
}
},
links: function (value) {
if (true === this.emitEvent) {
this.$emit('set-field', {index: this.index, field: 'links', value: lodashClonedeep(value)});
}
this.emitEvent = true;
},
customFields: function (value) {
this.availableFields = value;
}
},
methods: {
removeLink: function (index) {
this.links.splice(index, 1);
},
getTextForLinkType: function (linkTypeId) {
let parts = linkTypeId.split('-');
for (let i in this.linkTypes) {
if (this.linkTypes.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = this.linkTypes[i];
if (parts[0] === current.id && parts[1] === current.direction) {
return current.type;
}
}
}
return 'text for #' + linkTypeId;
},
selectTransaction: function (event) {
for (let i in this.searchResults) {
if (this.searchResults.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = this.searchResults[i];
if (current.selected) {
this.addToSelected(current);
}
if (!current.selected) {
// remove from
this.removeFromSelected(current);
}
}
}
},
selectLinkType: function (event) {
for (let i in this.searchResults) {
if (this.searchResults.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = this.searchResults[i];
this.updateSelected(current.transaction_journal_id, current.link_type_id);
}
}
},
updateSelected(journalId, linkTypeId) {
for (let i in this.links) {
if (this.links.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = this.links[i];
if (parseInt(current.transaction_journal_id) === journalId) {
this.links[i].link_type_id = linkTypeId;
}
}
}
},
addToSelected(journal) {
let result = this.links.find(({transaction_journal_id}) => transaction_journal_id === journal.transaction_journal_id);
if (typeof result === 'undefined') {
this.links.push(journal);
}
},
removeFromSelected(journal) {
for (let i in this.links) {
if (this.links.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = this.links[i];
if (current.transaction_journal_id === journal.transaction_journal_id) {
this.links.splice(parseInt(i), 1);
}
}
}
},
getLinkTypes: function () {
let url = './api/v1/link_types';
axios.get(url)
.then(response => {
this.parseLinkTypes(response.data);
}
);
},
resetModal: function() {
this.search();
},
parseLinkTypes: function (data) {
for (let i in data.data) {
if (data.data.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = data.data[i];
let linkTypeInward = {
id: current.id,
type: current.attributes.inward,
direction: 'inward'
};
let linkTypeOutward = {
id: current.id,
type: current.attributes.outward,
direction: 'outward'
};
if (linkTypeInward.type === linkTypeOutward.type) {
linkTypeInward.type = linkTypeInward.type + ' (←)';
linkTypeOutward.type = linkTypeOutward.type + ' (→)';
}
this.linkTypes.push(linkTypeInward);
this.linkTypes.push(linkTypeOutward);
}
}
},
search: function () {
if('' === this.query) {
this.searchResults = [];
return;
}
this.searching = true;
this.searchResults = [];
let url = './api/v1/search/transactions?limit=10&query=' + this.query;
axios.get(url)
.then(response => {
this.parseSearch(response.data);
}
);
},
parseSearch: function (data) {
for (let i in data.data) {
if (data.data.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
for (let ii in data.data[i].attributes.transactions) {
if (data.data[i].attributes.transactions.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
let current = data.data[i].attributes.transactions[ii];
current.transaction_group_id = parseInt(data.data[i].id);
current.selected = this.isJournalSelected(current.transaction_journal_id);
current.link_type_id = this.getJournalLinkType(current.transaction_journal_id);
current.link_type_text = '';
this.searchResults.push(current);
}
}
}
}
this.searching = false;
},
getJournalLinkType: function (journalId) {
for (let i in this.links) {
if (this.links.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = this.links[i];
if (current.transaction_journal_id === journalId) {
return current.link_type_id;
}
}
}
return '1-inward';
},
isJournalSelected: function (journalId) {
for (let i in this.links) {
if (this.links.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
let current = this.links[i];
if (current.transaction_journal_id === journalId) {
return true;
}
}
}
return false;
}
}
}
</script>

View File

@ -1,435 +0,0 @@
<!--
- TransactionListLarge.vue
- Copyright (c) 2020 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<div class="row">
<div class="col-lg-8 col-md-6 col-sm-12 col-xs-12">
<BPagination v-if="!loading"
v-model="currentPage"
:total-rows="total"
:per-page="perPage"
aria-controls="my-table"
></BPagination>
</div>
<div class="col-lg-4 col-md-6 col-sm-12 col-xs-12">
<button @click="newCacheKey" class="btn btn-sm float-right btn-info"><span class="fas fa-sync"></span></button>
</div>
</div>
<div class="row">
<div class="col">
<div class="card">
<div class="card-body p-0">
<BTable id="my-table" small striped hover responsive="md" primary-key="key" :no-local-sorting="false"
:items="transactions"
:fields="fields"
:per-page="perPage"
sort-icon-left
ref="table"
:current-page="currentPage"
:busy.sync="loading"
:sort-desc.sync="sortDesc"
:sort-compare="tableSortCompare"
>
<template #table-busy>
<span class="fa fa-spinner fa-spin"></span>
</template>
<template #cell(type)="data">
<span v-if="!data.item.dummy">
<span class="fas fa-long-arrow-alt-right" v-if="'deposit' === data.item.type.toLowerCase()"></span>
<span class="fas fa-long-arrow-alt-left" v-if="'withdrawal' === data.item.type.toLowerCase()"></span>
<span class="fas fa-arrows-alt-h" v-if="'transfer' === data.item.type.toLowerCase()"></span>
</span>
</template>
<template #cell(description)="data">
<span class="fa fa-spinner fa-spin" v-if="data.item.dummy"></span>
<span v-if="!data.item.split">
<span v-if="data.item.hasAttachments" class="fas fa-paperclip"></span>
<a :href="'./transactions/show/' + data.item.id" :title="data.value">
{{ data.item.description }}
</a>
</span>
<span v-if="data.item.split">
<!-- title first -->
<span class="fas fa-angle-right" @click="toggleCollapse(data.item.id)" style="cursor: pointer;"></span>
<span v-if="data.item.hasAttachments" class="fas fa-paperclip"></span>
<a :href="'./transactions/show/' + data.item.id" :title="data.value">
{{ data.item.description }}
</a><br/>
<span v-if="!data.item.collapsed">
<span v-for="(split, index) in data.item.splits" v-bind:key="index">
&nbsp; &nbsp; {{ split.description }}<br/>
</span>
</span>
</span>
</template>
<template #cell(amount)="data">
<!-- row amount first (3x) -->
<span :class="'text-success ' + (!data.item.collapsed ? 'font-weight-bold' : '')" v-if="'deposit' === data.item.type">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: data.item.currency_code}).format(data.item.amount) }}
</span>
<span :class="'text-danger ' + (!data.item.collapsed ? 'font-weight-bold' : '')" v-if="'withdrawal' === data.item.type">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: data.item.currency_code}).format(-data.item.amount) }}
</span>
<span :class="'text-muted ' + (!data.item.collapsed ? 'font-weight-bold' : '')" v-if="'transfer' === data.item.type.toLowerCase()">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: data.item.currency_code}).format(data.item.amount) }}
</span>
<br/>
<!-- splits -->
<span v-if="!data.item.collapsed">
<span v-for="(split, index) in data.item.splits" v-bind:key="index">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: split.currency_code}).format(split.amount) }}<br/>
</span>
</span>
</template>
<template #cell(date)="data">
{{ data.item.date_formatted }}
</template>
<template #cell(source_account)="data">
<!-- extra break for splits -->
<span v-if="true===data.item.split && !data.item.collapsed">
<br/>
</span>
<em v-if="true===data.item.split && data.item.collapsed">
...
</em>
<!-- loop all accounts, hidden if split -->
<span v-for="(split, index) in data.item.splits" v-bind:key="index"
v-if="false===data.item.split || (true===data.item.split && !data.item.collapsed)">
<a :href="'./accounts/show/' + split.source_id" :title="split.source_name">{{ split.source_name }}</a><br/>
</span>
</template>
<template #cell(destination_account)="data">
<!-- extra break for splits -->
<span v-if="true===data.item.split && !data.item.collapsed">
<br/>
</span>
<em v-if="true===data.item.split && data.item.collapsed">
...
</em>
<!-- loop all accounts, hidden if split -->
<span v-for="(split, index) in data.item.splits" v-bind:key="index"
v-if="false===data.item.split || (true===data.item.split && !data.item.collapsed)">
<a :href="'./accounts/show/' + split.destination_id" :title="split.destination_name">{{ split.destination_name }}</a><br/>
</span>
</template>
<template #cell(menu)="data">
<div class="btn-group btn-group-sm">
<div class="dropdown">
<button class="btn btn-light btn-sm dropdown-toggle" type="button" :id="'dropdownMenuButton' + data.item.id" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
{{ $t('firefly.actions') }}
</button>
<div class="dropdown-menu" :aria-labelledby="'dropdownMenuButton' + data.item.id">
<a class="dropdown-item" :href="'./transactions/edit/' + data.item.id"><span class="fa fas fa-pencil-alt"></span> {{
$t('firefly.edit')
}}</a>
<a class="dropdown-item" :href="'./transactions/delete/' + data.item.id"><span class="fa far fa-trash"></span> {{
$t('firefly.delete')
}}</a>
</div>
</div>
</div>
</template>
<template #cell(category_name)="data">
<!-- extra break for splits -->
<span v-if="true===data.item.split && !data.item.collapsed">
<br/>
</span>
<em v-if="true===data.item.split && data.item.collapsed">
...
</em>
<!-- loop all categories, hidden if split -->
<span v-for="(split, index) in data.item.splits" v-bind:key="index"
v-if="false===data.item.split || (true===data.item.split && !data.item.collapsed)">
<a :href="'./categories/show/' + split.category_id" :title="split.category_name">{{ split.category_name }}</a><br/>
</span>
</template>
</BTable>
</div>
<div class="card-footer"> (button)
<a :href="'./transactions/create/TODO'" class="btn btn-success"
:title="$t('firefly.create_new_transaction')">{{ $t('firefly.create_new_transaction') }}</a>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-8 col-md-6 col-sm-12 col-xs-12">
<BPagination
v-model="currentPage"
:total-rows="total"
:per-page="perPage"
aria-controls="my-table"
></BPagination>
</div>
<div class="col-lg-4 col-md-6 col-sm-12 col-xs-12">
<button @click="newCacheKey" class="btn btn-sm float-right btn-info"><span class="fas fa-sync"></span></button>
</div>
</div>
</div>
</template>
<script>
import {mapGetters, mapMutations} from "vuex";
import {BPagination, BTable} from 'bootstrap-vue';
import format from "date-fns/format";
export default {
name: "TransactionListLarge",
components: {BPagination, BTable},
data() {
return {
locale: 'en-US',
fields: [],
currentPage: 1,
transactions: [],
loading: true
}
},
computed: {
...mapGetters('root', ['listPageSize', 'cacheKey']),
},
created() {
this.locale = localStorage.locale ?? 'en-US';
this.updateFieldList();
//this.currentPage = this.page;
this.parseTransactions();
},
watch: {
currentPage: function (value) {
// console.log('Watch currentPage go to ' + value);
this.$emit('jump-page', {page: value});
},
entries: function (value) {
// console.log('detected new transactions! (' + value.length + ')');
this.parseTransactions();
if(this.isEmpty) {
this.loading=false;
}
},
// value: function (value) {
// // console.log('Watch value!');
// }
},
methods: {
...mapMutations('root', ['refreshCacheKey',]),
parseTransactions: function () {
this.transactions = [];
// console.log('Start of parseTransactions. Count of entries is ' + this.entries.length + ' and page is ' + this.page);
// console.log('Reported total is ' + this.total);
if (0 === this.entries.length) {
// console.log('Will not render now because length is 0.');
return;
}
// console.log('Now have ' + this.transactions.length + ' transactions');
for (let i = 0; i < this.total; i++) {
this.transactions.push({dummy: true, type: 'x'});
// console.log('Push dummy to index ' + i);
// console.log('Now have ' + this.transactions.length + ' transactions');
}
// console.log('Generated ' + this.total + ' dummies');
// console.log('Now have ' + this.transactions.length + ' transactions');
let index = (this.page - 1) * this.perPage;
// console.log('Start index is ' + index);
for (let i in this.entries) {
let transaction = this.entries[i];
// build split
this.transactions[index] = this.parseTransaction(transaction);
// console.log('Push transaction to index ' + index);
// console.log('Now have ' + this.transactions.length + ' transactions');
index++;
}
// console.log('Added ' + this.entries.length + ' entries');
// console.log('Now have ' + this.transactions.length + ' transactions');
// console.log(this.transactions);
this.loading = false;
},
newCacheKey: function () {
this.refreshCacheKey();
// console.log('Cache key is now ' + this.cacheKey);
this.$emit('refreshed-cache-key');
},
updateFieldList: function () {
this.fields = [
{key: 'type', label: ' ', sortable: false},
{key: 'description', label: this.$t('list.description'), sortable: true},
{key: 'amount', label: this.$t('list.amount'), sortable: true},
{key: 'date', label: this.$t('list.date'), sortable: true},
{key: 'source_account', label: this.$t('list.source_account'), sortable: true},
{key: 'destination_account', label: this.$t('list.destination_account'), sortable: true},
{key: 'category_name', label: this.$t('list.category'), sortable: true},
{key: 'menu', label: ' ', sortable: false},
];
},
/**
* Parse a single transaction.
* @param transaction
*/
parseTransaction: function (transaction) {
let row = {};
// default values:
row.splits = [];
row.key = transaction.id;
row.id = transaction.id
row.dummy = false;
row.hasAttachments = false;
// pick this up from the first transaction
let first = transaction.attributes.transactions[0];
row.type = first.type;
row.date = new Date(first.date);
row.date_formatted = format(row.date, this.$t('config.month_and_day_fns'));
row.description = first.description;
row.collapsed = true;
row.split = false;
row.amount = 0;
row.currency_code = first.currency_code;
if (transaction.attributes.transactions.length > 1) {
row.split = true;
row.description = transaction.attributes.group_title;
}
// collapsed?
if (typeof transaction.collapsed !== 'undefined') {
row.collapsed = transaction.collapsed;
}
//console.log('is collapsed? ' + row.collapsed);
// then loop each split
for (let i in transaction.attributes.transactions) {
if (transaction.attributes.transactions.hasOwnProperty(i)) {
let info = transaction.attributes.transactions[i];
let split = {};
row.amount = row.amount + parseFloat(info.amount);
split.type = info.type;
split.description = info.description;
split.amount = info.amount;
split.currency_code = info.currency_code;
split.source_name = info.source_name;
split.source_id = info.source_id;
split.destination_name = info.destination_name;
split.destination_id = info.destination_id;
split.category_id = info.category_id;
split.category_name = info.category_name;
split.split_index = i;
if(true === info.has_attachments) {
row.hasAttachments = true;
}
row.splits.push(split);
}
}
return row;
},
toggleCollapse: function (id) {
let transaction = this.transactions.filter(transaction => transaction.id === id)[0];
transaction.collapsed = !transaction.collapsed;
},
tableSortCompare: function (aRow, bRow, key, sortDesc, formatter, compareOptions, compareLocale) {
let a = aRow[key]
let b = bRow[key]
if (aRow.id === bRow.id) {
// Order split transactions normally when compared to each other, except always put the header first
if (aRow.split_parent === null) {
return sortDesc ? 1 : -1;
} else if (bRow.split_parent === null) {
return sortDesc ? -1 : 1;
}
} else {
// Sort split transactions based on their parent when compared to other transactions
if (aRow.split && aRow.split_parent !== null) {
a = aRow.split_parent[key]
}
if (bRow.split && bRow.split_parent !== null) {
b = bRow.split_parent[key]
}
}
if (
(typeof a === 'number' && typeof b === 'number') ||
(a instanceof Date && b instanceof Date)
) {
// If both compared fields are native numbers or both are native dates
return a < b ? -1 : a > b ? 1 : 0
} else {
// Otherwise stringify the field data and use String.prototype.localeCompare
return toString(a).localeCompare(toString(b), compareLocale, compareOptions)
}
function toString(value) {
if (value === null || typeof value === 'undefined') {
return ''
} else if (value instanceof Object) {
return Object.keys(value)
.sort()
.map(key => toString(value[key]))
.join(' ')
} else {
return String(value)
}
}
},
},
props: {
page: {
type: Number
},
perPage: {
type: Number,
default: 1
},
sortDesc: {
type: Boolean,
default: true
},
isEmpty: {
type: Boolean,
default: false
},
total: {
type: Number,
default: 1
},
entries: {
type: Array,
default: function () {
return [];
}
},
accountId: {
type: Number,
default: function () {
return 0;
}
},
}
}
</script>

View File

@ -1,168 +0,0 @@
<!--
- TransactionLocation.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div v-if="showField" class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.location') }}
</div>
<div style="width:100%;height:300px;">
<l-map
ref="myMap"
:center="center"
:zoom="zoom" style="width:100%;height:300px;"
@ready="prepMap()"
@update:zoom="zoomUpdated"
@update:center="centerUpdated"
@update:bounds="boundsUpdated"
>
<l-tile-layer :url="url"></l-tile-layer>
<l-marker :lat-lng="marker" :visible="hasMarker"></l-marker>
</l-map>
<span>
<button class="btn btn-default btn-xs" @click="clearLocation">{{ $t('firefly.clear_location') }}</button>
</span>
</div>
<p>&nbsp;</p>
</div>
</template>
<script>
// If you need to reference 'L', such as in 'L.icon', then be sure to
// explicitly import 'leaflet' into your component
// import L from 'leaflet';
import {LMap, LMarker, LTileLayer} from 'vue2-leaflet';
import 'leaflet/dist/leaflet.css';
import L from 'leaflet';
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
iconUrl: require('leaflet/dist/images/marker-icon.png'),
shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});
export default {
name: "TransactionLocation",
props: {
index: {},
value: {
type: Object,
required: false
},
errors: {},
customFields: {},
},
components: {
LMap,
LTileLayer,
LMarker,
},
created() {
if (null === this.value || typeof this.value === 'undefined') {
axios.get('./api/v1/configuration/firefly.default_location').then(response => {
this.zoom = parseInt(response.data.data.value.zoom_level);
this.center =
[
parseFloat(response.data.data.value.latitude),
parseFloat(response.data.data.value.longitude),
]
;
});
return;
}
if (null !== this.value.zoom_level && null !== this.value.latitude && null !== this.value.longitude) {
this.zoom = this.value.zoom_level;
this.center = [
parseFloat(this.value.latitude),
parseFloat(this.value.longitude),
];
this.hasMarker = true;
}
},
data() {
return {
availableFields: this.customFields,
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
zoom: 3,
center: [0, 0],
bounds: null,
map: null,
hasMarker: false,
marker: [0, 0],
}
},
methods: {
prepMap: function () {
this.map = this.$refs.myMap.mapObject;
this.map.on('contextmenu', this.setObjectLocation);
this.map.on('zoomend', this.saveZoomLevel);
},
setObjectLocation: function (event) {
this.marker = [event.latlng.lat, event.latlng.lng];
this.hasMarker = true;
this.emitEvent();
},
saveZoomLevel: function () {
this.emitEvent();
},
clearLocation: function () {
this.hasMarker = false;
this.emitEvent();
},
emitEvent() {
this.$emit('set-marker-location', {
index: this.index,
zoomLevel: this.zoom,
lat: this.marker[0],
lng: this.marker[1],
hasMarker: this.hasMarker
}
);
},
zoomUpdated(zoom) {
this.zoom = zoom;
},
centerUpdated(center) {
this.center = center;
},
boundsUpdated(bounds) {
this.bounds = bounds;
}
},
computed: {
showField: function () {
if ('location' in this.availableFields) {
return this.availableFields.location;
}
return false;
}
},
watch: {
customFields: function (value) {
this.availableFields = value;
},
}
}
</script>

View File

@ -1,69 +0,0 @@
<!--
- TransactionNotes.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div v-if="showField" class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.notes') }}
</div>
<div class="input-group">
<textarea
v-model="notes"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="$t('firefly.notes')"
></textarea>
</div>
</div>
</template>
<script>
export default {
props: ['index', 'value', 'errors', 'customFields'],
name: "TransactionNotes",
data() {
return {
notes: this.value,
availableFields: this.customFields,
}
},
computed: {
showField: function () {
if ('notes' in this.availableFields) {
return this.availableFields.notes;
}
return false;
}
},
watch: {
value: function (value) {
this.notes = value;
},
customFields: function (value) {
this.availableFields = value;
},
notes: function (value) {
this.$emit('set-field', {field: 'notes', index: this.index, value: value});
}
}
}
</script>

View File

@ -1,100 +0,0 @@
<!--
- TransactionPiggyBank.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.piggy_bank') }}
</div>
<div class="input-group">
<select
ref="piggy_bank_id"
v-model="piggy_bank_id"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:title="$t('firefly.piggy_bank')"
autocomplete="off"
name="piggy_bank_id[]"
>
<option v-for="piggy in this.piggyList" :label="piggy.name_with_balance" :value="piggy.id">{{ piggy.name_with_balance }}</option>
</select>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
export default {
props: ['index', 'value', 'errors'],
name: "TransactionPiggyBank",
data() {
return {
piggyList: [],
piggy_bank_id: this.value,
}
},
created() {
this.collectData();
},
methods: {
collectData() {
this.piggyList.push(
{
id: 0,
name_with_balance: this.$t('firefly.no_piggy_bank'),
}
);
this.getPiggies();
},
getPiggies() {
axios.get('./api/v1/autocomplete/piggy-banks-with-balance')
.then(response => {
this.parsePiggies(response.data);
}
);
},
parsePiggies(data) {
for (let key in data) {
if (data.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
let current = data[key];
this.piggyList.push(
{
id: parseInt(current.id),
name_with_balance: current.name_with_balance
}
);
}
}
},
},
watch: {
value: function (value) {
this.piggy_bank_id = value;
},
piggy_bank_id: function (value) {
this.$emit('set-field', {field: 'piggy_bank_id', index: this.index, value: value});
this.emitEvent = true;
}
}
}
</script>

View File

@ -1,136 +0,0 @@
<!--
- TransactionTags.vue
- Copyright (c) 2021 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.tags') }}
</div>
<div class="input-group">
<vue-tags-input
v-model="currentTag"
:add-only-from-autocomplete="false"
:autocomplete-items="autocompleteItems"
:tags="tags"
ref="input"
:title="$t('firefly.tags')"
v-bind:placeholder="$t('firefly.tags')"
@tags-changed="newTags => this.tags = newTags"
/>
</div>
<span v-if="errors.length > 0">
<span v-for="error in errors" class="text-danger small">{{ error }}<br/></span>
</span>
</div>
</template>
<script>
import VueTagsInput from "@johmun/vue-tags-input";
import axios from "axios";
export default {
name: "TransactionTags",
components: {
VueTagsInput
},
props: ['value', 'index', 'errors'],
data() {
return {
autocompleteItems: [],
debounce: null,
tags: [],
currentTag: '',
updateTags: true, // the idea is that this is always true, except when the tags-function sets the value.
tagList: this.value,
};
},
created() {
let tags = [];
for (let i in this.value) {
if (this.value.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
tags.push({text: this.value[i]});
}
}
this.updateTags = false;
this.tags = tags;
},
watch: {
'currentTag': 'initItems',
value: function (value) {
this.tagList = value;
},
tagList: function (value) {
// console.log('watch tagList');
this.$emit('set-field', {field: 'tags', index: this.index, value: value});
this.updateTags = false;
this.tags = value;
},
tags: function (value) {
if (this.updateTags) {
let shortList = [];
for (let key in value) {
if (value.hasOwnProperty(key)) {
shortList.push({text: value[key].text});
}
}
this.tagList = shortList;
}
this.updateTags = true;
}
},
methods: {
initItems() {
if (this.currentTag.length < 2) {
return;
}
const url = document.getElementsByTagName('base')[0].href + `api/v1/autocomplete/tags?query=${this.currentTag}`;
clearTimeout(this.debounce);
this.debounce = setTimeout(() => {
axios.get(url).then(response => {
this.autocompleteItems = response.data.map(item => {
return {text: item.tag};
});
}).catch(() => console.warn('Oh. Something went wrong loading tags.'));
}, 300);
},
},
}
</script>
<style>
.vue-tags-input {
width: 100%;
max-width: 100% !important;
display: block;
border-radius: 0.25rem;
}
.ti-input {
border-radius: 0.25rem;
max-width: 100%;
width: 100%;
}
.ti-new-tag-input {
font-size: 1rem;
}
</style>

54
frontend/src/i18n.js vendored
View File

@ -1,54 +0,0 @@
/*
* i18n.js
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Create VueI18n instance with options
module.exports = new vuei18n({
locale: document.documentElement.lang, // set locale
fallbackLocale: 'en',
messages: {
'bg': require('./locales/bg.json'),
'cs': require('./locales/cs.json'),
'de': require('./locales/de.json'),
'en': require('./locales/en.json'),
'en-us': require('./locales/en.json'),
'en-gb': require('./locales/en-gb.json'),
'es': require('./locales/es.json'),
'el': require('./locales/el.json'),
'fr': require('./locales/fr.json'),
'hu': require('./locales/hu.json'),
//'id': require('./locales/id.json'),
'it': require('./locales/it.json'),
'ja': require('./locales/ja.json'),
'nl': require('./locales/nl.json'),
'nb': require('./locales/nb.json'),
'pl': require('./locales/pl.json'),
'fi': require('./locales/fi.json'),
'pt-br': require('./locales/pt-br.json'),
'pt-pt': require('./locales/pt.json'),
'ro': require('./locales/ro.json'),
'ru': require('./locales/ru.json'),
//'zh': require('./locales/zh.json'),
'zh-tw': require('./locales/zh-tw.json'),
'zh-cn': require('./locales/zh-cn.json'),
'sk': require('./locales/sk.json'),
'sv': require('./locales/sv.json'),
'vi': require('./locales/vi.json'),
}
});

View File

@ -1,258 +0,0 @@
{
"firefly": {
"Transfer": "\u041f\u0440\u0435\u0445\u0432\u044a\u0440\u043b\u044f\u043d\u0435",
"Withdrawal": "\u0422\u0435\u0433\u043b\u0435\u043d\u0435",
"Deposit": "\u0414\u0435\u043f\u043e\u0437\u0438\u0442",
"date_and_time": "Date and time",
"no_currency": "(\u0431\u0435\u0437 \u0432\u0430\u043b\u0443\u0442\u0430)",
"date": "\u0414\u0430\u0442\u0430",
"time": "Time",
"no_budget": "(\u0431\u0435\u0437 \u0431\u044e\u0434\u0436\u0435\u0442)",
"destination_account": "\u041f\u0440\u0438\u0445\u043e\u0434\u043d\u0430 \u0441\u043c\u0435\u0442\u043a\u0430",
"source_account": "\u0420\u0430\u0437\u0445\u043e\u0434\u043d\u0430 \u0441\u043c\u0435\u0442\u043a\u0430",
"single_split": "\u0420\u0430\u0437\u0434\u0435\u043b",
"create_new_transaction": "Create a new transaction",
"balance": "\u0421\u0430\u043b\u0434\u043e",
"transaction_journal_extra": "Extra information",
"transaction_journal_meta": "\u041c\u0435\u0442\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f",
"basic_journal_information": "Basic transaction information",
"bills_to_pay": "\u0421\u043c\u0435\u0442\u043a\u0438 \u0437\u0430 \u043f\u043b\u0430\u0449\u0430\u043d\u0435",
"left_to_spend": "\u041e\u0441\u0442\u0430\u043d\u0430\u043b\u0438 \u0437\u0430 \u0445\u0430\u0440\u0447\u0435\u043d\u0435",
"attachments": "\u041f\u0440\u0438\u043a\u0430\u0447\u0435\u043d\u0438 \u0444\u0430\u0439\u043b\u043e\u0432\u0435",
"net_worth": "\u041d\u0435\u0442\u043d\u0430 \u0441\u0442\u043e\u0439\u043d\u043e\u0441\u0442",
"bill": "\u0421\u043c\u0435\u0442\u043a\u0430",
"no_bill": "(\u043d\u044f\u043c\u0430 \u0441\u043c\u0435\u0442\u043a\u0430)",
"tags": "\u0415\u0442\u0438\u043a\u0435\u0442\u0438",
"internal_reference": "Internal reference",
"external_url": "External URL",
"no_piggy_bank": "(\u0431\u0435\u0437 \u043a\u0430\u0441\u0438\u0447\u043a\u0430)",
"paid": "\u041f\u043b\u0430\u0442\u0435\u043d\u0438",
"notes": "\u0411\u0435\u043b\u0435\u0436\u043a\u0438",
"yourAccounts": "\u0412\u0430\u0448\u0438\u0442\u0435 \u0441\u043c\u0435\u0442\u043a\u0438",
"go_to_asset_accounts": "\u0412\u0438\u0436\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0442\u0435 \u0441\u0438",
"delete_account": "\u0418\u0437\u0442\u0440\u0438\u0432\u0430\u043d\u0435 \u043d\u0430 \u043f\u0440\u043e\u0444\u0438\u043b",
"transaction_table_description": "\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u0449\u0430 \u0432\u0430\u0448\u0438\u0442\u0435 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438",
"account": "\u0421\u043c\u0435\u0442\u043a\u0430",
"description": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435",
"amount": "\u0421\u0443\u043c\u0430",
"budget": "\u0411\u044e\u0434\u0436\u0435\u0442",
"category": "\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f",
"opposing_account": "\u041f\u0440\u043e\u0442\u0438\u0432\u043e\u043f\u043e\u043b\u043e\u0436\u043d\u0430 \u0441\u043c\u0435\u0442\u043a\u0430",
"budgets": "\u0411\u044e\u0434\u0436\u0435\u0442\u0438",
"categories": "\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438",
"go_to_budgets": "\u0412\u0438\u0436\u0442\u0435 \u0431\u044e\u0434\u0436\u0435\u0442\u0438\u0442\u0435 \u0441\u0438",
"income": "\u041f\u0440\u0438\u0445\u043e\u0434\u0438",
"go_to_deposits": "\u041e\u0442\u0438\u0434\u0438 \u0432 \u0434\u0435\u043f\u043e\u0437\u0438\u0442\u0438",
"go_to_categories": "\u0412\u0438\u0436 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438\u0442\u0435 \u0441\u0438",
"expense_accounts": "\u0421\u043c\u0435\u0442\u043a\u0438 \u0437\u0430 \u0440\u0430\u0437\u0445\u043e\u0434\u0438",
"go_to_expenses": "\u041e\u0442\u0438\u0434\u0438 \u0432 \u0420\u0430\u0437\u0445\u043e\u0434\u0438",
"go_to_bills": "\u0412\u0438\u0436 \u0441\u043c\u0435\u0442\u043a\u0438\u0442\u0435 \u0441\u0438",
"bills": "\u0421\u043c\u0435\u0442\u043a\u0438",
"last_thirty_days": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0442\u0435 \u0442\u0440\u0438\u0439\u0441\u0435\u0442 \u0434\u043d\u0438",
"last_seven_days": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0442\u0435 \u0441\u0435\u0434\u0435\u043c \u0434\u043d\u0438",
"go_to_piggies": "\u0412\u0438\u0436 \u043a\u0430\u0441\u0438\u0447\u043a\u0438\u0442\u0435 \u0441\u0438",
"saved": "\u0417\u0430\u043f\u0438\u0441\u0430\u043d",
"piggy_banks": "\u041a\u0430\u0441\u0438\u0447\u043a\u0438",
"piggy_bank": "\u041a\u0430\u0441\u0438\u0447\u043a\u0430",
"amounts": "\u0421\u0443\u043c\u0438",
"left": "\u041e\u0441\u0442\u0430\u043d\u0430\u043b\u0438",
"spent": "\u041f\u043e\u0445\u0430\u0440\u0447\u0435\u043d\u0438",
"Default asset account": "\u0421\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u0430\u043a\u0442\u0438\u0432\u0438 \u043f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435",
"search_results": "\u0420\u0435\u0437\u0443\u043b\u0442\u0430\u0442\u0438 \u043e\u0442 \u0442\u044a\u0440\u0441\u0435\u043d\u0435\u0442\u043e",
"include": "Include?",
"transaction": "\u0422\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f",
"account_role_defaultAsset": "\u0421\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u0430\u043a\u0442\u0438\u0432\u0438 \u043f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435",
"account_role_savingAsset": "\u0421\u043f\u0435\u0441\u0442\u043e\u0432\u043d\u0430 \u0441\u043c\u0435\u0442\u043a\u0430",
"account_role_sharedAsset": "\u0421\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u0441\u043f\u043e\u0434\u0435\u043b\u0435\u043d\u0438 \u0430\u043a\u0442\u0438\u0432\u0438",
"clear_location": "\u0418\u0437\u0447\u0438\u0441\u0442\u0438 \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e",
"account_role_ccAsset": "\u041a\u0440\u0435\u0434\u0438\u0442\u043d\u0430 \u043a\u0430\u0440\u0442\u0430",
"account_role_cashWalletAsset": "\u041f\u0430\u0440\u0438\u0447\u0435\u043d \u043f\u043e\u0440\u0442\u0444\u0435\u0439\u043b",
"daily_budgets": "\u0414\u043d\u0435\u0432\u043d\u0438 \u0431\u044e\u0434\u0436\u0435\u0442\u0438",
"weekly_budgets": "\u0421\u0435\u0434\u043c\u0438\u0447\u043d\u0438 \u0431\u044e\u0434\u0436\u0435\u0442\u0438",
"monthly_budgets": "\u041c\u0435\u0441\u0435\u0447\u043d\u0438 \u0431\u044e\u0434\u0436\u0435\u0442\u0438",
"journals_in_period_for_account_js": "All transactions for account {title} between {start} and {end}",
"quarterly_budgets": "\u0422\u0440\u0438\u043c\u0435\u0441\u0435\u0447\u043d\u0438 \u0431\u044e\u0434\u0436\u0435\u0442\u0438",
"create_new_expense": "\u0421\u044a\u0437\u0434\u0430\u0439 \u043d\u043e\u0432\u0430 \u0441\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u0440\u0430\u0437\u0445\u043e\u0434\u0438",
"create_new_revenue": "\u0421\u044a\u0437\u0434\u0430\u0439 \u043d\u043e\u0432\u0430 \u0441\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u0438",
"create_new_liabilities": "Create new liability",
"half_year_budgets": "\u0428\u0435\u0441\u0442\u043c\u0435\u0441\u0435\u0447\u043d\u0438 \u0431\u044e\u0434\u0436\u0435\u0442\u0438",
"yearly_budgets": "\u0413\u043e\u0434\u0438\u0448\u043d\u0438 \u0431\u044e\u0434\u0436\u0435\u0442\u0438",
"split_transaction_title": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043d\u0430 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0430 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f",
"errors_submission": "\u0418\u043c\u0430\u0448\u0435 \u043d\u0435\u0449\u043e \u043d\u0435\u0440\u0435\u0434\u043d\u043e \u0441 \u0432\u0430\u0448\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u0438. \u041c\u043e\u043b\u044f, \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u0442\u0435 \u0433\u0440\u0435\u0448\u043a\u0438\u0442\u0435.",
"flash_error": "\u0413\u0440\u0435\u0448\u043a\u0430!",
"store_transaction": "Store transaction",
"flash_success": "\u0423\u0441\u043f\u0435\u0445!",
"create_another": "\u0421\u043b\u0435\u0434 \u0441\u044a\u0445\u0440\u0430\u043d\u044f\u0432\u0430\u043d\u0435\u0442\u043e \u0441\u0435 \u0432\u044a\u0440\u043d\u0435\u0442\u0435 \u0442\u0443\u043a, \u0437\u0430 \u0434\u0430 \u0441\u044a\u0437\u0434\u0430\u0434\u0435\u0442\u0435 \u043d\u043e\u0432\u0430.",
"update_transaction": "\u041e\u0431\u043d\u043e\u0432\u0438 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f\u0442\u0430",
"after_update_create_another": "\u0421\u043b\u0435\u0434 \u043e\u0431\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435\u0442\u043e \u0441\u0435 \u0432\u044a\u0440\u043d\u0435\u0442\u0435 \u0442\u0443\u043a, \u0437\u0430 \u0434\u0430 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435 \u0441 \u0440\u0435\u0434\u0430\u043a\u0446\u0438\u044f\u0442\u0430.",
"transaction_updated_no_changes": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") did not receive any changes.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") has been updated.",
"spent_x_of_y": "Spent {amount} of {total}",
"search": "\u0422\u044a\u0440\u0441\u0435\u043d\u0435",
"create_new_asset": "\u0421\u044a\u0437\u0434\u0430\u0439 \u043d\u043e\u0432\u0430 \u0441\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u0430\u043a\u0442\u0438\u0432\u0438",
"asset_accounts": "\u0421\u043c\u0435\u0442\u043a\u0438 \u0437\u0430 \u0430\u043a\u0442\u0438\u0432\u0438",
"reset_after": "\u0418\u0437\u0447\u0438\u0441\u0442\u0432\u0430\u043d\u0435 \u043d\u0430 \u0444\u043e\u0440\u043c\u0443\u043b\u044f\u0440\u0430 \u0441\u043b\u0435\u0434 \u0438\u0437\u043f\u0440\u0430\u0449\u0430\u043d\u0435",
"bill_paid_on": "Paid on {date}",
"first_split_decides": "The first split determines the value of this field",
"first_split_overrules_source": "The first split may overrule the source account",
"first_split_overrules_destination": "The first split may overrule the destination account",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">\u0422\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f #{ID}(\"{title}\")<\/a> \u0431\u0435\u0448\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u0430.",
"custom_period": "Custom period",
"reset_to_current": "Reset to current period",
"select_period": "Select a period",
"location": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435",
"other_budgets": "\u0412\u0440\u0435\u043c\u0435\u0432\u043e \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u043d\u0438 \u0431\u044e\u0434\u0436\u0435\u0442\u0438",
"journal_links": "\u0412\u0440\u044a\u0437\u043a\u0438 \u043d\u0430 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f",
"go_to_withdrawals": "\u0412\u0438\u0436\u0442\u0435 \u0442\u0435\u0433\u043b\u0435\u043d\u0438\u044f\u0442\u0430 \u0441\u0438",
"revenue_accounts": "\u0421\u043c\u0435\u0442\u043a\u0438 \u0437\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u0438",
"add_another_split": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0434\u0440\u0443\u0433 \u0440\u0430\u0437\u0434\u0435\u043b",
"actions": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f",
"earned": "\u0421\u043f\u0435\u0447\u0435\u043b\u0435\u043d\u0438",
"empty": "(\u043f\u0440\u0430\u0437\u043d\u043e)",
"edit": "\u041f\u0440\u043e\u043c\u0435\u043d\u0438",
"never": "\u041d\u0438\u043a\u043e\u0433\u0430",
"account_type_Loan": "\u0417\u0430\u0435\u043c",
"account_type_Mortgage": "\u0418\u043f\u043e\u0442\u0435\u043a\u0430",
"stored_new_account_js": "New account \"<a href=\"accounts\/show\/{ID}\">{name}<\/a>\" stored!",
"account_type_Debt": "\u0414\u044a\u043b\u0433",
"liability_direction_null_short": "Unknown",
"delete": "\u0418\u0437\u0442\u0440\u0438\u0439",
"store_new_asset_account": "\u0417\u0430\u043f\u0430\u043c\u0435\u0442\u0438 \u043d\u043e\u0432\u0430 \u0441\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u0430\u043a\u0442\u0438\u0432\u0438",
"store_new_expense_account": "\u0417\u0430\u043f\u0430\u043c\u0435\u0442\u0438 \u043d\u043e\u0432\u0430 \u0441\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u0440\u0430\u0437\u0445\u043e\u0434\u0438",
"store_new_liabilities_account": "\u0417\u0430\u043f\u0430\u043c\u0435\u0442\u0438 \u043d\u043e\u0432\u043e \u0437\u0430\u0434\u044a\u043b\u0436\u0435\u043d\u0438\u0435",
"store_new_revenue_account": "\u0417\u0430\u043f\u0430\u043c\u0435\u0442\u0438 \u043d\u043e\u0432\u0430 \u0441\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u0438",
"mandatoryFields": "\u0417\u0430\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u0438 \u043f\u043e\u043b\u0435\u0442\u0430",
"optionalFields": "\u041d\u0435\u0437\u0430\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u0438 \u043f\u043e\u043b\u0435\u0442\u0430",
"reconcile_this_account": "\u0421\u044a\u0433\u043b\u0430\u0441\u0443\u0432\u0430\u0439 \u0442\u0430\u0437\u0438 \u0441\u043c\u0435\u0442\u043a\u0430",
"interest_calc_weekly": "Per week",
"interest_calc_monthly": "\u041d\u0430 \u043c\u0435\u0441\u0435\u0446",
"interest_calc_quarterly": "Per quarter",
"interest_calc_half-year": "Per half year",
"interest_calc_yearly": "\u0413\u043e\u0434\u0438\u0448\u043d\u043e",
"liability_direction_credit": "I am owed this debt",
"liability_direction_debit": "I owe this debt to somebody else",
"liability_direction_credit_short": "Owed this debt",
"liability_direction_debit_short": "Owe this debt",
"account_type_debt": "Debt",
"account_type_loan": "Loan",
"left_in_debt": "Amount due",
"account_type_mortgage": "Mortgage",
"save_transactions_by_moving_js": "No transactions|Save this transaction by moving it to another account. |Save these transactions by moving them to another account.",
"none_in_select_list": "(\u043d\u0438\u0449\u043e)",
"transaction_expand_split": "Expand split",
"transaction_collapse_split": "Collapse split",
"default_group_title_name": "(\u0431\u0435\u0437 \u0433\u0440\u0443\u043f\u0430)",
"bill_repeats_weekly": "Repeats weekly",
"bill_repeats_monthly": "Repeats monthly",
"bill_repeats_quarterly": "Repeats quarterly",
"bill_repeats_half-year": "Repeats every half year",
"bill_repeats_yearly": "Repeats yearly",
"bill_repeats_weekly_other": "Repeats every other week",
"bill_repeats_monthly_other": "Repeats every other month",
"bill_repeats_quarterly_other": "Repeats every other quarter",
"bill_repeats_half-year_other": "Repeats yearly",
"bill_repeats_yearly_other": "Repeats every other year",
"bill_repeats_weekly_skip": "Repeats every {skip} weeks",
"bill_repeats_monthly_skip": "Repeats every {skip} months",
"bill_repeats_quarterly_skip": "Repeats every {skip} quarters",
"bill_repeats_half-year_skip": "Repeats every {skip} half years",
"bill_repeats_yearly_skip": "Repeats every {skip} years",
"not_expected_period": "\u041d\u0435 \u0441\u0435 \u043e\u0447\u0430\u043a\u0432\u0430 \u0442\u043e\u0437\u0438 \u043f\u0435\u0440\u0438\u043e\u0434",
"subscriptions": "Subscriptions",
"bill_expected_date_js": "Expected {date}",
"inactive": "\u041d\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e",
"forever": "Forever",
"extension_date_is": "Extension date is {date}",
"create_new_bill": "\u0421\u044a\u0437\u0434\u0430\u0439 \u043d\u043e\u0432\u0430 \u0441\u043c\u0435\u0442\u043a\u0430",
"store_new_bill": "\u0417\u0430\u043f\u0430\u043c\u0435\u0442\u0435\u0442\u0435 \u043d\u043e\u0432\u0430 \u0441\u043c\u0435\u0442\u043a\u0430",
"repeat_freq_yearly": "\u0435\u0436\u0435\u0433\u043e\u0434\u043d\u043e",
"repeat_freq_half-year": "\u043d\u0430 \u0432\u0441\u0435\u043a\u0438 6 \u043c\u0435\u0441\u0435\u0446\u0430",
"repeat_freq_quarterly": "\u0442\u0440\u0438\u043c\u0435\u0441\u0435\u0447\u043d\u043e",
"repeat_freq_monthly": "\u043c\u0435\u0441\u0435\u0447\u043d\u043e",
"repeat_freq_weekly": "\u0435\u0436\u0435\u0441\u0435\u0434\u043c\u0438\u0447\u043d\u043e",
"credit_card_type_monthlyFull": "Full payment every month",
"update_liabilities_account": "\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0430\u0439 \u0437\u0430\u0434\u044a\u043b\u0436\u0435\u043d\u0438\u0435",
"update_expense_account": "\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0430\u0439 \u0441\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u0440\u0430\u0437\u0445\u043e\u0434\u0438",
"update_revenue_account": "\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0430\u0439 \u0441\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u0438",
"update_undefined_account": "Update account",
"update_asset_account": "\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0430\u0439 \u0441\u043c\u0435\u0442\u043a\u0430 \u0437\u0430 \u0430\u043a\u0442\u0438\u0432\u0438",
"updated_account_js": "Updated account \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
},
"list": {
"piggy_bank": "\u041a\u0430\u0441\u0438\u0447\u043a\u0430",
"percentage": "%",
"amount": "\u0421\u0443\u043c\u0430",
"lastActivity": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0430 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442",
"name": "\u0418\u043c\u0435",
"role": "\u041f\u0440\u0438\u0432\u0438\u043b\u0435\u0433\u0438\u0438",
"description": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435",
"date": "\u0414\u0430\u0442\u0430",
"source_account": "\u0420\u0430\u0437\u0445\u043e\u0434\u043d\u0430 \u0441\u043c\u0435\u0442\u043a\u0430",
"destination_account": "\u041f\u0440\u0438\u0445\u043e\u0434\u043d\u0430 \u0441\u043c\u0435\u0442\u043a\u0430",
"category": "\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f",
"iban": "IBAN",
"interest": "\u041b\u0438\u0445\u0432\u0430",
"interest_period": "Interest period",
"liability_type": "\u0412\u0438\u0434 \u043d\u0430 \u0437\u0430\u0434\u044a\u043b\u0436\u0435\u043d\u0438\u0435\u0442\u043e",
"liability_direction": "Liability in\/out",
"currentBalance": "\u0422\u0435\u043a\u0443\u0449 \u0431\u0430\u043b\u0430\u043d\u0441",
"next_expected_match": "\u0421\u043b\u0435\u0434\u0432\u0430\u0449o \u043e\u0447\u0430\u043a\u0432\u0430\u043do \u0441\u044a\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435",
"expected_info": "Next expected transaction",
"start_date": "Start date",
"end_date": "End date",
"payment_info": "Payment information"
},
"config": {
"html_language": "bg",
"week_in_year_fns": "'Week' w, yyyy",
"month_and_day_fns": "MMMM d, y",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "\u0421\u0443\u043c\u0430 \u0432\u044a\u0432 \u0432\u0430\u043b\u0443\u0442\u0430",
"interest_date": "\u041f\u0430\u0434\u0435\u0436 \u043d\u0430 \u043b\u0438\u0445\u0432\u0430",
"name": "\u0418\u043c\u0435",
"amount": "\u0421\u0443\u043c\u0430",
"iban": "IBAN",
"BIC": "BIC",
"notes": "\u0411\u0435\u043b\u0435\u0436\u043a\u0438",
"location": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435",
"repeat_freq": "\u041f\u043e\u0432\u0442\u043e\u0440\u0435\u043d\u0438\u044f",
"skip": "\u041f\u0440\u043e\u043f\u0443\u0441\u043d\u0438",
"startdate": "\u041d\u0430\u0447\u0430\u043b\u043d\u0430 \u0434\u0430\u0442\u0430",
"enddate": "End date",
"object_group": "\u0413\u0440\u0443\u043f\u0430",
"attachments": "\u041f\u0440\u0438\u043a\u0430\u0447\u0435\u043d\u0438 \u0444\u0430\u0439\u043b\u043e\u0432\u0435",
"deletePermanently": "\u0411\u0435\u0437\u0432\u044a\u0437\u0432\u0440\u0430\u0442\u043d\u043e \u0438\u0437\u0442\u0440\u0438\u0432\u0430\u043d\u0435",
"active": "\u0410\u043a\u0442\u0438\u0432\u0435\u043d",
"include_net_worth": "\u0412\u043a\u043b\u044e\u0447\u0438 \u0432 \u043e\u0431\u0449\u043e\u0442\u043e \u0431\u043e\u0433\u0430\u0442\u0441\u0442\u0432\u043e",
"cc_type": "\u041f\u043e\u0433\u0430\u0441\u0438\u0442\u0435\u043b\u0435\u043d \u043f\u043b\u0430\u043d \u043d\u0430 \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u0430 \u043a\u0430\u0440\u0442\u0430",
"account_number": "\u041d\u043e\u043c\u0435\u0440 \u043d\u0430 \u0441\u043c\u0435\u0442\u043a\u0430",
"cc_monthly_payment_date": "\u0414\u0430\u0442\u0430 \u0437\u0430 \u043c\u0435\u0441\u0435\u0447\u043d\u043e \u043f\u043b\u0430\u0449\u0430\u043d\u0435 \u043f\u043e \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u0430 \u043a\u0430\u0440\u0442\u0430",
"virtual_balance": "\u0412\u0438\u0440\u0442\u0443\u0430\u043b\u0435\u043d \u0431\u0430\u043b\u0430\u043d\u0441",
"opening_balance": "\u041d\u0430\u0447\u0430\u043b\u043d\u043e \u0441\u0430\u043b\u0434\u043e",
"opening_balance_date": "\u0414\u0430\u0442\u0430 \u043d\u0430 \u043d\u0430\u0447\u0430\u043b\u043d\u043e\u0442\u043e \u0441\u0430\u043b\u0434\u043e",
"date": "\u0414\u0430\u0442\u0430",
"interest": "\u041b\u0438\u0445\u0432\u0430",
"interest_period": "\u041b\u0438\u0445\u0432\u0435\u043d \u043f\u0435\u0440\u0438\u043e\u0434",
"currency_id": "\u0412\u0430\u043b\u0443\u0442\u0430",
"liability_type": "Liability type",
"account_role": "\u0420\u043e\u043b\u044f \u043d\u0430 \u0441\u043c\u0435\u0442\u043a\u0430\u0442\u0430",
"liability_direction": "Liability in\/out",
"book_date": "\u0414\u0430\u0442\u0430 \u043d\u0430 \u043e\u0441\u0447\u0435\u0442\u043e\u0432\u043e\u0434\u044f\u0432\u0430\u043d\u0435",
"permDeleteWarning": "\u0418\u0437\u0442\u0440\u0438\u0432\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u043d\u0435\u0449\u0430 \u043e\u0442 Firefly III \u0435 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u0438 \u043d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0431\u044a\u0434\u0435 \u0432\u044a\u0437\u0441\u0442\u0430\u043d\u043e\u0432\u0435\u043d\u043e.",
"account_areYouSure_js": "Are you sure you want to delete the account named \"{name}\"?",
"also_delete_piggyBanks_js": "No piggy banks|The only piggy bank connected to this account will be deleted as well.|All {count} piggy banks connected to this account will be deleted as well.",
"also_delete_transactions_js": "No transactions|The only transaction connected to this account will be deleted as well.|All {count} transactions connected to this account will be deleted as well.",
"process_date": "\u0414\u0430\u0442\u0430 \u043d\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430",
"due_date": "\u0414\u0430\u0442\u0430 \u043d\u0430 \u043f\u0430\u0434\u0435\u0436",
"payment_date": "\u0414\u0430\u0442\u0430 \u043d\u0430 \u043f\u043b\u0430\u0449\u0430\u043d\u0435",
"invoice_date": "\u0414\u0430\u0442\u0430 \u043d\u0430 \u0444\u0430\u043a\u0442\u0443\u0440\u0430",
"amount_min": "\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u043d\u0430 \u0441\u0443\u043c\u0430",
"amount_max": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u043d\u0430 \u0441\u0443\u043c\u0430",
"start_date": "\u041d\u0430\u0447\u0430\u043b\u043e \u043d\u0430 \u043e\u0431\u0445\u0432\u0430\u0442\u0430",
"end_date": "\u041a\u0440\u0430\u0439 \u043d\u0430 \u043e\u0431\u0445\u0432\u0430\u0442\u0430",
"extension_date": "Extension date"
}
}

View File

@ -1,258 +0,0 @@
{
"firefly": {
"Transfer": "P\u0159evod",
"Withdrawal": "V\u00fdb\u011br",
"Deposit": "Vklad",
"date_and_time": "Datum a \u010das",
"no_currency": "(\u017e\u00e1dn\u00e1 m\u011bna)",
"date": "Datum",
"time": "\u010cas",
"no_budget": "(\u017e\u00e1dn\u00fd rozpo\u010det)",
"destination_account": "C\u00edlov\u00fd \u00fa\u010det",
"source_account": "Zdrojov\u00fd \u00fa\u010det",
"single_split": "Rozd\u011blit",
"create_new_transaction": "Vytvo\u0159it novou transakci",
"balance": "Z\u016fstatek",
"transaction_journal_extra": "V\u00edce informac\u00ed",
"transaction_journal_meta": "Meta informace",
"basic_journal_information": "Basic transaction information",
"bills_to_pay": "Faktury k zaplacen\u00ed",
"left_to_spend": "Zb\u00fdv\u00e1 k utracen\u00ed",
"attachments": "P\u0159\u00edlohy",
"net_worth": "\u010cist\u00e9 jm\u011bn\u00ed",
"bill": "\u00da\u010det",
"no_bill": "(no bill)",
"tags": "\u0160t\u00edtky",
"internal_reference": "Intern\u00ed odkaz",
"external_url": "Extern\u00ed URL adresa",
"no_piggy_bank": "(\u017e\u00e1dn\u00e1 pokladni\u010dka)",
"paid": "Zaplaceno",
"notes": "Pozn\u00e1mky",
"yourAccounts": "Va\u0161e \u00fa\u010dty",
"go_to_asset_accounts": "Zobrazit \u00fa\u010dty s aktivy",
"delete_account": "Smazat \u00fa\u010det",
"transaction_table_description": "A table containing your transactions",
"account": "\u00da\u010det",
"description": "Popis",
"amount": "\u010c\u00e1stka",
"budget": "Rozpo\u010det",
"category": "Kategorie",
"opposing_account": "Proti\u00fa\u010det",
"budgets": "Rozpo\u010dty",
"categories": "Kategorie",
"go_to_budgets": "P\u0159ej\u00edt k rozpo\u010dt\u016fm",
"income": "Odm\u011bna\/p\u0159\u00edjem",
"go_to_deposits": "P\u0159ej\u00edt na vklady",
"go_to_categories": "P\u0159ej\u00edt ke kategori\u00edm",
"expense_accounts": "V\u00fddajov\u00e9 \u00fa\u010dty",
"go_to_expenses": "P\u0159ej\u00edt na v\u00fddaje",
"go_to_bills": "P\u0159ej\u00edt k \u00fa\u010dt\u016fm",
"bills": "\u00da\u010dty",
"last_thirty_days": "Uplynul\u00fdch 30 dn\u00ed",
"last_seven_days": "Uplynul\u00fdch 7 dn\u016f",
"go_to_piggies": "P\u0159ej\u00edt k pokladni\u010dk\u00e1m",
"saved": "Ulo\u017eeno",
"piggy_banks": "Pokladni\u010dky",
"piggy_bank": "Pokladni\u010dka",
"amounts": "Amounts",
"left": "Zb\u00fdv\u00e1",
"spent": "Utraceno",
"Default asset account": "V\u00fdchoz\u00ed \u00fa\u010det s aktivy",
"search_results": "V\u00fdsledky hled\u00e1n\u00ed",
"include": "Include?",
"transaction": "Transakce",
"account_role_defaultAsset": "V\u00fdchoz\u00ed \u00fa\u010det aktiv",
"account_role_savingAsset": "Spo\u0159ic\u00ed \u00fa\u010det",
"account_role_sharedAsset": "Sd\u00edlen\u00fd \u00fa\u010det aktiv",
"clear_location": "Vymazat um\u00edst\u011bn\u00ed",
"account_role_ccAsset": "Kreditn\u00ed karta",
"account_role_cashWalletAsset": "Pen\u011b\u017eenka",
"daily_budgets": "Denn\u00ed rozpo\u010dty",
"weekly_budgets": "T\u00fddenn\u00ed rozpo\u010dty",
"monthly_budgets": "M\u011bs\u00ed\u010dn\u00ed rozpo\u010dty",
"journals_in_period_for_account_js": "Ve\u0161ker\u00e9 transakce pro \u00fa\u010det {title} mezi {start} a {end}",
"quarterly_budgets": "\u010ctvrtletn\u00ed rozpo\u010dty",
"create_new_expense": "Vytvo\u0159it v\u00fddajov\u00fd \u00fa\u010det",
"create_new_revenue": "Vytvo\u0159it nov\u00fd p\u0159\u00edjmov\u00fd \u00fa\u010det",
"create_new_liabilities": "Create new liability",
"half_year_budgets": "Pololetn\u00ed rozpo\u010dty",
"yearly_budgets": "Ro\u010dn\u00ed rozpo\u010dty",
"split_transaction_title": "Popis roz\u00fa\u010dtov\u00e1n\u00ed",
"errors_submission": "There was something wrong with your submission. Please check out the errors.",
"flash_error": "Chyba!",
"store_transaction": "Store transaction",
"flash_success": "\u00dasp\u011b\u0161n\u011b dokon\u010deno!",
"create_another": "After storing, return here to create another one.",
"update_transaction": "Aktualizovat transakci",
"after_update_create_another": "After updating, return here to continue editing.",
"transaction_updated_no_changes": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") did not receive any changes.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") has been updated.",
"spent_x_of_y": "Spent {amount} of {total}",
"search": "Hledat",
"create_new_asset": "Vytvo\u0159it nov\u00fd \u00fa\u010det aktiv",
"asset_accounts": "\u00da\u010dty aktiv",
"reset_after": "Reset form after submission",
"bill_paid_on": "Paid on {date}",
"first_split_decides": "The first split determines the value of this field",
"first_split_overrules_source": "The first split may overrule the source account",
"first_split_overrules_destination": "The first split may overrule the destination account",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
"custom_period": "Vlastn\u00ed obdob\u00ed",
"reset_to_current": "Obnovit aktu\u00e1ln\u00ed obdob\u00ed",
"select_period": "Vyberte obdob\u00ed",
"location": "Um\u00edst\u011bn\u00ed",
"other_budgets": "Custom timed budgets",
"journal_links": "Transaction links",
"go_to_withdrawals": "P\u0159ej\u00edt na v\u00fdb\u011bry",
"revenue_accounts": "P\u0159\u00edjmov\u00e9 \u00fa\u010dty",
"add_another_split": "P\u0159idat dal\u0161\u00ed roz\u00fa\u010dtov\u00e1n\u00ed",
"actions": "Akce",
"earned": "Vyd\u011bl\u00e1no",
"empty": "(pr\u00e1zdn\u00e9)",
"edit": "Upravit",
"never": "Nikdy",
"account_type_Loan": "P\u016fj\u010dka",
"account_type_Mortgage": "Hypot\u00e9ka",
"stored_new_account_js": "New account \"<a href=\"accounts\/show\/{ID}\">{name}<\/a>\" stored!",
"account_type_Debt": "Dluh",
"liability_direction_null_short": "Unknown",
"delete": "Odstranit",
"store_new_asset_account": "Ulo\u017eit nov\u00fd \u00fa\u010det aktiv",
"store_new_expense_account": "Ulo\u017eit nov\u00fd v\u00fddajov\u00fd \u00fa\u010det",
"store_new_liabilities_account": "Ulo\u017eit nov\u00fd z\u00e1vazek",
"store_new_revenue_account": "Ulo\u017eit nov\u00fd p\u0159\u00edjmov\u00fd \u00fa\u010det",
"mandatoryFields": "Povinn\u00e9 kolonky",
"optionalFields": "Voliteln\u00e9 kolonky",
"reconcile_this_account": "Vy\u00fa\u010dtovat tento \u00fa\u010det",
"interest_calc_weekly": "Per week",
"interest_calc_monthly": "Za m\u011bs\u00edc",
"interest_calc_quarterly": "Per quarter",
"interest_calc_half-year": "Per half year",
"interest_calc_yearly": "Za rok",
"liability_direction_credit": "I am owed this debt",
"liability_direction_debit": "I owe this debt to somebody else",
"liability_direction_credit_short": "Owed this debt",
"liability_direction_debit_short": "Owe this debt",
"account_type_debt": "Debt",
"account_type_loan": "Loan",
"left_in_debt": "Amount due",
"account_type_mortgage": "Mortgage",
"save_transactions_by_moving_js": "No transactions|Save this transaction by moving it to another account. |Save these transactions by moving them to another account.",
"none_in_select_list": "(\u017e\u00e1dn\u00e9)",
"transaction_expand_split": "Expand split",
"transaction_collapse_split": "Collapse split",
"default_group_title_name": "(neseskupeno)",
"bill_repeats_weekly": "Repeats weekly",
"bill_repeats_monthly": "Repeats monthly",
"bill_repeats_quarterly": "Repeats quarterly",
"bill_repeats_half-year": "Repeats every half year",
"bill_repeats_yearly": "Repeats yearly",
"bill_repeats_weekly_other": "Repeats every other week",
"bill_repeats_monthly_other": "Repeats every other month",
"bill_repeats_quarterly_other": "Repeats every other quarter",
"bill_repeats_half-year_other": "Repeats yearly",
"bill_repeats_yearly_other": "Repeats every other year",
"bill_repeats_weekly_skip": "Repeats every {skip} weeks",
"bill_repeats_monthly_skip": "Repeats every {skip} months",
"bill_repeats_quarterly_skip": "Repeats every {skip} quarters",
"bill_repeats_half-year_skip": "Repeats every {skip} half years",
"bill_repeats_yearly_skip": "Repeats every {skip} years",
"not_expected_period": "Not expected this period",
"subscriptions": "Subscriptions",
"bill_expected_date_js": "Expected {date}",
"inactive": "Neaktivn\u00ed",
"forever": "Forever",
"extension_date_is": "Extension date is {date}",
"create_new_bill": "Vytvo\u0159it novou fakturu",
"store_new_bill": "Ulo\u017eit novou \u00fa\u010dtenku",
"repeat_freq_yearly": "ro\u010dn\u011b",
"repeat_freq_half-year": "p\u016floro\u010dn\u011b",
"repeat_freq_quarterly": "\u010dtvrtletn\u011b",
"repeat_freq_monthly": "m\u011bs\u00ed\u010dn\u011b",
"repeat_freq_weekly": "t\u00fddn\u011b",
"credit_card_type_monthlyFull": "Full payment every month",
"update_liabilities_account": "Aktualizovat z\u00e1vazek",
"update_expense_account": "Aktualizovat v\u00fddajov\u00fd \u00fa\u010det",
"update_revenue_account": "Aktualizovat p\u0159\u00edjmov\u00fd \u00fa\u010det",
"update_undefined_account": "Update account",
"update_asset_account": "Aktualizovat v\u00fddajov\u00fd \u00fa\u010det",
"updated_account_js": "Updated account \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
},
"list": {
"piggy_bank": "Pokladni\u010dka",
"percentage": "%",
"amount": "\u010c\u00e1stka",
"lastActivity": "Posledn\u00ed aktivita",
"name": "Jm\u00e9no",
"role": "Role",
"description": "Popis",
"date": "Datum",
"source_account": "Zdrojov\u00fd \u00fa\u010det",
"destination_account": "C\u00edlov\u00fd \u00fa\u010det",
"category": "Kategorie",
"iban": "IBAN",
"interest": "\u00darok",
"interest_period": "\u00darokov\u00e9 obdob\u00ed",
"liability_type": "Typ z\u00e1vazku",
"liability_direction": "Sm\u011br z\u00e1vazku",
"currentBalance": "Aktu\u00e1ln\u00ed z\u016fstatek",
"next_expected_match": "Dal\u0161\u00ed o\u010dek\u00e1van\u00e1 shoda",
"expected_info": "Dal\u0161\u00ed o\u010dek\u00e1van\u00e1 transakce",
"start_date": "Datum zah\u00e1jen\u00ed",
"end_date": "Datum ukon\u010den\u00ed",
"payment_info": "Informace o platb\u011b"
},
"config": {
"html_language": "cs",
"week_in_year_fns": "'t\u00fdden' w, yyyy",
"month_and_day_fns": "d MMMM, y",
"quarter_fns": "Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "\u010c\u00e1stka v ciz\u00ed m\u011bn\u011b",
"interest_date": "\u00darokov\u00e9 datum",
"name": "N\u00e1zev",
"amount": "\u010c\u00e1stka",
"iban": "IBAN",
"BIC": "BIC",
"notes": "Pozn\u00e1mky",
"location": "\u00dadaje o poloze",
"repeat_freq": "Opakuje se",
"skip": "P\u0159esko\u010dit",
"startdate": "Datum zah\u00e1jen\u00ed",
"enddate": "Datum ukon\u010den\u00ed",
"object_group": "Skupina",
"attachments": "P\u0159\u00edlohy",
"deletePermanently": "Nadobro smazat",
"active": "Aktivn\u00ed",
"include_net_worth": "Zahrnout do \u010dist\u00e9ho jm\u011bn\u00ed",
"cc_type": "Z\u00fa\u010dtovac\u00ed obdob\u00ed kreditn\u00ed karty",
"account_number": "\u010c\u00edslo \u00fa\u010dtu",
"cc_monthly_payment_date": "Datum m\u011bs\u00ed\u010dn\u00ed \u00fahrady kreditn\u00ed karty",
"virtual_balance": "Virtu\u00e1ln\u00ed z\u016fstatek",
"opening_balance": "Po\u010d\u00e1te\u010dn\u00ed z\u016fstatek",
"opening_balance_date": "Datum po\u010d\u00e1te\u010dn\u00edho z\u016fstatku",
"date": "Datum",
"interest": "\u00darok",
"interest_period": "\u00darokov\u00e9 obdob\u00ed",
"currency_id": "M\u011bna",
"liability_type": "Typ z\u00e1vazku",
"account_role": "Role \u00fa\u010dtu",
"liability_direction": "Sm\u011br z\u00e1vazku",
"book_date": "Datum rezervace",
"permDeleteWarning": "Odstran\u011bn\u00ed v\u011bc\u00ed z Firefly III je trval\u00e9 a nelze vr\u00e1tit zp\u011bt.",
"account_areYouSure_js": "Jste si jisti, \u017ee chcete odstranit \u00fa\u010det s n\u00e1zvem \"{name}\"?",
"also_delete_piggyBanks_js": "\u017d\u00e1dn\u00e9 pokladni\u010dky|Jedin\u00e1 pokladni\u010dka p\u0159ipojen\u00e1 k tomuto \u00fa\u010dtu bude tak\u00e9 odstran\u011bna. V\u0161ech {count} pokladni\u010dek, kter\u00e9 jsou p\u0159ipojeny k tomuto \u00fa\u010dtu, bude tak\u00e9 odstran\u011bno.",
"also_delete_transactions_js": "\u017d\u00e1dn\u00e9 transakce|Jedin\u00e1 transakce p\u0159ipojen\u00e1 k tomuto \u00fa\u010dtu bude tak\u00e9 smaz\u00e1na.|V\u0161ech {count} transakc\u00ed p\u0159ipojen\u00fdch k tomuto \u00fa\u010dtu bude tak\u00e9 odstran\u011bno.",
"process_date": "Datum zpracov\u00e1n\u00ed",
"due_date": "Datum splatnosti",
"payment_date": "Datum zaplacen\u00ed",
"invoice_date": "Datum vystaven\u00ed",
"amount_min": "Minim\u00e1ln\u00ed \u010d\u00e1stka",
"amount_max": "Maxim\u00e1ln\u00ed \u010d\u00e1stka",
"start_date": "Za\u010d\u00e1tek rozsahu",
"end_date": "Konec rozsahu",
"extension_date": "Datum roz\u0161\u00ed\u0159en\u00ed"
}
}

View File

@ -1,258 +0,0 @@
{
"firefly": {
"Transfer": "Umbuchung",
"Withdrawal": "Ausgabe",
"Deposit": "Einnahme",
"date_and_time": "Datum und Uhrzeit",
"no_currency": "(ohne W\u00e4hrung)",
"date": "Datum",
"time": "Uhrzeit",
"no_budget": "(kein Budget)",
"destination_account": "Zielkonto",
"source_account": "Quellkonto",
"single_split": "Teil",
"create_new_transaction": "Neue Buchung erstellen",
"balance": "Kontostand",
"transaction_journal_extra": "Zus\u00e4tzliche Informationen",
"transaction_journal_meta": "Metainformationen",
"basic_journal_information": "Allgemeine Buchungsinformationen",
"bills_to_pay": "Unbezahlte Rechnungen",
"left_to_spend": "Verbleibend zum Ausgeben",
"attachments": "Anh\u00e4nge",
"net_worth": "Eigenkapital",
"bill": "Rechnung",
"no_bill": "(keine Belege)",
"tags": "Schlagw\u00f6rter",
"internal_reference": "Interner Verweis",
"external_url": "Externe URL",
"no_piggy_bank": "(kein Sparschwein)",
"paid": "Bezahlt",
"notes": "Notizen",
"yourAccounts": "Deine Konten",
"go_to_asset_accounts": "Bestandskonten anzeigen",
"delete_account": "Konto l\u00f6schen",
"transaction_table_description": "Eine Tabelle mit Ihren Buchungen",
"account": "Konto",
"description": "Beschreibung",
"amount": "Betrag",
"budget": "Budget",
"category": "Kategorie",
"opposing_account": "Gegenkonto",
"budgets": "Budgets",
"categories": "Kategorien",
"go_to_budgets": "Budgets anzeigen",
"income": "Einnahmen \/ Einkommen",
"go_to_deposits": "Einnahmen anzeigen",
"go_to_categories": "Kategorien anzeigen",
"expense_accounts": "Ausgabekonten",
"go_to_expenses": "Ausgaben anzeigen",
"go_to_bills": "Rechnungen anzeigen",
"bills": "Vertr\u00e4ge",
"last_thirty_days": "Letzte 30 Tage",
"last_seven_days": "Letzte sieben Tage",
"go_to_piggies": "Sparschweine anzeigen",
"saved": "Gespeichert",
"piggy_banks": "Sparschweine",
"piggy_bank": "Sparschwein",
"amounts": "Betr\u00e4ge",
"left": "\u00dcbrig",
"spent": "Ausgegeben",
"Default asset account": "Standard-Bestandskonto",
"search_results": "Suchergebnisse",
"include": "Inbegriffen?",
"transaction": "\u00dcberweisung",
"account_role_defaultAsset": "Standard-Bestandskonto",
"account_role_savingAsset": "Sparkonto",
"account_role_sharedAsset": "Gemeinsames Bestandskonto",
"clear_location": "Ort leeren",
"account_role_ccAsset": "Kreditkarte",
"account_role_cashWalletAsset": "Geldb\u00f6rse",
"daily_budgets": "Tagesbudgets",
"weekly_budgets": "Wochenbudgets",
"monthly_budgets": "Monatsbudgets",
"journals_in_period_for_account_js": "Alle Buchungen f\u00fcr Account {title} zwischen {start} und {end}",
"quarterly_budgets": "Quartalsbudgets",
"create_new_expense": "Neues Ausgabenkonto erstellen",
"create_new_revenue": "Neues Einnahmenkonto erstellen",
"create_new_liabilities": "Neue Verbindlichkeit anlegen",
"half_year_budgets": "Halbjahresbudgets",
"yearly_budgets": "Jahresbudgets",
"split_transaction_title": "Beschreibung der Splittbuchung",
"errors_submission": "Ihre \u00dcbermittlung ist fehlgeschlagen. Bitte \u00fcberpr\u00fcfen Sie die Fehler.",
"flash_error": "Fehler!",
"store_transaction": "Buchung speichern",
"flash_success": "Geschafft!",
"create_another": "Nach dem Speichern hierher zur\u00fcckkehren, um ein weiteres zu erstellen.",
"update_transaction": "Buchung aktualisieren",
"after_update_create_another": "Nach dem Aktualisieren hierher zur\u00fcckkehren, um weiter zu bearbeiten.",
"transaction_updated_no_changes": "<a href=\"transactions\/show\/{ID}\">Die Buchung #{ID}<\/a> (\"{title}\") wurde nicht ver\u00e4ndert.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">Die Buchung #{ID}<\/a> (\"{title}\") wurde aktualisiert.",
"spent_x_of_y": "{amount} von {total} ausgegeben",
"search": "Suche",
"create_new_asset": "Neues Bestandskonto erstellen",
"asset_accounts": "Bestandskonten",
"reset_after": "Formular nach der \u00dcbermittlung zur\u00fccksetzen",
"bill_paid_on": "Bezahlt am {date}",
"first_split_decides": "Die erste Aufteilung bestimmt den Wert dieses Feldes",
"first_split_overrules_source": "Die erste Aufteilung k\u00f6nnte das Quellkonto \u00fcberschreiben",
"first_split_overrules_destination": "Die erste Aufteilung k\u00f6nnte das Zielkonto \u00fcberschreiben",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Buchung #{ID} (\"{title}\")<\/a> wurde gespeichert.",
"custom_period": "Benutzerdefinierter Zeitraum",
"reset_to_current": "Auf aktuellen Zeitraum zur\u00fccksetzen",
"select_period": "Zeitraum ausw\u00e4hlen",
"location": "Standort",
"other_budgets": "Zeitlich befristete Budgets",
"journal_links": "Buchungsverkn\u00fcpfungen",
"go_to_withdrawals": "Ausgaben anzeigen",
"revenue_accounts": "Einnahmekonten",
"add_another_split": "Eine weitere Aufteilung hinzuf\u00fcgen",
"actions": "Aktionen",
"earned": "Eingenommen",
"empty": "(leer)",
"edit": "Bearbeiten",
"never": "Nie",
"account_type_Loan": "Darlehen",
"account_type_Mortgage": "Hypothek",
"stored_new_account_js": "Neues Konto \"<a href=\"accounts\/show\/{ID}\">\u201e{name}\u201d<\/a>\" gespeichert!",
"account_type_Debt": "Schuld",
"liability_direction_null_short": "Unbekannt",
"delete": "L\u00f6schen",
"store_new_asset_account": "Neues Bestandskonto speichern",
"store_new_expense_account": "Neues Ausgabenkonto speichern",
"store_new_liabilities_account": "Neue Verbindlichkeit speichern",
"store_new_revenue_account": "Neues Einnahmenkonto speichern",
"mandatoryFields": "Pflichtfelder",
"optionalFields": "Optionale Felder",
"reconcile_this_account": "Dieses Konto abgleichen",
"interest_calc_weekly": "Pro Woche",
"interest_calc_monthly": "Monatlich",
"interest_calc_quarterly": "Viertelj\u00e4hrlich",
"interest_calc_half-year": "Halbj\u00e4hrlich",
"interest_calc_yearly": "J\u00e4hrlich",
"liability_direction_credit": "Mir wird dies geschuldet",
"liability_direction_debit": "Ich schulde dies jemandem",
"liability_direction_credit_short": "Geschuldeter Betrag",
"liability_direction_debit_short": "Schuldiger Betrag",
"account_type_debt": "Schulden",
"account_type_loan": "Darlehen",
"left_in_debt": "F\u00e4lliger Betrag",
"account_type_mortgage": "Hypothek",
"save_transactions_by_moving_js": "Keine Buchungen|Speichern Sie diese Buchung, indem Sie sie auf ein anderes Konto verschieben. |Speichern Sie diese Buchungen, indem Sie sie auf ein anderes Konto verschieben.",
"none_in_select_list": "(Keine)",
"transaction_expand_split": "Aufteilung erweitern",
"transaction_collapse_split": "Aufteilung reduzieren",
"default_group_title_name": "(ohne Gruppierung)",
"bill_repeats_weekly": "Wiederholt sich w\u00f6chentlich",
"bill_repeats_monthly": "Wiederholt sich monatlich",
"bill_repeats_quarterly": "Wiederholt sich viertelj\u00e4hrlich",
"bill_repeats_half-year": "Wiederholt sich halbj\u00e4hrlich",
"bill_repeats_yearly": "Wiederholt sich j\u00e4hrlich",
"bill_repeats_weekly_other": "Wiederholt sich jede zweite Woche",
"bill_repeats_monthly_other": "Wiederholt sich jeden zweiten Monat",
"bill_repeats_quarterly_other": "Wiederholt sich jedes zweite Vierteljahr",
"bill_repeats_half-year_other": "Wiederholt sich j\u00e4hrlich",
"bill_repeats_yearly_other": "Wiederholt sich jedes zweite Jahr",
"bill_repeats_weekly_skip": "Wiederholt sich alle {skip} Wochen",
"bill_repeats_monthly_skip": "Wiederholt sich alle {skip} Monate",
"bill_repeats_quarterly_skip": "Wiederholt sich alle {skip} Vierteljahre",
"bill_repeats_half-year_skip": "Wiederholt sich alle {skip} Halbjahre",
"bill_repeats_yearly_skip": "Wiederholt sich alle {skip} Jahre",
"not_expected_period": "In diesem Zeitraum nicht erwartet",
"subscriptions": "Abonnements",
"bill_expected_date_js": "Erwartet {date}",
"inactive": "Inaktiv",
"forever": "Dauerhaft",
"extension_date_is": "Zeitpunkt der Verl\u00e4ngerung ist {date}",
"create_new_bill": "Eine neue Rechnung erstellen",
"store_new_bill": "Neue Rechnung speichern",
"repeat_freq_yearly": "J\u00e4hrlich",
"repeat_freq_half-year": "halbj\u00e4hrlich",
"repeat_freq_quarterly": "viertelj\u00e4hrlich",
"repeat_freq_monthly": "monatlich",
"repeat_freq_weekly": "w\u00f6chentlich",
"credit_card_type_monthlyFull": "Vollst\u00e4ndige Zahlung jeden Monat",
"update_liabilities_account": "Verbindlichkeit aktualisieren",
"update_expense_account": "Ausgabenkonto aktualisieren",
"update_revenue_account": "Einnahmenkonto aktualisieren",
"update_undefined_account": "Konto aktualisieren",
"update_asset_account": "Bestandskonto aktualisieren",
"updated_account_js": "Konto \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\" aktualisiert."
},
"list": {
"piggy_bank": "Sparschwein",
"percentage": "%",
"amount": "Betrag",
"lastActivity": "Letzte Aktivit\u00e4t",
"name": "Name",
"role": "Rolle",
"description": "Beschreibung",
"date": "Datum",
"source_account": "Quellkonto",
"destination_account": "Zielkonto",
"category": "Kategorie",
"iban": "IBAN",
"interest": "Zinsen",
"interest_period": "Zinsperiode",
"liability_type": "Verbindlichkeitsart",
"liability_direction": "Verbindlichkeit ein\/aus",
"currentBalance": "Aktueller Kontostand",
"next_expected_match": "N\u00e4chste erwartete \u00dcbereinstimmung",
"expected_info": "N\u00e4chste erwartete Buchung",
"start_date": "Beginnt am",
"end_date": "Endet am",
"payment_info": "Zahlungsinformationen"
},
"config": {
"html_language": "de",
"week_in_year_fns": "'Woche' ww\/yyyy",
"month_and_day_fns": "d. MMMM Y",
"quarter_fns": "'Q'QQQ, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Ausl\u00e4ndischer Betrag",
"interest_date": "Zinstermin",
"name": "Name",
"amount": "Betrag",
"iban": "IBAN",
"BIC": "BIC",
"notes": "Notizen",
"location": "Herkunft",
"repeat_freq": "Wiederholungen",
"skip": "\u00dcberspringen",
"startdate": "Startdatum",
"enddate": "Endet am",
"object_group": "Gruppe",
"attachments": "Anh\u00e4nge",
"deletePermanently": "Dauerhaft l\u00f6schen",
"active": "Aktiv",
"include_net_worth": "Im Eigenkapital enthalten",
"cc_type": "Kreditkartenzahlungsplan",
"account_number": "Kontonummer",
"cc_monthly_payment_date": "Monatlicher Kreditkartenzahlungsplan",
"virtual_balance": "Virtueller Kontostand",
"opening_balance": "Er\u00f6ffnungsbilanz",
"opening_balance_date": "Er\u00f6ffnungsbilanzdatum",
"date": "Datum",
"interest": "Zinsen",
"interest_period": "Verzinsungszeitraum",
"currency_id": "W\u00e4hrung",
"liability_type": "Art der Verbindlichkeit",
"account_role": "Kontenfunktion",
"liability_direction": "Verbindlichkeit Ein\/Aus",
"book_date": "Buchungsdatum",
"permDeleteWarning": "Das L\u00f6schen von Dingen in Firefly III ist dauerhaft und kann nicht r\u00fcckg\u00e4ngig gemacht werden.",
"account_areYouSure_js": "M\u00f6chten Sie das Konto \u201e{name}\u201d wirklich l\u00f6schen?",
"also_delete_piggyBanks_js": "Keine Sparschweine|Das einzige Sparschwein, welches mit diesem Konto verbunden ist, wird ebenfalls gel\u00f6scht.|Alle {count} Sparschweine, welche mit diesem Konto verbunden sind, werden ebenfalls gel\u00f6scht.",
"also_delete_transactions_js": "Keine Buchungen|Die einzige Buchung, die mit diesem Konto verbunden ist, wird ebenfalls gel\u00f6scht.|Alle {count} Buchungen, die mit diesem Konto verbunden sind, werden ebenfalls gel\u00f6scht.",
"process_date": "Bearbeitungsdatum",
"due_date": "F\u00e4lligkeitstermin",
"payment_date": "Zahlungsdatum",
"invoice_date": "Rechnungsdatum",
"amount_min": "Mindestbetrag",
"amount_max": "H\u00f6chstbetrag",
"start_date": "Anfang des Bereichs",
"end_date": "Ende des Bereichs",
"extension_date": "Verl\u00e4ngerungsdatum"
}
}

View File

@ -1,258 +0,0 @@
{
"firefly": {
"Transfer": "\u039c\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac",
"Withdrawal": "\u0391\u03bd\u03ac\u03bb\u03b7\u03c8\u03b7",
"Deposit": "\u039a\u03b1\u03c4\u03ac\u03b8\u03b5\u03c3\u03b7",
"date_and_time": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03ba\u03b1\u03b9 \u03ce\u03c1\u03b1",
"no_currency": "(\u03c7\u03c9\u03c1\u03af\u03c2 \u03bd\u03cc\u03bc\u03b9\u03c3\u03bc\u03b1)",
"date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1",
"time": "\u038f\u03c1\u03b1",
"no_budget": "(\u03c7\u03c9\u03c1\u03af\u03c2 \u03c0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03cc)",
"destination_account": "\u039b\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03c0\u03c1\u03bf\u03bf\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd",
"source_account": "\u039b\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03c0\u03c1\u03bf\u03ad\u03bb\u03b5\u03c5\u03c3\u03b7\u03c2",
"single_split": "\u0394\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2",
"create_new_transaction": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bc\u03b9\u03b1\u03c2 \u03bd\u03ad\u03b1\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2",
"balance": "\u0399\u03c3\u03bf\u03b6\u03cd\u03b3\u03b9\u03bf",
"transaction_journal_extra": "\u03a0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2",
"transaction_journal_meta": "\u03a0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03bc\u03b5\u03c4\u03b1\u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd",
"basic_journal_information": "\u0392\u03b1\u03c3\u03b9\u03ba\u03ad\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2",
"bills_to_pay": "\u03a0\u03ac\u03b3\u03b9\u03b1 \u03ad\u03be\u03bf\u03b4\u03b1 \u03c0\u03c1\u03bf\u03c2 \u03c0\u03bb\u03b7\u03c1\u03c9\u03bc\u03ae",
"left_to_spend": "\u0394\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b1 \u03c0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03ce\u03bd",
"attachments": "\u03a3\u03c5\u03bd\u03b7\u03bc\u03bc\u03ad\u03bd\u03b1",
"net_worth": "\u039a\u03b1\u03b8\u03b1\u03c1\u03ae \u03b1\u03be\u03af\u03b1",
"bill": "\u03a0\u03ac\u03b3\u03b9\u03bf \u03ad\u03be\u03bf\u03b4\u03bf",
"no_bill": "(\u03c7\u03c9\u03c1\u03af\u03c2 \u03c0\u03ac\u03b3\u03b9\u03bf \u03ad\u03be\u03bf\u03b4\u03bf)",
"tags": "\u0395\u03c4\u03b9\u03ba\u03ad\u03c4\u03b5\u03c2",
"internal_reference": "\u0395\u03c3\u03c9\u03c4\u03b5\u03c1\u03b9\u03ba\u03ae \u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac",
"external_url": "\u0395\u03be\u03c9\u03c4\u03b5\u03c1\u03b9\u03ba\u03cc URL",
"no_piggy_bank": "(\u03c7\u03c9\u03c1\u03af\u03c2 \u03ba\u03bf\u03c5\u03bc\u03c0\u03b1\u03c1\u03ac)",
"paid": "\u03a0\u03bb\u03b7\u03c1\u03c9\u03bc\u03ad\u03bd\u03bf",
"notes": "\u03a3\u03b7\u03bc\u03b5\u03b9\u03ce\u03c3\u03b5\u03b9\u03c2",
"yourAccounts": "\u039f\u03b9 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03af \u03c3\u03b1\u03c2",
"go_to_asset_accounts": "\u0394\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd\u03c2 \u03ba\u03b5\u03c6\u03b1\u03bb\u03b1\u03af\u03bf\u03c5 \u03c3\u03b1\u03c2",
"delete_account": "\u0394\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd",
"transaction_table_description": "\u0388\u03bd\u03b1\u03c2 \u03c0\u03af\u03bd\u03b1\u03ba\u03b1\u03c2 \u03bc\u03b5 \u03c4\u03b9\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 \u03c3\u03b1\u03c2",
"account": "\u039b\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2",
"description": "\u03a0\u03b5\u03c1\u03b9\u03b3\u03c1\u03b1\u03c6\u03ae",
"amount": "\u03a0\u03bf\u03c3\u03cc",
"budget": "\u03a0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03cc\u03c2",
"category": "\u039a\u03b1\u03c4\u03b7\u03b3\u03bf\u03c1\u03af\u03b1",
"opposing_account": "\u0388\u03bd\u03b1\u03bd\u03c4\u03b9 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2",
"budgets": "\u03a0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03bf\u03af",
"categories": "\u039a\u03b1\u03c4\u03b7\u03b3\u03bf\u03c1\u03af\u03b5\u03c2",
"go_to_budgets": "\u03a0\u03b7\u03b3\u03b1\u03af\u03bd\u03b5\u03c4\u03b5 \u03c3\u03c4\u03bf\u03c5\u03c2 \u03c0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03bf\u03cd\u03c2 \u03c3\u03b1\u03c2",
"income": "\u0388\u03c3\u03bf\u03b4\u03b1",
"go_to_deposits": "\u03a0\u03b7\u03b3\u03b1\u03af\u03bd\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \u03ba\u03b1\u03c4\u03b1\u03b8\u03ad\u03c3\u03b5\u03b9\u03c2",
"go_to_categories": "\u03a0\u03b7\u03b3\u03b1\u03af\u03bd\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \u03ba\u03b1\u03c4\u03b7\u03b3\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03b1\u03c2",
"expense_accounts": "\u0394\u03b1\u03c0\u03ac\u03bd\u03b5\u03c2",
"go_to_expenses": "\u03a0\u03b7\u03b3\u03b1\u03af\u03bd\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \u03b4\u03b1\u03c0\u03ac\u03bd\u03b5\u03c2",
"go_to_bills": "\u03a0\u03b7\u03b3\u03b1\u03af\u03bd\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b1 \u03c0\u03ac\u03b3\u03b9\u03b1 \u03ad\u03be\u03bf\u03b4\u03b1",
"bills": "\u03a0\u03ac\u03b3\u03b9\u03b1 \u03ad\u03be\u03bf\u03b4\u03b1",
"last_thirty_days": "\u03a4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03b5\u03c2 \u03c4\u03c1\u03b9\u03ac\u03bd\u03c4\u03b1 \u03b7\u03bc\u03ad\u03c1\u03b5\u03c2",
"last_seven_days": "\u03a4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03b5\u03c2 \u03b5\u03c0\u03c4\u03ac \u03b7\u03bc\u03ad\u03c1\u03b5\u03c2",
"go_to_piggies": "\u03a0\u03b7\u03b3\u03b1\u03af\u03bd\u03b5\u03c4\u03b5 \u03c3\u03c4\u03bf\u03c5\u03c2 \u03ba\u03bf\u03c5\u03bc\u03c0\u03b1\u03c1\u03ac\u03b4\u03b5\u03c2 \u03c3\u03b1\u03c2",
"saved": "\u0391\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03cd\u03c4\u03b7\u03ba\u03b5",
"piggy_banks": "\u039a\u03bf\u03c5\u03bc\u03c0\u03b1\u03c1\u03ac\u03b4\u03b5\u03c2",
"piggy_bank": "\u039a\u03bf\u03c5\u03bc\u03c0\u03b1\u03c1\u03ac\u03c2",
"amounts": "\u03a0\u03bf\u03c3\u03ac",
"left": "\u0391\u03c0\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5\u03bd",
"spent": "\u0394\u03b1\u03c0\u03b1\u03bd\u03ae\u03b8\u03b7\u03ba\u03b1\u03bd",
"Default asset account": "\u0392\u03b1\u03c3\u03b9\u03ba\u03cc\u03c2 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ba\u03b5\u03c6\u03b1\u03bb\u03b1\u03af\u03bf\u03c5",
"search_results": "\u0391\u03c0\u03bf\u03c4\u03b5\u03bb\u03ad\u03c3\u03bc\u03b1\u03c4\u03b1 \u03b1\u03bd\u03b1\u03b6\u03ae\u03c4\u03b7\u03c3\u03b7\u03c2",
"include": "\u03a0\u03b5\u03c1\u03b9\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9;",
"transaction": "\u03a3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae",
"account_role_defaultAsset": "\u0392\u03b1\u03c3\u03b9\u03ba\u03cc\u03c2 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ba\u03b5\u03c6\u03b1\u03bb\u03b1\u03af\u03bf\u03c5",
"account_role_savingAsset": "\u039b\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03b1\u03c0\u03bf\u03c4\u03b1\u03bc\u03af\u03b5\u03c5\u03c3\u03b7\u03c2",
"account_role_sharedAsset": "\u039a\u03bf\u03b9\u03bd\u03cc\u03c2 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ba\u03b5\u03c6\u03b1\u03bb\u03b1\u03af\u03bf\u03c5",
"clear_location": "\u0395\u03ba\u03ba\u03b1\u03b8\u03ac\u03c1\u03b9\u03c3\u03b7 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1\u03c2",
"account_role_ccAsset": "\u03a0\u03b9\u03c3\u03c4\u03c9\u03c4\u03b9\u03ba\u03ae \u03ba\u03ac\u03c1\u03c4\u03b1",
"account_role_cashWalletAsset": "\u03a0\u03bf\u03c1\u03c4\u03bf\u03c6\u03cc\u03bb\u03b9 \u03bc\u03b5\u03c4\u03c1\u03b7\u03c4\u03ce\u03bd",
"daily_budgets": "\u0397\u03bc\u03b5\u03c1\u03ae\u03c3\u03b9\u03bf\u03b9 \u03c0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03bf\u03af",
"weekly_budgets": "\u0395\u03b2\u03b4\u03bf\u03bc\u03b1\u03b4\u03b9\u03b1\u03af\u03bf\u03b9 \u03c0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03bf\u03af",
"monthly_budgets": "\u039c\u03b7\u03bd\u03b9\u03b1\u03af\u03bf\u03b9 \u03c0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03bf\u03af",
"journals_in_period_for_account_js": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc {title} \u03bc\u03b5\u03c4\u03b1\u03be\u03cd {start} \u03ba\u03b1\u03b9 {end}",
"quarterly_budgets": "\u03a4\u03c1\u03b9\u03bc\u03b7\u03bd\u03b9\u03b1\u03af\u03bf\u03b9 \u03c0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03bf\u03af",
"create_new_expense": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bd\u03ad\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03b4\u03b1\u03c0\u03b1\u03bd\u03ce\u03bd",
"create_new_revenue": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bd\u03ad\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03b5\u03c3\u03cc\u03b4\u03c9\u03bd",
"create_new_liabilities": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bd\u03ad\u03b1\u03c2 \u03c5\u03c0\u03bf\u03c7\u03c1\u03ad\u03c9\u03c3\u03b7\u03c2",
"half_year_budgets": "\u0395\u03be\u03b1\u03bc\u03b7\u03bd\u03b9\u03b1\u03af\u03bf\u03b9 \u03c0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03bf\u03af",
"yearly_budgets": "\u0395\u03c4\u03ae\u03c3\u03b9\u03bf\u03b9 \u03c0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03bf\u03af",
"split_transaction_title": "\u03a0\u03b5\u03c1\u03b9\u03b3\u03c1\u03b1\u03c6\u03ae \u03c4\u03b7\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2 \u03bc\u03b5 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03cc",
"errors_submission": "\u03a5\u03c0\u03ae\u03c1\u03be\u03b5 \u03ba\u03ac\u03c0\u03bf\u03b9\u03bf \u03bb\u03ac\u03b8\u03bf\u03c2 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03c5\u03c0\u03bf\u03b2\u03bf\u03bb\u03ae \u03c3\u03b1\u03c2. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03ce \u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b1 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1\u03c4\u03b1.",
"flash_error": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1!",
"store_transaction": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2",
"flash_success": "\u0395\u03c0\u03b9\u03c4\u03c5\u03c7\u03af\u03b1!",
"create_another": "\u039c\u03b5\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7, \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c8\u03c4\u03b5 \u03b5\u03b4\u03ce \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03b1\u03ba\u03cc\u03bc\u03b7 \u03ad\u03bd\u03b1.",
"update_transaction": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2",
"after_update_create_another": "\u039c\u03b5\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7, \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c8\u03c4\u03b5 \u03b5\u03b4\u03ce \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1.",
"transaction_updated_no_changes": "<a href=\"transactions\/show\/{ID}\">\u0397 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae #{ID}<\/a> (\"{title}\") \u03c0\u03b1\u03c1\u03ad\u03bc\u03b5\u03b9\u03bd\u03b5 \u03c7\u03c9\u03c1\u03af\u03c2 \u03ba\u03b1\u03bc\u03af\u03b1 \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">\u0397 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae #{ID}<\/a> (\"{title}\") \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03c9\u03b8\u03b5\u03af.",
"spent_x_of_y": "\u0394\u03b1\u03c0\u03b1\u03bd\u03ae\u03b8\u03b7\u03ba\u03b1\u03bd {amount} \u03b1\u03c0\u03cc {total}",
"search": "\u0391\u03bd\u03b1\u03b6\u03ae\u03c4\u03b7\u03c3\u03b7",
"create_new_asset": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bd\u03ad\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03ba\u03b5\u03c6\u03b1\u03bb\u03b1\u03af\u03bf\u03c5",
"asset_accounts": "\u039a\u03b5\u03c6\u03ac\u03bb\u03b1\u03b9\u03b1",
"reset_after": "\u0395\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac \u03c6\u03cc\u03c1\u03bc\u03b1\u03c2 \u03bc\u03b5\u03c4\u03ac \u03c4\u03b7\u03bd \u03c5\u03c0\u03bf\u03b2\u03bf\u03bb\u03ae",
"bill_paid_on": "\u03a0\u03bb\u03b7\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03b9\u03c2 {date}",
"first_split_decides": "\u039f \u03c0\u03c1\u03ce\u03c4\u03bf\u03c2 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03ba\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03c4\u03b7\u03bd \u03c4\u03b9\u03bc\u03ae \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03c0\u03b5\u03b4\u03af\u03bf\u03c5",
"first_split_overrules_source": "\u039f \u03c0\u03c1\u03ce\u03c4\u03bf\u03c2 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b5\u03bd\u03b4\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03bc\u03c8\u03b5\u03b9 \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c0\u03c1\u03bf\u03ad\u03bb\u03b5\u03c5\u03c3\u03b7\u03c2",
"first_split_overrules_destination": "\u039f \u03c0\u03c1\u03ce\u03c4\u03bf\u03c2 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b5\u03bd\u03b4\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03bc\u03c8\u03b5\u03b9 \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c0\u03c1\u03bf\u03bf\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">\u0397 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae #{ID} (\"{title}\")<\/a> \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03c5\u03c4\u03b5\u03af.",
"custom_period": "\u03a0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03c3\u03bc\u03ad\u03bd\u03b7 \u03c0\u03b5\u03c1\u03af\u03bf\u03b4\u03bf\u03c2",
"reset_to_current": "\u0395\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac \u03c3\u03c4\u03b7\u03bd \u03c4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c0\u03b5\u03c1\u03af\u03bf\u03b4\u03bf",
"select_period": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03b5\u03c1\u03af\u03bf\u03b4\u03bf",
"location": "\u03a4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1",
"other_budgets": "\u03a0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03bf\u03af \u03bc\u03b5 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ae \u03c0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae",
"journal_links": "\u03a3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03b9\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ce\u03bd",
"go_to_withdrawals": "\u03a0\u03b7\u03b3\u03b1\u03af\u03bd\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \u03b1\u03bd\u03b1\u03bb\u03ae\u03c8\u03b5\u03b9\u03c2 \u03c3\u03b1\u03c2",
"revenue_accounts": "\u0388\u03c3\u03bf\u03b4\u03b1",
"add_another_split": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03b5\u03bd\u03cc\u03c2 \u03b1\u03ba\u03cc\u03bc\u03b1 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd",
"actions": "\u0395\u03bd\u03ad\u03c1\u03b3\u03b5\u03b9\u03b5\u03c2",
"earned": "\u039a\u03b5\u03c1\u03b4\u03ae\u03b8\u03b7\u03ba\u03b1\u03bd",
"empty": "(\u03ba\u03b5\u03bd\u03cc)",
"edit": "\u0395\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1",
"never": "\u03a0\u03bf\u03c4\u03ad",
"account_type_Loan": "\u0394\u03ac\u03bd\u03b5\u03b9\u03bf",
"account_type_Mortgage": "\u03a5\u03c0\u03bf\u03b8\u03ae\u03ba\u03b7",
"stored_new_account_js": "\u039f \u03bd\u03ad\u03bf\u03c2 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \"<a href=\"accounts\/show\/{ID}\">{name}<\/a>\" \u03b1\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03cd\u03c4\u03b7\u03ba\u03b5!",
"account_type_Debt": "\u03a7\u03c1\u03ad\u03bf\u03c2",
"liability_direction_null_short": "\u0386\u03b3\u03bd\u03c9\u03c3\u03c4\u03bf",
"delete": "\u0394\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae",
"store_new_asset_account": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7 \u03bd\u03ad\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03ba\u03b5\u03c6\u03b1\u03bb\u03b1\u03af\u03bf\u03c5",
"store_new_expense_account": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7 \u03bd\u03ad\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03b4\u03b1\u03c0\u03b1\u03bd\u03ce\u03bd",
"store_new_liabilities_account": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7 \u03bd\u03ad\u03b1\u03c2 \u03c5\u03c0\u03bf\u03c7\u03c1\u03ad\u03c9\u03c3\u03b7\u03c2",
"store_new_revenue_account": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7 \u03bd\u03ad\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03b5\u03c3\u03cc\u03b4\u03c9\u03bd",
"mandatoryFields": "\u03a5\u03c0\u03bf\u03c7\u03c1\u03b5\u03c9\u03c4\u03b9\u03ba\u03ac \u03c0\u03b5\u03b4\u03af\u03b1",
"optionalFields": "\u03a0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03ac \u03c0\u03b5\u03b4\u03af\u03b1",
"reconcile_this_account": "\u03a4\u03b1\u03ba\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd",
"interest_calc_weekly": "\u0391\u03bd\u03ac \u03b5\u03b2\u03b4\u03bf\u03bc\u03ac\u03b4\u03b1",
"interest_calc_monthly": "\u0391\u03bd\u03ac \u03bc\u03ae\u03bd\u03b1",
"interest_calc_quarterly": "\u0391\u03bd\u03ac \u03c4\u03c1\u03af\u03bc\u03b7\u03bd\u03bf",
"interest_calc_half-year": "\u0391\u03bd\u03ac \u03b5\u03be\u03ac\u03bc\u03b7\u03bd\u03bf",
"interest_calc_yearly": "\u0391\u03bd\u03ac \u03ad\u03c4\u03bf\u03c2",
"liability_direction_credit": "\u039c\u03bf\u03c5 \u03bf\u03c6\u03b5\u03af\u03bb\u03bf\u03c5\u03bd \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c7\u03c1\u03ad\u03bf\u03c2 \u03c3\u03b5 \u03bc\u03ad\u03bd\u03b1",
"liability_direction_debit": "\u039f\u03c6\u03b5\u03af\u03bb\u03c9 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c7\u03c1\u03ad\u03bf\u03c2 \u03c3\u03b5 \u03ba\u03ac\u03c0\u03bf\u03b9\u03bf\u03bd \u03ac\u03bb\u03bb\u03bf",
"liability_direction_credit_short": "\u039c\u03bf\u03c5 \u03bf\u03c6\u03b5\u03af\u03bb\u03bf\u03c5\u03bd \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c7\u03c1\u03ad\u03bf\u03c2",
"liability_direction_debit_short": "\u039f\u03c6\u03b5\u03af\u03bb\u03c9 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c7\u03c1\u03ad\u03bf\u03c2",
"account_type_debt": "\u03a7\u03c1\u03ad\u03bf\u03c2",
"account_type_loan": "\u0394\u03ac\u03bd\u03b5\u03b9\u03bf",
"left_in_debt": "\u039f\u03c6\u03b5\u03b9\u03bb\u03cc\u03bc\u03b5\u03bd\u03bf \u03c0\u03bf\u03c3\u03cc",
"account_type_mortgage": "\u03a5\u03c0\u03bf\u03b8\u03ae\u03ba\u03b7",
"save_transactions_by_moving_js": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03bd \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2|\u0391\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03cd\u03c3\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae \u03bc\u03b5\u03c4\u03b1\u03ba\u03b9\u03bd\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b7\u03bd \u03c3\u03b5 \u03ac\u03bb\u03bb\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc|\u0391\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03cd\u03c3\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ad\u03c2 \u03c4\u03b9\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 \u03bc\u03b5\u03c4\u03b1\u03ba\u03b9\u03bd\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b9\u03c2 \u03c3\u03b5 \u03ac\u03bb\u03bb\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc.",
"none_in_select_list": "(\u03c4\u03af\u03c0\u03bf\u03c4\u03b1)",
"transaction_expand_split": "\u0391\u03bd\u03ac\u03c0\u03c4\u03c5\u03be\u03b7 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd",
"transaction_collapse_split": "\u03a3\u03cd\u03bc\u03c0\u03c4\u03c5\u03be\u03b7 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd",
"default_group_title_name": "(\u03c7\u03c9\u03c1\u03af\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1)",
"bill_repeats_weekly": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03b5\u03b2\u03b4\u03bf\u03bc\u03b1\u03b4\u03b9\u03b1\u03af\u03c9\u03c2",
"bill_repeats_monthly": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03bc\u03b7\u03bd\u03b9\u03b1\u03af\u03c9\u03c2",
"bill_repeats_quarterly": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03b1\u03bd\u03ac \u03c4\u03c1\u03af\u03bc\u03b7\u03bd\u03bf",
"bill_repeats_half-year": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 \u03bc\u03b9\u03c3\u03cc \u03c7\u03c1\u03cc\u03bd\u03bf",
"bill_repeats_yearly": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c4\u03b7\u03c3\u03af\u03c9\u03c2",
"bill_repeats_weekly_other": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 \u03b4\u03b5\u03cd\u03c4\u03b5\u03c1\u03b7 \u03b5\u03b2\u03b4\u03bf\u03bc\u03ac\u03b4\u03b1",
"bill_repeats_monthly_other": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 \u03b4\u03b5\u03cd\u03c4\u03b5\u03c1\u03bf \u03bc\u03ae\u03bd\u03b1",
"bill_repeats_quarterly_other": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 \u03b4\u03b5\u03cd\u03c4\u03b5\u03c1\u03bf \u03c4\u03c1\u03af\u03bc\u03b7\u03bd\u03bf",
"bill_repeats_half-year_other": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03b5\u03c4\u03b7\u03c3\u03af\u03c9\u03c2",
"bill_repeats_yearly_other": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 \u03b4\u03b5\u03cd\u03c4\u03b5\u03c1\u03bf \u03c7\u03c1\u03cc\u03bd\u03bf",
"bill_repeats_weekly_skip": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 {skip} \u03b5\u03b2\u03b4\u03bf\u03bc\u03ac\u03b4\u03b5\u03c2",
"bill_repeats_monthly_skip": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 {skip} \u03bc\u03ae\u03bd\u03b5\u03c2",
"bill_repeats_quarterly_skip": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 {skip} \u03c4\u03c1\u03af\u03bc\u03b7\u03bd\u03b1",
"bill_repeats_half-year_skip": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 {skip} \u03b5\u03be\u03ac\u03bc\u03b7\u03bd\u03b1",
"bill_repeats_yearly_skip": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 {skip} \u03ad\u03c4\u03b7",
"not_expected_period": "\u0394\u03b5\u03bd \u03b1\u03bd\u03b1\u03bc\u03ad\u03bd\u03b5\u03c4\u03b1\u03b9 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03c0\u03b5\u03c1\u03af\u03bf\u03b4\u03bf",
"subscriptions": "\u03a3\u03c5\u03bd\u03b4\u03c1\u03bf\u03bc\u03ad\u03c2",
"bill_expected_date_js": "\u0391\u03bd\u03b1\u03bc\u03ad\u03bd\u03b5\u03c4\u03b1\u03b9 {date}",
"inactive": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc",
"forever": "\u0393\u03b9\u03b1 \u03c0\u03ac\u03bd\u03c4\u03b1",
"extension_date_is": "\u0397 \u03b7\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03c0\u03b1\u03c1\u03ac\u03c4\u03b1\u03c3\u03b7\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 {date}",
"create_new_bill": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bd\u03ad\u03bf\u03c5 \u03c0\u03ac\u03b3\u03b9\u03bf\u03c5 \u03ad\u03be\u03bf\u03b4\u03bf\u03c5",
"store_new_bill": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7 \u03bd\u03ad\u03bf\u03c5 \u03c0\u03ac\u03b3\u03b9\u03bf\u03c5 \u03ad\u03be\u03bf\u03b4\u03bf\u03c5",
"repeat_freq_yearly": "\u03b5\u03c4\u03b7\u03c3\u03af\u03c9\u03c2",
"repeat_freq_half-year": "\u03b5\u03be\u03b1\u03bc\u03b7\u03bd\u03b9\u03b1\u03af\u03c9\u03c2",
"repeat_freq_quarterly": "\u03c4\u03c1\u03b9\u03bc\u03b7\u03bd\u03b9\u03b1\u03af\u03c9\u03c2",
"repeat_freq_monthly": "\u03bc\u03b7\u03bd\u03b9\u03b1\u03af\u03c9\u03c2",
"repeat_freq_weekly": "\u03b5\u03b2\u03b4\u03bf\u03bc\u03b1\u03b4\u03b9\u03b1\u03af\u03c9\u03c2",
"credit_card_type_monthlyFull": "\u0395\u03be\u03cc\u03c6\u03bb\u03b7\u03c3\u03b7 \u03ba\u03ac\u03b8\u03b5 \u03bc\u03ae\u03bd\u03b1",
"update_liabilities_account": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03c5\u03c0\u03bf\u03c7\u03c1\u03ad\u03c9\u03c3\u03b7\u03c2",
"update_expense_account": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03b4\u03b1\u03c0\u03b1\u03bd\u03ce\u03bd",
"update_revenue_account": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03b5\u03c3\u03cc\u03b4\u03c9\u03bd",
"update_undefined_account": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd",
"update_asset_account": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03ba\u03b5\u03c6\u03b1\u03bb\u03b1\u03af\u03bf\u03c5",
"updated_account_js": "\u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5 \u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
},
"list": {
"piggy_bank": "\u039a\u03bf\u03c5\u03bc\u03c0\u03b1\u03c1\u03ac\u03c2",
"percentage": "pct.",
"amount": "\u03a0\u03bf\u03c3\u03cc",
"lastActivity": "\u03a4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03b1 \u03b4\u03c1\u03b1\u03c3\u03c4\u03b7\u03c1\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1",
"name": "\u038c\u03bd\u03bf\u03bc\u03b1",
"role": "\u03a1\u03cc\u03bb\u03bf\u03c2",
"description": "\u03a0\u03b5\u03c1\u03b9\u03b3\u03c1\u03b1\u03c6\u03ae",
"date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1",
"source_account": "\u039b\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03c0\u03c1\u03bf\u03ad\u03bb\u03b5\u03c5\u03c3\u03b7\u03c2",
"destination_account": "\u039b\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03c0\u03c1\u03bf\u03bf\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd",
"category": "\u039a\u03b1\u03c4\u03b7\u03b3\u03bf\u03c1\u03af\u03b1",
"iban": "IBAN",
"interest": "\u03a4\u03cc\u03ba\u03bf\u03c2",
"interest_period": "\u03a4\u03bf\u03ba\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b7 \u03c0\u03b5\u03c1\u03af\u03bf\u03b4\u03bf\u03c2",
"liability_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c5\u03c0\u03bf\u03c7\u03c1\u03ad\u03c9\u03c3\u03b7\u03c2",
"liability_direction": "\u03a5\u03c0\u03bf\u03c7\u03c1\u03ad\u03c9\u03c3\u03b7 \u03b5\u03bd\u03c4\u03cc\u03c2\/\u03b5\u03ba\u03c4\u03cc\u03c2",
"currentBalance": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03c5\u03c0\u03cc\u03bb\u03bf\u03b9\u03c0\u03bf",
"next_expected_match": "\u0395\u03c0\u03cc\u03bc\u03b5\u03bd\u03b7 \u03b1\u03bd\u03b1\u03bc\u03b5\u03bd\u03cc\u03bc\u03b5\u03bd\u03b7 \u03b1\u03bd\u03c4\u03b9\u03c3\u03c4\u03bf\u03af\u03c7\u03b9\u03c3\u03b7",
"expected_info": "\u0395\u03c0\u03cc\u03bc\u03b5\u03bd\u03b7 \u03b1\u03bd\u03b1\u03bc\u03b5\u03bd\u03cc\u03bc\u03b5\u03bd\u03b7 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae",
"start_date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03ad\u03bd\u03b1\u03c1\u03be\u03b7\u03c2",
"end_date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03bb\u03ae\u03be\u03b7\u03c2",
"payment_info": "\u03a0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03a0\u03bb\u03b7\u03c1\u03c9\u03bc\u03ae\u03c2"
},
"config": {
"html_language": "el",
"week_in_year_fns": "'\u0395\u03b2\u03b4\u03bf\u03bc\u03ac\u03b4\u03b1' w, yyyy",
"month_and_day_fns": "d MMMM y",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "\u03a0\u03bf\u03c3\u03cc \u03c3\u03b5 \u03be\u03ad\u03bd\u03bf \u03bd\u03cc\u03bc\u03b9\u03c3\u03bc\u03b1",
"interest_date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03c4\u03bf\u03ba\u03b9\u03c3\u03bc\u03bf\u03cd",
"name": "\u038c\u03bd\u03bf\u03bc\u03b1",
"amount": "\u03a0\u03bf\u03c3\u03cc",
"iban": "IBAN",
"BIC": "BIC",
"notes": "\u03a3\u03b7\u03bc\u03b5\u03b9\u03ce\u03c3\u03b5\u03b9\u03c2",
"location": "\u03a4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1",
"repeat_freq": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03ae\u03c8\u03b5\u03b9\u03c2",
"skip": "\u03a0\u03b1\u03c1\u03ac\u03bb\u03b5\u03b9\u03c8\u03b7",
"startdate": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u0388\u03bd\u03b1\u03c1\u03be\u03b7\u03c2",
"enddate": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03bb\u03ae\u03be\u03b7\u03c2",
"object_group": "\u039f\u03bc\u03ac\u03b4\u03b1",
"attachments": "\u03a3\u03c5\u03bd\u03b7\u03bc\u03bc\u03ad\u03bd\u03b1",
"deletePermanently": "\u039f\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03ae \u03b4\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae",
"active": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc",
"include_net_worth": "\u0395\u03bd\u03c4\u03cc\u03c2 \u03ba\u03b1\u03b8\u03b1\u03c1\u03ae\u03c2 \u03b1\u03be\u03af\u03b1\u03c2",
"cc_type": "\u03a3\u03c7\u03ad\u03b4\u03b9\u03bf \u03c0\u03bb\u03b7\u03c1\u03c9\u03bc\u03ae\u03c2 \u03c0\u03b9\u03c3\u03c4\u03c9\u03c4\u03b9\u03ba\u03ae\u03c2 \u03ba\u03ac\u03c1\u03c4\u03b1\u03c2",
"account_number": "\u0391\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd",
"cc_monthly_payment_date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03bc\u03b7\u03bd\u03b9\u03b1\u03af\u03b1\u03c2 \u03c0\u03bb\u03b7\u03c1\u03c9\u03bc\u03ae\u03c2 \u03ba\u03ac\u03c1\u03c4\u03b1\u03c2",
"virtual_balance": "\u0395\u03b9\u03ba\u03bf\u03bd\u03b9\u03ba\u03cc \u03c5\u03c0\u03cc\u03bb\u03bf\u03b9\u03c0\u03bf",
"opening_balance": "\u03a5\u03c0\u03cc\u03bb\u03bf\u03b9\u03c0\u03bf \u03ad\u03bd\u03b1\u03c1\u03be\u03b7\u03c2",
"opening_balance_date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03c5\u03c0\u03bf\u03bb\u03bf\u03af\u03c0\u03bf\u03c5 \u03ad\u03bd\u03b1\u03c1\u03be\u03b7\u03c2",
"date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1",
"interest": "\u03a4\u03cc\u03ba\u03bf\u03c2",
"interest_period": "\u03a4\u03bf\u03ba\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b7 \u03c0\u03b5\u03c1\u03af\u03bf\u03b4\u03bf\u03c2",
"currency_id": "\u039d\u03cc\u03bc\u03b9\u03c3\u03bc\u03b1",
"liability_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c5\u03c0\u03bf\u03c7\u03c1\u03ad\u03c9\u03c3\u03b7\u03c2",
"account_role": "\u03a1\u03cc\u03bb\u03bf\u03c2 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd",
"liability_direction": "\u03a5\u03c0\u03bf\u03c7\u03c1\u03ad\u03c9\u03c3\u03b7 \u03b5\u03bd\u03c4\u03cc\u03c2\/\u03b5\u03ba\u03c4\u03cc\u03c2",
"book_date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2",
"permDeleteWarning": "\u0397 \u03b4\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03c9\u03bd \u03b1\u03c0\u03cc \u03c4\u03bf Firefly III \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03cc\u03bd\u03b9\u03bc\u03b7 \u03ba\u03b1\u03b9 \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b1\u03bd\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af.",
"account_areYouSure_js": "\u0395\u03af\u03c3\u03c4\u03b5 \u03c3\u03af\u03b3\u03bf\u03c5\u03c1\u03bf\u03b9 \u03cc\u03c4\u03b9 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03ac\u03c8\u03b5\u03c4\u03b5 \u03c4\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03bc\u03b5 \u03cc\u03bd\u03bf\u03bc\u03b1 \"{name}\";",
"also_delete_piggyBanks_js": "\u03a7\u03c9\u03c1\u03af\u03c2 \u03ba\u03bf\u03c5\u03bc\u03c0\u03b1\u03c1\u03ac | \u039f \u03bc\u03cc\u03bd\u03bf\u03c2 \u03ba\u03bf\u03c5\u03bc\u03c0\u03b1\u03c1\u03ac\u03c2 \u03c0\u03bf\u03c5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03bf\u03c2 \u03c3\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03b8\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03b5\u03af \u03b5\u03c0\u03af\u03c3\u03b7\u03c2. | \u038c\u03bb\u03bf\u03b9 \u03bf\u03b9 \u03ba\u03bf\u03c5\u03bc\u03c0\u03b1\u03c1\u03ac\u03b4\u03b5\u03c2 {count} \u03c0\u03bf\u03c5 \u03c3\u03c5\u03bd\u03b4\u03ad\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03b8\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03bf\u03cd\u03bd \u03b5\u03c0\u03af\u03c3\u03b7\u03c2.",
"also_delete_transactions_js": "\u03a7\u03c9\u03c1\u03af\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 | \u0397 \u03bc\u03cc\u03bd\u03b7 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae \u03c0\u03bf\u03c5 \u03c3\u03c5\u03bd\u03b4\u03ad\u03b5\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03b8\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03b5\u03af \u03b5\u03c0\u03af\u03c3\u03b7\u03c2. | \u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 {count} \u03c0\u03bf\u03c5 \u03c3\u03c5\u03bd\u03b4\u03ad\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03b8\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03bf\u03cd\u03bd \u03b5\u03c0\u03af\u03c3\u03b7\u03c2.",
"process_date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03b5\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1\u03c2",
"due_date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03c0\u03c1\u03bf\u03b8\u03b5\u03c3\u03bc\u03af\u03b1\u03c2",
"payment_date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03c0\u03bb\u03b7\u03c1\u03c9\u03bc\u03ae\u03c2",
"invoice_date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7\u03c2",
"amount_min": "\u0395\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf \u03c0\u03bf\u03c3\u03cc",
"amount_max": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03bf \u03c0\u03bf\u03c3\u03cc",
"start_date": "\u0391\u03c1\u03c7\u03ae \u03c4\u03bf\u03c5 \u03b5\u03cd\u03c1\u03bf\u03c5\u03c2",
"end_date": "\u03a4\u03ad\u03bb\u03bf\u03c2 \u03c4\u03bf\u03c5 \u03b5\u03cd\u03c1\u03bf\u03c5\u03c2",
"extension_date": "\u0397\u03bc\u03b5\u03c1\u03bf\u03bc\u03b7\u03bd\u03af\u03b1 \u03b5\u03c0\u03ad\u03ba\u03c4\u03b1\u03c3\u03b7\u03c2"
}
}

View File

@ -1,258 +0,0 @@
{
"firefly": {
"Transfer": "Transfer",
"Withdrawal": "Withdrawal",
"Deposit": "Deposit",
"date_and_time": "Date and time",
"no_currency": "(no currency)",
"date": "Date",
"time": "Time",
"no_budget": "(no budget)",
"destination_account": "Destination account",
"source_account": "Source account",
"single_split": "Split",
"create_new_transaction": "Create a new transaction",
"balance": "Balance",
"transaction_journal_extra": "Extra information",
"transaction_journal_meta": "Meta information",
"basic_journal_information": "Basic transaction information",
"bills_to_pay": "Bills to pay",
"left_to_spend": "Left to spend",
"attachments": "Attachments",
"net_worth": "Net worth",
"bill": "Bill",
"no_bill": "(no bill)",
"tags": "Tags",
"internal_reference": "Internal reference",
"external_url": "External URL",
"no_piggy_bank": "(no piggy bank)",
"paid": "Paid",
"notes": "Notes",
"yourAccounts": "Your accounts",
"go_to_asset_accounts": "View your asset accounts",
"delete_account": "Delete account",
"transaction_table_description": "A table containing your transactions",
"account": "Account",
"description": "Description",
"amount": "Amount",
"budget": "Budget",
"category": "Category",
"opposing_account": "Opposing account",
"budgets": "Budgets",
"categories": "Categories",
"go_to_budgets": "Go to your budgets",
"income": "Revenue \/ income",
"go_to_deposits": "Go to deposits",
"go_to_categories": "Go to your categories",
"expense_accounts": "Expense accounts",
"go_to_expenses": "Go to expenses",
"go_to_bills": "Go to your bills",
"bills": "Bills",
"last_thirty_days": "Last thirty days",
"last_seven_days": "Last seven days",
"go_to_piggies": "Go to your piggy banks",
"saved": "Saved",
"piggy_banks": "Piggy banks",
"piggy_bank": "Piggy bank",
"amounts": "Amounts",
"left": "Left",
"spent": "Spent",
"Default asset account": "Default asset account",
"search_results": "Search results",
"include": "Include?",
"transaction": "Transaction",
"account_role_defaultAsset": "Default asset account",
"account_role_savingAsset": "Savings account",
"account_role_sharedAsset": "Shared asset account",
"clear_location": "Clear location",
"account_role_ccAsset": "Credit card",
"account_role_cashWalletAsset": "Cash wallet",
"daily_budgets": "Daily budgets",
"weekly_budgets": "Weekly budgets",
"monthly_budgets": "Monthly budgets",
"journals_in_period_for_account_js": "All transactions for account {title} between {start} and {end}",
"quarterly_budgets": "Quarterly budgets",
"create_new_expense": "Create new expense account",
"create_new_revenue": "Create new revenue account",
"create_new_liabilities": "Create new liability",
"half_year_budgets": "Half-yearly budgets",
"yearly_budgets": "Yearly budgets",
"split_transaction_title": "Description of the split transaction",
"errors_submission": "There was something wrong with your submission. Please check out the errors.",
"flash_error": "Error!",
"store_transaction": "Store transaction",
"flash_success": "Success!",
"create_another": "After storing, return here to create another one.",
"update_transaction": "Update transaction",
"after_update_create_another": "After updating, return here to continue editing.",
"transaction_updated_no_changes": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") did not receive any changes.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") has been updated.",
"spent_x_of_y": "Spent {amount} of {total}",
"search": "Search",
"create_new_asset": "Create new asset account",
"asset_accounts": "Asset accounts",
"reset_after": "Reset form after submission",
"bill_paid_on": "Paid on {date}",
"first_split_decides": "The first split determines the value of this field",
"first_split_overrules_source": "The first split may overrule the source account",
"first_split_overrules_destination": "The first split may overrule the destination account",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
"custom_period": "Custom period",
"reset_to_current": "Reset to current period",
"select_period": "Select a period",
"location": "Location",
"other_budgets": "Custom timed budgets",
"journal_links": "Transaction links",
"go_to_withdrawals": "Go to your withdrawals",
"revenue_accounts": "Revenue accounts",
"add_another_split": "Add another split",
"actions": "Actions",
"earned": "Earned",
"empty": "(empty)",
"edit": "Edit",
"never": "Never",
"account_type_Loan": "Loan",
"account_type_Mortgage": "Mortgage",
"stored_new_account_js": "New account \"<a href=\"accounts\/show\/{ID}\">{name}<\/a>\" stored!",
"account_type_Debt": "Debt",
"liability_direction_null_short": "Unknown",
"delete": "Delete",
"store_new_asset_account": "Store new asset account",
"store_new_expense_account": "Store new expense account",
"store_new_liabilities_account": "Store new liability",
"store_new_revenue_account": "Store new revenue account",
"mandatoryFields": "Mandatory fields",
"optionalFields": "Optional fields",
"reconcile_this_account": "Reconcile this account",
"interest_calc_weekly": "Per week",
"interest_calc_monthly": "Per month",
"interest_calc_quarterly": "Per quarter",
"interest_calc_half-year": "Per half year",
"interest_calc_yearly": "Per year",
"liability_direction_credit": "I am owed this debt",
"liability_direction_debit": "I owe this debt to somebody else",
"liability_direction_credit_short": "Owed this debt",
"liability_direction_debit_short": "Owe this debt",
"account_type_debt": "Debt",
"account_type_loan": "Loan",
"left_in_debt": "Amount due",
"account_type_mortgage": "Mortgage",
"save_transactions_by_moving_js": "No transactions|Save this transaction by moving it to another account. |Save these transactions by moving them to another account.",
"none_in_select_list": "(none)",
"transaction_expand_split": "Expand split",
"transaction_collapse_split": "Collapse split",
"default_group_title_name": "(ungrouped)",
"bill_repeats_weekly": "Repeats weekly",
"bill_repeats_monthly": "Repeats monthly",
"bill_repeats_quarterly": "Repeats quarterly",
"bill_repeats_half-year": "Repeats every half year",
"bill_repeats_yearly": "Repeats yearly",
"bill_repeats_weekly_other": "Repeats every other week",
"bill_repeats_monthly_other": "Repeats every other month",
"bill_repeats_quarterly_other": "Repeats every other quarter",
"bill_repeats_half-year_other": "Repeats yearly",
"bill_repeats_yearly_other": "Repeats every other year",
"bill_repeats_weekly_skip": "Repeats every {skip} weeks",
"bill_repeats_monthly_skip": "Repeats every {skip} months",
"bill_repeats_quarterly_skip": "Repeats every {skip} quarters",
"bill_repeats_half-year_skip": "Repeats every {skip} half years",
"bill_repeats_yearly_skip": "Repeats every {skip} years",
"not_expected_period": "Not expected this period",
"subscriptions": "Subscriptions",
"bill_expected_date_js": "Expected {date}",
"inactive": "Inactive",
"forever": "Forever",
"extension_date_is": "Extension date is {date}",
"create_new_bill": "Create new bill",
"store_new_bill": "Store new bill",
"repeat_freq_yearly": "yearly",
"repeat_freq_half-year": "every half-year",
"repeat_freq_quarterly": "quarterly",
"repeat_freq_monthly": "monthly",
"repeat_freq_weekly": "weekly",
"credit_card_type_monthlyFull": "Full payment every month",
"update_liabilities_account": "Update liability",
"update_expense_account": "Update expense account",
"update_revenue_account": "Update revenue account",
"update_undefined_account": "Update account",
"update_asset_account": "Update asset account",
"updated_account_js": "Updated account \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
},
"list": {
"piggy_bank": "Piggy bank",
"percentage": "pct.",
"amount": "Amount",
"lastActivity": "Last activity",
"name": "Name",
"role": "Role",
"description": "Description",
"date": "Date",
"source_account": "Source account",
"destination_account": "Destination account",
"category": "Category",
"iban": "IBAN",
"interest": "Interest",
"interest_period": "Interest period",
"liability_type": "Type of liability",
"liability_direction": "Liability in\/out",
"currentBalance": "Current balance",
"next_expected_match": "Next expected match",
"expected_info": "Next expected transaction",
"start_date": "Start date",
"end_date": "End date",
"payment_info": "Payment information"
},
"config": {
"html_language": "en-gb",
"week_in_year_fns": "'Week' w, yyyy",
"month_and_day_fns": "MMMM d, y",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Foreign amount",
"interest_date": "Interest date",
"name": "Name",
"amount": "Amount",
"iban": "IBAN",
"BIC": "BIC",
"notes": "Notes",
"location": "Location",
"repeat_freq": "Repeats",
"skip": "Skip",
"startdate": "Start date",
"enddate": "End date",
"object_group": "Group",
"attachments": "Attachments",
"deletePermanently": "Delete permanently",
"active": "Active",
"include_net_worth": "Include in net worth",
"cc_type": "Credit card payment plan",
"account_number": "Account number",
"cc_monthly_payment_date": "Credit card monthly payment date",
"virtual_balance": "Virtual balance",
"opening_balance": "Opening balance",
"opening_balance_date": "Opening balance date",
"date": "Date",
"interest": "Interest",
"interest_period": "Interest period",
"currency_id": "Currency",
"liability_type": "Liability type",
"account_role": "Account role",
"liability_direction": "Liability in\/out",
"book_date": "Book date",
"permDeleteWarning": "Deleting stuff from Firefly III is permanent and cannot be undone.",
"account_areYouSure_js": "Are you sure you want to delete the account named \"{name}\"?",
"also_delete_piggyBanks_js": "No piggy banks|The only piggy bank connected to this account will be deleted as well.|All {count} piggy banks connected to this account will be deleted as well.",
"also_delete_transactions_js": "No transactions|The only transaction connected to this account will be deleted as well.|All {count} transactions connected to this account will be deleted as well.",
"process_date": "Processing date",
"due_date": "Due date",
"payment_date": "Payment date",
"invoice_date": "Invoice date",
"amount_min": "Minimum amount",
"amount_max": "Maximum amount",
"start_date": "Start of range",
"end_date": "End of range",
"extension_date": "Extension date"
}
}

View File

@ -1,258 +0,0 @@
{
"firefly": {
"Transfer": "Transfer",
"Withdrawal": "Withdrawal",
"Deposit": "Deposit",
"date_and_time": "Date and time",
"no_currency": "(no currency)",
"date": "Date",
"time": "Time",
"no_budget": "(no budget)",
"destination_account": "Destination account",
"source_account": "Source account",
"single_split": "Split",
"create_new_transaction": "Create a new transaction",
"balance": "Balance",
"transaction_journal_extra": "Extra information",
"transaction_journal_meta": "Meta information",
"basic_journal_information": "Basic transaction information",
"bills_to_pay": "Bills to pay",
"left_to_spend": "Left to spend",
"attachments": "Attachments",
"net_worth": "Net worth",
"bill": "Bill",
"no_bill": "(no bill)",
"tags": "Tags",
"internal_reference": "Internal reference",
"external_url": "External URL",
"no_piggy_bank": "(no piggy bank)",
"paid": "Paid",
"notes": "Notes",
"yourAccounts": "Your accounts",
"go_to_asset_accounts": "View your asset accounts",
"delete_account": "Delete account",
"transaction_table_description": "A table containing your transactions",
"account": "Account",
"description": "Description",
"amount": "Amount",
"budget": "Budget",
"category": "Category",
"opposing_account": "Opposing account",
"budgets": "Budgets",
"categories": "Categories",
"go_to_budgets": "Go to your budgets",
"income": "Revenue \/ income",
"go_to_deposits": "Go to deposits",
"go_to_categories": "Go to your categories",
"expense_accounts": "Expense accounts",
"go_to_expenses": "Go to expenses",
"go_to_bills": "Go to your bills",
"bills": "Bills",
"last_thirty_days": "Last thirty days",
"last_seven_days": "Last seven days",
"go_to_piggies": "Go to your piggy banks",
"saved": "Saved",
"piggy_banks": "Piggy banks",
"piggy_bank": "Piggy bank",
"amounts": "Amounts",
"left": "Left",
"spent": "Spent",
"Default asset account": "Default asset account",
"search_results": "Search results",
"include": "Include?",
"transaction": "Transaction",
"account_role_defaultAsset": "Default asset account",
"account_role_savingAsset": "Savings account",
"account_role_sharedAsset": "Shared asset account",
"clear_location": "Clear location",
"account_role_ccAsset": "Credit card",
"account_role_cashWalletAsset": "Cash wallet",
"daily_budgets": "Daily budgets",
"weekly_budgets": "Weekly budgets",
"monthly_budgets": "Monthly budgets",
"journals_in_period_for_account_js": "All transactions for account {title} between {start} and {end}",
"quarterly_budgets": "Quarterly budgets",
"create_new_expense": "Create new expense account",
"create_new_revenue": "Create new revenue account",
"create_new_liabilities": "Create new liability",
"half_year_budgets": "Half-yearly budgets",
"yearly_budgets": "Yearly budgets",
"split_transaction_title": "Description of the split transaction",
"errors_submission": "There was something wrong with your submission. Please check out the errors.",
"flash_error": "Error!",
"store_transaction": "Store transaction",
"flash_success": "Success!",
"create_another": "After storing, return here to create another one.",
"update_transaction": "Update transaction",
"after_update_create_another": "After updating, return here to continue editing.",
"transaction_updated_no_changes": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") did not receive any changes.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") has been updated.",
"spent_x_of_y": "Spent {amount} of {total}",
"search": "Search",
"create_new_asset": "Create new asset account",
"asset_accounts": "Asset accounts",
"reset_after": "Reset form after submission",
"bill_paid_on": "Paid on {date}",
"first_split_decides": "The first split determines the value of this field",
"first_split_overrules_source": "The first split may overrule the source account",
"first_split_overrules_destination": "The first split may overrule the destination account",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
"custom_period": "Custom period",
"reset_to_current": "Reset to current period",
"select_period": "Select a period",
"location": "Location",
"other_budgets": "Custom timed budgets",
"journal_links": "Transaction links",
"go_to_withdrawals": "Go to your withdrawals",
"revenue_accounts": "Revenue accounts",
"add_another_split": "Add another split",
"actions": "Actions",
"earned": "Earned",
"empty": "(empty)",
"edit": "Edit",
"never": "Never",
"account_type_Loan": "Loan",
"account_type_Mortgage": "Mortgage",
"stored_new_account_js": "New account \"<a href=\"accounts\/show\/{ID}\">{name}<\/a>\" stored!",
"account_type_Debt": "Debt",
"liability_direction_null_short": "Unknown",
"delete": "Delete",
"store_new_asset_account": "Store new asset account",
"store_new_expense_account": "Store new expense account",
"store_new_liabilities_account": "Store new liability",
"store_new_revenue_account": "Store new revenue account",
"mandatoryFields": "Mandatory fields",
"optionalFields": "Optional fields",
"reconcile_this_account": "Reconcile this account",
"interest_calc_weekly": "Per week",
"interest_calc_monthly": "Per month",
"interest_calc_quarterly": "Per quarter",
"interest_calc_half-year": "Per half year",
"interest_calc_yearly": "Per year",
"liability_direction_credit": "I am owed this debt",
"liability_direction_debit": "I owe this debt to somebody else",
"liability_direction_credit_short": "Owed this debt",
"liability_direction_debit_short": "Owe this debt",
"account_type_debt": "Debt",
"account_type_loan": "Loan",
"left_in_debt": "Amount due",
"account_type_mortgage": "Mortgage",
"save_transactions_by_moving_js": "No transactions|Save this transaction by moving it to another account. |Save these transactions by moving them to another account.",
"none_in_select_list": "(none)",
"transaction_expand_split": "Expand split",
"transaction_collapse_split": "Collapse split",
"default_group_title_name": "(ungrouped)",
"bill_repeats_weekly": "Repeats weekly",
"bill_repeats_monthly": "Repeats monthly",
"bill_repeats_quarterly": "Repeats quarterly",
"bill_repeats_half-year": "Repeats every half year",
"bill_repeats_yearly": "Repeats yearly",
"bill_repeats_weekly_other": "Repeats every other week",
"bill_repeats_monthly_other": "Repeats every other month",
"bill_repeats_quarterly_other": "Repeats every other quarter",
"bill_repeats_half-year_other": "Repeats yearly",
"bill_repeats_yearly_other": "Repeats every other year",
"bill_repeats_weekly_skip": "Repeats every {skip} weeks",
"bill_repeats_monthly_skip": "Repeats every {skip} months",
"bill_repeats_quarterly_skip": "Repeats every {skip} quarters",
"bill_repeats_half-year_skip": "Repeats every {skip} half years",
"bill_repeats_yearly_skip": "Repeats every {skip} years",
"not_expected_period": "Not expected this period",
"subscriptions": "Subscriptions",
"bill_expected_date_js": "Expected {date}",
"inactive": "Inactive",
"forever": "Forever",
"extension_date_is": "Extension date is {date}",
"create_new_bill": "Create new bill",
"store_new_bill": "Store new bill",
"repeat_freq_yearly": "yearly",
"repeat_freq_half-year": "every half-year",
"repeat_freq_quarterly": "quarterly",
"repeat_freq_monthly": "monthly",
"repeat_freq_weekly": "weekly",
"credit_card_type_monthlyFull": "Full payment every month",
"update_liabilities_account": "Update liability",
"update_expense_account": "Update expense account",
"update_revenue_account": "Update revenue account",
"update_undefined_account": "Update account",
"update_asset_account": "Update asset account",
"updated_account_js": "Updated account \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
},
"list": {
"piggy_bank": "Piggy bank",
"percentage": "pct.",
"amount": "Amount",
"lastActivity": "Last activity",
"name": "Name",
"role": "Role",
"description": "Description",
"date": "Date",
"source_account": "Source account",
"destination_account": "Destination account",
"category": "Category",
"iban": "IBAN",
"interest": "Interest",
"interest_period": "Interest period",
"liability_type": "Type of liability",
"liability_direction": "Liability in\/out",
"currentBalance": "Current balance",
"next_expected_match": "Next expected match",
"expected_info": "Next expected transaction",
"start_date": "Start date",
"end_date": "End date",
"payment_info": "Payment information"
},
"config": {
"html_language": "en",
"week_in_year_fns": "'Week' w, yyyy",
"month_and_day_fns": "MMMM d, y",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Foreign amount",
"interest_date": "Interest date",
"name": "Name",
"amount": "Amount",
"iban": "IBAN",
"BIC": "BIC",
"notes": "Notes",
"location": "Location",
"repeat_freq": "Repeats",
"skip": "Skip",
"startdate": "Start date",
"enddate": "End date",
"object_group": "Group",
"attachments": "Attachments",
"deletePermanently": "Delete permanently",
"active": "Active",
"include_net_worth": "Include in net worth",
"cc_type": "Credit card payment plan",
"account_number": "Account number",
"cc_monthly_payment_date": "Credit card monthly payment date",
"virtual_balance": "Virtual balance",
"opening_balance": "Opening balance",
"opening_balance_date": "Opening balance date",
"date": "Date",
"interest": "Interest",
"interest_period": "Interest period",
"currency_id": "Currency",
"liability_type": "Liability type",
"account_role": "Account role",
"liability_direction": "Liability in\/out",
"book_date": "Book date",
"permDeleteWarning": "Deleting stuff from Firefly III is permanent and cannot be undone.",
"account_areYouSure_js": "Are you sure you want to delete the account named \"{name}\"?",
"also_delete_piggyBanks_js": "No piggy banks|The only piggy bank connected to this account will be deleted as well.|All {count} piggy banks connected to this account will be deleted as well.",
"also_delete_transactions_js": "No transactions|The only transaction connected to this account will be deleted as well.|All {count} transactions connected to this account will be deleted as well.",
"process_date": "Processing date",
"due_date": "Due date",
"payment_date": "Payment date",
"invoice_date": "Invoice date",
"amount_min": "Minimum amount",
"amount_max": "Maximum amount",
"start_date": "Start of range",
"end_date": "End of range",
"extension_date": "Extension date"
}
}

View File

@ -1,258 +0,0 @@
{
"firefly": {
"Transfer": "Transferencia",
"Withdrawal": "Retiro",
"Deposit": "Dep\u00f3sito",
"date_and_time": "Fecha y hora",
"no_currency": "(sin moneda)",
"date": "Fecha",
"time": "Hora",
"no_budget": "(sin presupuesto)",
"destination_account": "Cuenta destino",
"source_account": "Cuenta origen",
"single_split": "Divisi\u00f3n",
"create_new_transaction": "Crear una nueva transacci\u00f3n",
"balance": "Balance",
"transaction_journal_extra": "Informaci\u00f3n adicional",
"transaction_journal_meta": "Informaci\u00f3n Meta",
"basic_journal_information": "Informaci\u00f3n b\u00e1sica de transacci\u00f3n",
"bills_to_pay": "Facturas por pagar",
"left_to_spend": "Disponible para gastar",
"attachments": "Archivos adjuntos",
"net_worth": "Valor Neto",
"bill": "Factura",
"no_bill": "(sin factura)",
"tags": "Etiquetas",
"internal_reference": "Referencia interna",
"external_url": "URL externa",
"no_piggy_bank": "(sin hucha)",
"paid": "Pagado",
"notes": "Notas",
"yourAccounts": "Tus cuentas",
"go_to_asset_accounts": "Ver tus cuentas de activos",
"delete_account": "Eliminar cuenta",
"transaction_table_description": "Una tabla que contiene sus transacciones",
"account": "Cuenta",
"description": "Descripci\u00f3n",
"amount": "Cantidad",
"budget": "Presupuesto",
"category": "Categoria",
"opposing_account": "Cuenta opuesta",
"budgets": "Presupuestos",
"categories": "Categor\u00edas",
"go_to_budgets": "Ir a tus presupuestos",
"income": "Ingresos \/ salarios",
"go_to_deposits": "Ir a dep\u00f3sitos",
"go_to_categories": "Ir a tus categor\u00edas",
"expense_accounts": "Cuentas de gastos",
"go_to_expenses": "Ir a gastos",
"go_to_bills": "Ir a tus cuentas",
"bills": "Facturas",
"last_thirty_days": "\u00daltimos treinta d\u00edas",
"last_seven_days": "\u00daltimos siete d\u00edas",
"go_to_piggies": "Ir a tu hucha",
"saved": "Guardado",
"piggy_banks": "Huchas",
"piggy_bank": "Hucha",
"amounts": "Importes",
"left": "Disponible",
"spent": "Gastado",
"Default asset account": "Cuenta de activos por defecto",
"search_results": "Buscar resultados",
"include": "\u00bfIncluir?",
"transaction": "Transaccion",
"account_role_defaultAsset": "Cuentas de ingresos por defecto",
"account_role_savingAsset": "Cuentas de ahorros",
"account_role_sharedAsset": "Cuenta de ingresos compartida",
"clear_location": "Eliminar ubicaci\u00f3n",
"account_role_ccAsset": "Tarjeta de Cr\u00e9dito",
"account_role_cashWalletAsset": "Billetera de efectivo",
"daily_budgets": "Presupuestos diarios",
"weekly_budgets": "Presupuestos semanales",
"monthly_budgets": "Presupuestos mensuales",
"journals_in_period_for_account_js": "Todas las transacciones de la cuenta {title} entre {start} y {end}",
"quarterly_budgets": "Presupuestos trimestrales",
"create_new_expense": "Crear nueva cuenta de gastos",
"create_new_revenue": "Crear nueva cuenta de ingresos",
"create_new_liabilities": "Crear nuevo pasivo",
"half_year_budgets": "Presupuestos semestrales",
"yearly_budgets": "Presupuestos anuales",
"split_transaction_title": "Descripci\u00f3n de la transacci\u00f3n dividida",
"errors_submission": "Hubo un problema con su env\u00edo. Por favor, compruebe los errores.",
"flash_error": "\u00a1Error!",
"store_transaction": "Guardar transacci\u00f3n",
"flash_success": "\u00a1Operaci\u00f3n correcta!",
"create_another": "Despu\u00e9s de guardar, vuelve aqu\u00ed para crear otro.",
"update_transaction": "Actualizar transacci\u00f3n",
"after_update_create_another": "Despu\u00e9s de actualizar, vuelve aqu\u00ed para continuar editando.",
"transaction_updated_no_changes": "<a href=\"transactions\/show\/{ID}\">La transacci\u00f3n #{ID}<\/a> (\"{title}\") no recibi\u00f3 ning\u00fan cambio.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">La transacci\u00f3n #{ID}<\/a> (\"{title}\") ha sido actualizada.",
"spent_x_of_y": "{amount} gastado de {total}",
"search": "Buscar",
"create_new_asset": "Crear nueva cuenta de activos",
"asset_accounts": "Cuenta de activos",
"reset_after": "Restablecer formulario despu\u00e9s del env\u00edo",
"bill_paid_on": "Pagado el {date}",
"first_split_decides": "La primera divisi\u00f3n determina el valor de este campo",
"first_split_overrules_source": "La primera divisi\u00f3n puede anular la cuenta de origen",
"first_split_overrules_destination": "La primera divisi\u00f3n puede anular la cuenta de destino",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">La transacci\u00f3n #{ID} (\"{title}\")<\/a> ha sido almacenada.",
"custom_period": "Per\u00edodo personalizado",
"reset_to_current": "Restablecer al per\u00edodo actual",
"select_period": "Seleccione un per\u00edodo",
"location": "Ubicaci\u00f3n",
"other_budgets": "Presupuestos de tiempo personalizado",
"journal_links": "Enlaces de transacciones",
"go_to_withdrawals": "Ir a tus retiradas",
"revenue_accounts": "Cuentas de ingresos",
"add_another_split": "A\u00f1adir otra divisi\u00f3n",
"actions": "Acciones",
"earned": "Ganado",
"empty": "(vac\u00edo)",
"edit": "Editar",
"never": "Nunca",
"account_type_Loan": "Pr\u00e9stamo",
"account_type_Mortgage": "Hipoteca",
"stored_new_account_js": "Nueva cuenta \"<a href=\"accounts\/show\/{ID}\">{name}<\/a>\" guardada!",
"account_type_Debt": "Deuda",
"liability_direction_null_short": "Desconocido",
"delete": "Eliminar",
"store_new_asset_account": "Crear cuenta de activos",
"store_new_expense_account": "Crear cuenta de gastos",
"store_new_liabilities_account": "Crear nuevo pasivo",
"store_new_revenue_account": "Crear cuenta de ingresos",
"mandatoryFields": "Campos obligatorios",
"optionalFields": "Campos opcionales",
"reconcile_this_account": "Reconciliar esta cuenta",
"interest_calc_weekly": "Por semana",
"interest_calc_monthly": "Por mes",
"interest_calc_quarterly": "Por trimestre",
"interest_calc_half-year": "Por semestre",
"interest_calc_yearly": "Por a\u00f1o",
"liability_direction_credit": "Se me debe esta deuda",
"liability_direction_debit": "Le debo esta deuda a otra persona",
"liability_direction_credit_short": "Ten\u00eda esta deuda",
"liability_direction_debit_short": "Tiene esta deuda",
"account_type_debt": "Deuda",
"account_type_loan": "Pr\u00e9stamo",
"left_in_debt": "Importe debido",
"account_type_mortgage": "Hipoteca",
"save_transactions_by_moving_js": "Ninguna transacci\u00f3n|Guardar esta transacci\u00f3n movi\u00e9ndola a otra cuenta. |Guardar estas transacciones movi\u00e9ndolas a otra cuenta.",
"none_in_select_list": "(ninguno)",
"transaction_expand_split": "Expandir divisi\u00f3n",
"transaction_collapse_split": "Colapsar divisi\u00f3n",
"default_group_title_name": "(sin agrupaci\u00f3n)",
"bill_repeats_weekly": "Repetir semanalmente",
"bill_repeats_monthly": "Repetir mensualmente",
"bill_repeats_quarterly": "Repite trimestralmente",
"bill_repeats_half-year": "Repetir cada 6 meses",
"bill_repeats_yearly": "Repetir anualmente",
"bill_repeats_weekly_other": "Repetir cada dos semanas",
"bill_repeats_monthly_other": "Repetir cada dos meses",
"bill_repeats_quarterly_other": "Repetir cada dos trimestres",
"bill_repeats_half-year_other": "Repetir anualmente",
"bill_repeats_yearly_other": "Repetir cada dos a\u00f1os",
"bill_repeats_weekly_skip": "Repetir cada {skip} semanas",
"bill_repeats_monthly_skip": "Repetir cada {skip} meses",
"bill_repeats_quarterly_skip": "Repetir cada {skip} trimestres",
"bill_repeats_half-year_skip": "Repetir cada {skip} medios a\u00f1os",
"bill_repeats_yearly_skip": "Repetir cada {skip} a\u00f1os",
"not_expected_period": "No se espera en este per\u00edodo",
"subscriptions": "Suscripciones",
"bill_expected_date_js": "Fecha prevista {date}",
"inactive": "Inactivo",
"forever": "Siempre",
"extension_date_is": "Fecha de extensi\u00f3n es {date}",
"create_new_bill": "Crear nueva factura",
"store_new_bill": "Crear factura",
"repeat_freq_yearly": "anualmente",
"repeat_freq_half-year": "cada medio a\u00f1o",
"repeat_freq_quarterly": "trimestralmente",
"repeat_freq_monthly": "mensualmente",
"repeat_freq_weekly": "semanalmente",
"credit_card_type_monthlyFull": "Pago completo cada mes",
"update_liabilities_account": "Actualizar pasivo",
"update_expense_account": "Actualizar cuenta de gastos",
"update_revenue_account": "Actualizar cuenta de ingresos",
"update_undefined_account": "Actualizar cuenta",
"update_asset_account": "Actualizar cuenta de activos",
"updated_account_js": "Cuenta actualizada \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
},
"list": {
"piggy_bank": "Alcancilla",
"percentage": "pct.",
"amount": "Monto",
"lastActivity": "Actividad m\u00e1s reciente",
"name": "Nombre",
"role": "Rol",
"description": "Descripci\u00f3n",
"date": "Fecha",
"source_account": "Cuenta origen",
"destination_account": "Cuenta destino",
"category": "Categor\u00eda",
"iban": "IBAN",
"interest": "Inter\u00e9s",
"interest_period": "Per\u00edodo de inter\u00e9s",
"liability_type": "Tipo de pasivo",
"liability_direction": "Pasivo entrada\/salida",
"currentBalance": "Balance actual",
"next_expected_match": "Pr\u00f3xima coincidencia esperada",
"expected_info": "Siguiente transacci\u00f3n esperada",
"start_date": "Fecha de inicio",
"end_date": "Fecha fin",
"payment_info": "Informaci\u00f3n del pago"
},
"config": {
"html_language": "es",
"week_in_year_fns": "'Semana' w, yyyy",
"month_and_day_fns": "d MMMM y",
"quarter_fns": "'Trimestre' Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Cantidad extranjera",
"interest_date": "Fecha de inter\u00e9s",
"name": "Nombre",
"amount": "Importe",
"iban": "IBAN",
"BIC": "BIC",
"notes": "Notas",
"location": "Ubicaci\u00f3n",
"repeat_freq": "Repetici\u00f3n",
"skip": "Saltar",
"startdate": "Fecha de inicio",
"enddate": "Fecha fin",
"object_group": "Grupo",
"attachments": "Adjuntos",
"deletePermanently": "Borrar permanentemente",
"active": "Activo",
"include_net_worth": "Incluir en valor neto",
"cc_type": "Plan de pagos con tarjeta de cr\u00e9dito",
"account_number": "N\u00famero de cuenta",
"cc_monthly_payment_date": "Fecha de pago mensual de la tarjeta de cr\u00e9dito",
"virtual_balance": "Saldo virtual",
"opening_balance": "Saldo inicial",
"opening_balance_date": "Fecha del saldo inicial",
"date": "Fecha",
"interest": "Inter\u00e9s",
"interest_period": "Per\u00edodo de inter\u00e9s",
"currency_id": "Divisa",
"liability_type": "Tipo de pasivo",
"account_role": "Rol de cuenta",
"liability_direction": "Pasivo entrada\/salida",
"book_date": "Fecha de registro",
"permDeleteWarning": "Eliminar cosas de Firefly III es permanente y no se puede deshacer.",
"account_areYouSure_js": "\u00bfEst\u00e1 seguro que desea eliminar la cuenta llamada \"{name}\"?",
"also_delete_piggyBanks_js": "Ninguna alcanc\u00eda|La \u00fanica alcanc\u00eda conectada a esta cuenta tambi\u00e9n ser\u00e1 borrada. Tambi\u00e9n se eliminar\u00e1n todas {count} alcanc\u00edas conectados a esta cuenta.",
"also_delete_transactions_js": "Ninguna transacci\u00f3n|La \u00fanica transacci\u00f3n conectada a esta cuenta se eliminar\u00e1 tambi\u00e9n.|Todas las {count} transacciones conectadas a esta cuenta tambi\u00e9n se eliminar\u00e1n.",
"process_date": "Fecha de procesamiento",
"due_date": "Fecha de vencimiento",
"payment_date": "Fecha de pago",
"invoice_date": "Fecha de la factura",
"amount_min": "Importe m\u00ednimo",
"amount_max": "Importe m\u00e1ximo",
"start_date": "Inicio del rango",
"end_date": "Final del rango",
"extension_date": "Fecha de extensi\u00f3n"
}
}

View File

@ -1,258 +0,0 @@
{
"firefly": {
"Transfer": "Siirto",
"Withdrawal": "Nosto",
"Deposit": "Talletus",
"date_and_time": "Date and time",
"no_currency": "(ei valuuttaa)",
"date": "P\u00e4iv\u00e4m\u00e4\u00e4r\u00e4",
"time": "Time",
"no_budget": "(ei budjettia)",
"destination_account": "Kohdetili",
"source_account": "L\u00e4hdetili",
"single_split": "Split",
"create_new_transaction": "Create a new transaction",
"balance": "Saldo",
"transaction_journal_extra": "Extra information",
"transaction_journal_meta": "Metatiedot",
"basic_journal_information": "Basic transaction information",
"bills_to_pay": "Laskuja maksettavana",
"left_to_spend": "K\u00e4ytett\u00e4viss\u00e4",
"attachments": "Liitteet",
"net_worth": "Varallisuus",
"bill": "Lasku",
"no_bill": "(no bill)",
"tags": "T\u00e4git",
"internal_reference": "Internal reference",
"external_url": "External URL",
"no_piggy_bank": "(ei s\u00e4\u00e4st\u00f6possu)",
"paid": "Maksettu",
"notes": "Muistiinpanot",
"yourAccounts": "Omat tilisi",
"go_to_asset_accounts": "Tarkastele omaisuustilej\u00e4si",
"delete_account": "Poista k\u00e4ytt\u00e4j\u00e4tili",
"transaction_table_description": "A table containing your transactions",
"account": "Tili",
"description": "Kuvaus",
"amount": "Summa",
"budget": "Budjetti",
"category": "Kategoria",
"opposing_account": "Vastatili",
"budgets": "Budjetit",
"categories": "Kategoriat",
"go_to_budgets": "Avaa omat budjetit",
"income": "Tuotto \/ ansio",
"go_to_deposits": "Go to deposits",
"go_to_categories": "Avaa omat kategoriat",
"expense_accounts": "Kulutustilit",
"go_to_expenses": "Go to expenses",
"go_to_bills": "Avaa omat laskut",
"bills": "Laskut",
"last_thirty_days": "Viimeiset 30 p\u00e4iv\u00e4\u00e4",
"last_seven_days": "Viimeiset 7 p\u00e4iv\u00e4\u00e4",
"go_to_piggies": "Tarkastele s\u00e4\u00e4st\u00f6possujasi",
"saved": "Saved",
"piggy_banks": "S\u00e4\u00e4st\u00f6possut",
"piggy_bank": "S\u00e4\u00e4st\u00f6possu",
"amounts": "Amounts",
"left": "J\u00e4ljell\u00e4",
"spent": "K\u00e4ytetty",
"Default asset account": "Oletusomaisuustili",
"search_results": "Haun tulokset",
"include": "Include?",
"transaction": "Tapahtuma",
"account_role_defaultAsset": "Oletusk\u00e4ytt\u00f6tili",
"account_role_savingAsset": "S\u00e4\u00e4st\u00f6tili",
"account_role_sharedAsset": "Jaettu k\u00e4ytt\u00f6tili",
"clear_location": "Tyhjenn\u00e4 sijainti",
"account_role_ccAsset": "Luottokortti",
"account_role_cashWalletAsset": "K\u00e4teinen",
"daily_budgets": "Daily budgets",
"weekly_budgets": "Weekly budgets",
"monthly_budgets": "Monthly budgets",
"journals_in_period_for_account_js": "All transactions for account {title} between {start} and {end}",
"quarterly_budgets": "Quarterly budgets",
"create_new_expense": "Luo uusi maksutili",
"create_new_revenue": "Luo uusi tuottotili",
"create_new_liabilities": "Create new liability",
"half_year_budgets": "Half-yearly budgets",
"yearly_budgets": "Yearly budgets",
"split_transaction_title": "Jaetun tapahtuman kuvaus",
"errors_submission": "There was something wrong with your submission. Please check out the errors.",
"flash_error": "Virhe!",
"store_transaction": "Store transaction",
"flash_success": "Valmista tuli!",
"create_another": "Tallennuksen j\u00e4lkeen, palaa takaisin luomaan uusi tapahtuma.",
"update_transaction": "P\u00e4ivit\u00e4 tapahtuma",
"after_update_create_another": "P\u00e4ivityksen j\u00e4lkeen, palaa takaisin jatkamaan muokkausta.",
"transaction_updated_no_changes": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") did not receive any changes.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") has been updated.",
"spent_x_of_y": "Spent {amount} of {total}",
"search": "Hae",
"create_new_asset": "Luo uusi omaisuustili",
"asset_accounts": "K\u00e4ytt\u00f6tilit",
"reset_after": "Tyhjenn\u00e4 lomake l\u00e4hetyksen j\u00e4lkeen",
"bill_paid_on": "Paid on {date}",
"first_split_decides": "The first split determines the value of this field",
"first_split_overrules_source": "The first split may overrule the source account",
"first_split_overrules_destination": "The first split may overrule the destination account",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
"custom_period": "Custom period",
"reset_to_current": "Reset to current period",
"select_period": "Select a period",
"location": "Sijainti",
"other_budgets": "Custom timed budgets",
"journal_links": "Tapahtuman linkit",
"go_to_withdrawals": "Go to your withdrawals",
"revenue_accounts": "Tuottotilit",
"add_another_split": "Lis\u00e4\u00e4 tapahtumaan uusi osa",
"actions": "Toiminnot",
"earned": "Ansaittu",
"empty": "(tyhj\u00e4)",
"edit": "Muokkaa",
"never": "Ei koskaan",
"account_type_Loan": "Laina",
"account_type_Mortgage": "Kiinnelaina",
"stored_new_account_js": "New account \"<a href=\"accounts\/show\/{ID}\">{name}<\/a>\" stored!",
"account_type_Debt": "Velka",
"liability_direction_null_short": "Unknown",
"delete": "Poista",
"store_new_asset_account": "Tallenna uusi omaisuustili",
"store_new_expense_account": "Tallenna uusi kulutustili",
"store_new_liabilities_account": "Tallenna uusi vastuu",
"store_new_revenue_account": "Tallenna uusi tuottotili",
"mandatoryFields": "Pakolliset kent\u00e4t",
"optionalFields": "Valinnaiset kent\u00e4t",
"reconcile_this_account": "T\u00e4sm\u00e4yt\u00e4 t\u00e4m\u00e4 tili",
"interest_calc_weekly": "Per week",
"interest_calc_monthly": "Kuukaudessa",
"interest_calc_quarterly": "Per quarter",
"interest_calc_half-year": "Per half year",
"interest_calc_yearly": "Vuodessa",
"liability_direction_credit": "I am owed this debt",
"liability_direction_debit": "I owe this debt to somebody else",
"liability_direction_credit_short": "Owed this debt",
"liability_direction_debit_short": "Owe this debt",
"account_type_debt": "Debt",
"account_type_loan": "Loan",
"left_in_debt": "Amount due",
"account_type_mortgage": "Mortgage",
"save_transactions_by_moving_js": "No transactions|Save this transaction by moving it to another account. |Save these transactions by moving them to another account.",
"none_in_select_list": "(ei mit\u00e4\u00e4n)",
"transaction_expand_split": "Expand split",
"transaction_collapse_split": "Collapse split",
"default_group_title_name": "(ryhmittelem\u00e4tt\u00f6m\u00e4t)",
"bill_repeats_weekly": "Repeats weekly",
"bill_repeats_monthly": "Repeats monthly",
"bill_repeats_quarterly": "Repeats quarterly",
"bill_repeats_half-year": "Repeats every half year",
"bill_repeats_yearly": "Repeats yearly",
"bill_repeats_weekly_other": "Repeats every other week",
"bill_repeats_monthly_other": "Repeats every other month",
"bill_repeats_quarterly_other": "Repeats every other quarter",
"bill_repeats_half-year_other": "Repeats yearly",
"bill_repeats_yearly_other": "Repeats every other year",
"bill_repeats_weekly_skip": "Repeats every {skip} weeks",
"bill_repeats_monthly_skip": "Repeats every {skip} months",
"bill_repeats_quarterly_skip": "Repeats every {skip} quarters",
"bill_repeats_half-year_skip": "Repeats every {skip} half years",
"bill_repeats_yearly_skip": "Repeats every {skip} years",
"not_expected_period": "Ei odotettavissa t\u00e4ss\u00e4 jaksossa",
"subscriptions": "Subscriptions",
"bill_expected_date_js": "Expected {date}",
"inactive": "Ei aktiivinen",
"forever": "Forever",
"extension_date_is": "Extension date is {date}",
"create_new_bill": "Luo uusi lasku",
"store_new_bill": "Tallenna uusi lasku",
"repeat_freq_yearly": "vuosittain",
"repeat_freq_half-year": "puoli-vuosittain",
"repeat_freq_quarterly": "nelj\u00e4nnesvuosittain",
"repeat_freq_monthly": "kuukausittain",
"repeat_freq_weekly": "viikoittain",
"credit_card_type_monthlyFull": "Full payment every month",
"update_liabilities_account": "P\u00e4ivit\u00e4 vastuu",
"update_expense_account": "P\u00e4ivit\u00e4 kulutustili",
"update_revenue_account": "P\u00e4ivit\u00e4 tuottotili",
"update_undefined_account": "Update account",
"update_asset_account": "P\u00e4ivit\u00e4 omaisuustili",
"updated_account_js": "Updated account \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
},
"list": {
"piggy_bank": "S\u00e4\u00e4st\u00f6possu",
"percentage": "pros.",
"amount": "Summa",
"lastActivity": "Viimeisin tapahtuma",
"name": "Nimi",
"role": "Rooli",
"description": "Kuvaus",
"date": "P\u00e4iv\u00e4m\u00e4\u00e4r\u00e4",
"source_account": "L\u00e4hdetili",
"destination_account": "Kohdetili",
"category": "Kategoria",
"iban": "IBAN",
"interest": "Korko",
"interest_period": "Interest period",
"liability_type": "Vastuutyyppi",
"liability_direction": "Liability in\/out",
"currentBalance": "T\u00e4m\u00e4nhetkinen saldo",
"next_expected_match": "Seuraava lasku odotettavissa",
"expected_info": "Next expected transaction",
"start_date": "Start date",
"end_date": "End date",
"payment_info": "Payment information"
},
"config": {
"html_language": "fi",
"week_in_year_fns": "'Week' w, yyyy",
"month_and_day_fns": "MMMM d, y",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Ulkomaan summa",
"interest_date": "Korkop\u00e4iv\u00e4",
"name": "Nimi",
"amount": "Summa",
"iban": "IBAN",
"BIC": "BIC",
"notes": "Muistiinpanot",
"location": "Sijainti",
"repeat_freq": "Toistot",
"skip": "Ohita",
"startdate": "Aloitusp\u00e4iv\u00e4",
"enddate": "End date",
"object_group": "Ryhm\u00e4",
"attachments": "Liitteet",
"deletePermanently": "Poista pysyv\u00e4sti",
"active": "Aktiivinen",
"include_net_worth": "Sis\u00e4llyt\u00e4 varallisuuteen",
"cc_type": "Luottokortin maksusuunnitelma",
"account_number": "Tilinumero",
"cc_monthly_payment_date": "Luottokortin laskun er\u00e4p\u00e4iv\u00e4",
"virtual_balance": "Virtuaalinen saldo",
"opening_balance": "Alkusaldo",
"opening_balance_date": "Alkusaldon p\u00e4iv\u00e4m\u00e4\u00e4r\u00e4",
"date": "P\u00e4iv\u00e4m\u00e4\u00e4r\u00e4",
"interest": "Korko",
"interest_period": "Korkojakso",
"currency_id": "Valuutta",
"liability_type": "Liability type",
"account_role": "Tilin tyyppi",
"liability_direction": "Liability in\/out",
"book_date": "Kirjausp\u00e4iv\u00e4",
"permDeleteWarning": "Asioiden poistaminen Firefly III:sta on lopullista eik\u00e4 poistoa pysty perumaan.",
"account_areYouSure_js": "Are you sure you want to delete the account named \"{name}\"?",
"also_delete_piggyBanks_js": "No piggy banks|The only piggy bank connected to this account will be deleted as well.|All {count} piggy banks connected to this account will be deleted as well.",
"also_delete_transactions_js": "No transactions|The only transaction connected to this account will be deleted as well.|All {count} transactions connected to this account will be deleted as well.",
"process_date": "K\u00e4sittelyp\u00e4iv\u00e4",
"due_date": "Er\u00e4p\u00e4iv\u00e4",
"payment_date": "Maksup\u00e4iv\u00e4",
"invoice_date": "Laskun p\u00e4iv\u00e4m\u00e4\u00e4r\u00e4",
"amount_min": "V\u00e4himm\u00e4issumma",
"amount_max": "Enimm\u00e4issumma",
"start_date": "Valikoiman alku",
"end_date": "Valikoiman loppu",
"extension_date": "Extension date"
}
}

View File

@ -1,258 +0,0 @@
{
"firefly": {
"Transfer": "Transfert",
"Withdrawal": "D\u00e9pense",
"Deposit": "D\u00e9p\u00f4t",
"date_and_time": "Date et heure",
"no_currency": "(pas de devise)",
"date": "Date",
"time": "Heure",
"no_budget": "(pas de budget)",
"destination_account": "Compte de destination",
"source_account": "Compte source",
"single_split": "Ventilation",
"create_new_transaction": "Cr\u00e9er une nouvelle op\u00e9ration",
"balance": "Solde",
"transaction_journal_extra": "Informations suppl\u00e9mentaires",
"transaction_journal_meta": "M\u00e9ta informations",
"basic_journal_information": "Informations de base sur l'op\u00e9ration",
"bills_to_pay": "Factures \u00e0 payer",
"left_to_spend": "Reste \u00e0 d\u00e9penser",
"attachments": "Pi\u00e8ces jointes",
"net_worth": "Avoir net",
"bill": "Facture",
"no_bill": "(aucune facture)",
"tags": "Tags",
"internal_reference": "R\u00e9f\u00e9rence interne",
"external_url": "URL externe",
"no_piggy_bank": "(aucune tirelire)",
"paid": "Pay\u00e9",
"notes": "Notes",
"yourAccounts": "Vos comptes",
"go_to_asset_accounts": "Afficher vos comptes d'actifs",
"delete_account": "Supprimer le compte",
"transaction_table_description": "Une table contenant vos op\u00e9rations",
"account": "Compte",
"description": "Description",
"amount": "Montant",
"budget": "Budget",
"category": "Cat\u00e9gorie",
"opposing_account": "Compte oppos\u00e9",
"budgets": "Budgets",
"categories": "Cat\u00e9gories",
"go_to_budgets": "G\u00e9rer vos budgets",
"income": "Recette \/ revenu",
"go_to_deposits": "Aller aux d\u00e9p\u00f4ts",
"go_to_categories": "G\u00e9rer vos cat\u00e9gories",
"expense_accounts": "Comptes de d\u00e9penses",
"go_to_expenses": "Aller aux d\u00e9penses",
"go_to_bills": "G\u00e9rer vos factures",
"bills": "Factures",
"last_thirty_days": "Trente derniers jours",
"last_seven_days": "7 Derniers Jours",
"go_to_piggies": "G\u00e9rer vos tirelires",
"saved": "Sauvegard\u00e9",
"piggy_banks": "Tirelires",
"piggy_bank": "Tirelire",
"amounts": "Montants",
"left": "Reste",
"spent": "D\u00e9pens\u00e9",
"Default asset account": "Compte d\u2019actif par d\u00e9faut",
"search_results": "R\u00e9sultats de la recherche",
"include": "Inclure ?",
"transaction": "Op\u00e9ration",
"account_role_defaultAsset": "Compte d'actif par d\u00e9faut",
"account_role_savingAsset": "Compte d\u2019\u00e9pargne",
"account_role_sharedAsset": "Compte d'actif partag\u00e9",
"clear_location": "Effacer la localisation",
"account_role_ccAsset": "Carte de cr\u00e9dit",
"account_role_cashWalletAsset": "Porte-monnaie",
"daily_budgets": "Budgets quotidiens",
"weekly_budgets": "Budgets hebdomadaires",
"monthly_budgets": "Budgets mensuels",
"journals_in_period_for_account_js": "Toutes les op\u00e9rations pour le compte {title} entre {start} et {end}",
"quarterly_budgets": "Budgets trimestriels",
"create_new_expense": "Cr\u00e9er nouveau compte de d\u00e9penses",
"create_new_revenue": "Cr\u00e9er nouveau compte de recettes",
"create_new_liabilities": "Cr\u00e9er un nouveau passif",
"half_year_budgets": "Budgets semestriels",
"yearly_budgets": "Budgets annuels",
"split_transaction_title": "Description de l'op\u00e9ration ventil\u00e9e",
"errors_submission": "Certaines informations ne sont pas correctes dans votre formulaire. Veuillez v\u00e9rifier les erreurs.",
"flash_error": "Erreur !",
"store_transaction": "Enregistrer l'op\u00e9ration",
"flash_success": "Super !",
"create_another": "Apr\u00e8s enregistrement, revenir ici pour en cr\u00e9er un nouveau.",
"update_transaction": "Mettre \u00e0 jour l'op\u00e9ration",
"after_update_create_another": "Apr\u00e8s la mise \u00e0 jour, revenir ici pour continuer l'\u00e9dition.",
"transaction_updated_no_changes": "<a href=\"transactions\/show\/{ID}\">L'op\u00e9ration n\u00b0{ID}<\/a> (\"{title}\") n'a pas \u00e9t\u00e9 modifi\u00e9e.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">L'op\u00e9ration n\u00b0{ID}<\/a> (\"{title}\") a \u00e9t\u00e9 mise \u00e0 jour.",
"spent_x_of_y": "D\u00e9pens\u00e9 {amount} sur {total}",
"search": "Rechercher",
"create_new_asset": "Cr\u00e9er un nouveau compte d\u2019actif",
"asset_accounts": "Comptes d\u2019actif",
"reset_after": "R\u00e9initialiser le formulaire apr\u00e8s soumission",
"bill_paid_on": "Pay\u00e9 le {date}",
"first_split_decides": "La premi\u00e8re ventilation d\u00e9termine la valeur de ce champ",
"first_split_overrules_source": "La premi\u00e8re ventilation peut remplacer le compte source",
"first_split_overrules_destination": "La premi\u00e8re ventilation peut remplacer le compte de destination",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">L'op\u00e9ration n\u00b0{ID} (\"{title}\")<\/a> a \u00e9t\u00e9 enregistr\u00e9e.",
"custom_period": "P\u00e9riode personnalis\u00e9e",
"reset_to_current": "R\u00e9initialiser \u00e0 la p\u00e9riode en cours",
"select_period": "S\u00e9lectionnez une p\u00e9riode",
"location": "Emplacement",
"other_budgets": "Budgets \u00e0 p\u00e9riode personnalis\u00e9e",
"journal_links": "Liens d'op\u00e9ration",
"go_to_withdrawals": "Acc\u00e9der \u00e0 vos retraits",
"revenue_accounts": "Comptes de recettes",
"add_another_split": "Ajouter une autre fraction",
"actions": "Actions",
"earned": "Gagn\u00e9",
"empty": "(vide)",
"edit": "Modifier",
"never": "Jamais",
"account_type_Loan": "Pr\u00eat",
"account_type_Mortgage": "Pr\u00eat hypoth\u00e9caire",
"stored_new_account_js": "Nouveau compte \"<a href=\"accounts\/show\/{ID}\">{name}<\/a>\" enregistr\u00e9 !",
"account_type_Debt": "Dette",
"liability_direction_null_short": "Inconnu",
"delete": "Supprimer",
"store_new_asset_account": "Cr\u00e9er un nouveau compte d\u2019actif",
"store_new_expense_account": "Cr\u00e9er un nouveau compte de d\u00e9penses",
"store_new_liabilities_account": "Enregistrer un nouveau passif",
"store_new_revenue_account": "Cr\u00e9er un compte de recettes",
"mandatoryFields": "Champs obligatoires",
"optionalFields": "Champs optionnels",
"reconcile_this_account": "Rapprocher ce compte",
"interest_calc_weekly": "Par semaine",
"interest_calc_monthly": "Par mois",
"interest_calc_quarterly": "Par trimestre",
"interest_calc_half-year": "Par semestre",
"interest_calc_yearly": "Par an",
"liability_direction_credit": "On me doit cette dette",
"liability_direction_debit": "Je dois cette dette \u00e0 quelqu'un d'autre",
"liability_direction_credit_short": "Emprunteur",
"liability_direction_debit_short": "Pr\u00eateur",
"account_type_debt": "Dette",
"account_type_loan": "Emprunt",
"left_in_debt": "Montant d\u00fb",
"account_type_mortgage": "Pr\u00eat immobilier",
"save_transactions_by_moving_js": "Aucune op\u00e9ration|Conserver cette op\u00e9ration en la d\u00e9pla\u00e7ant vers un autre compte. |Conserver ces op\u00e9rations en les d\u00e9pla\u00e7ant vers un autre compte.",
"none_in_select_list": "(aucun)",
"transaction_expand_split": "D\u00e9velopper la ventilation",
"transaction_collapse_split": "R\u00e9duire la ventilation",
"default_group_title_name": "(Sans groupement)",
"bill_repeats_weekly": "Se r\u00e9p\u00e8te toutes les semaines",
"bill_repeats_monthly": "Se r\u00e9p\u00e8te tous les mois",
"bill_repeats_quarterly": "Se r\u00e9p\u00e8te tous les trimestres",
"bill_repeats_half-year": "Se r\u00e9p\u00e8te tous les semestres",
"bill_repeats_yearly": "Se r\u00e9p\u00e8te tous les ans",
"bill_repeats_weekly_other": "Se r\u00e9p\u00e8te toutes les deux semaines",
"bill_repeats_monthly_other": "Se r\u00e9p\u00e8te tous les deux mois",
"bill_repeats_quarterly_other": "Se r\u00e9p\u00e8te tous les deux trimestres",
"bill_repeats_half-year_other": "Se r\u00e9p\u00e8te tous les ans",
"bill_repeats_yearly_other": "Se r\u00e9p\u00e8te tous les deux ans",
"bill_repeats_weekly_skip": "Se r\u00e9p\u00e8te toutes les {skip} semaines",
"bill_repeats_monthly_skip": "Se r\u00e9p\u00e8te tous les {skip} mois",
"bill_repeats_quarterly_skip": "Se r\u00e9p\u00e8te tous les {skip} trimestres",
"bill_repeats_half-year_skip": "Se r\u00e9p\u00e8te tous les {skip} semestres",
"bill_repeats_yearly_skip": "Se r\u00e9p\u00e8te tous les {skip} ans",
"not_expected_period": "Pas attendu cette p\u00e9riode",
"subscriptions": "Abonnements",
"bill_expected_date_js": "Attendu le {date}",
"inactive": "Inactif",
"forever": "Pour toujours",
"extension_date_is": "La date de l'extension est {date}",
"create_new_bill": "Cr\u00e9er une nouvelle facture",
"store_new_bill": "Cr\u00e9er une nouvelle facture",
"repeat_freq_yearly": "annuellement",
"repeat_freq_half-year": "semestriel",
"repeat_freq_quarterly": "trimestriel",
"repeat_freq_monthly": "mensuel",
"repeat_freq_weekly": "hebdomadaire",
"credit_card_type_monthlyFull": "Paiement complet tous les mois",
"update_liabilities_account": "Mettre \u00e0 jour le passif",
"update_expense_account": "Mettre \u00e0 jour le compte de d\u00e9penses",
"update_revenue_account": "Mettre \u00e0 jour le compte de recettes",
"update_undefined_account": "Mettre \u00e0 jour le compte",
"update_asset_account": "Mettre \u00e0 jour le compte d\u2019actif",
"updated_account_js": "Compte \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\" mis \u00e0 jour."
},
"list": {
"piggy_bank": "Tirelire",
"percentage": "pct.",
"amount": "Montant",
"lastActivity": "Activit\u00e9 r\u00e9cente",
"name": "Nom",
"role": "R\u00f4le",
"description": "Description",
"date": "Date",
"source_account": "Compte source",
"destination_account": "Compte destinataire",
"category": "Cat\u00e9gorie",
"iban": "Num\u00e9ro IBAN",
"interest": "Int\u00e9r\u00eat",
"interest_period": "P\u00e9riode d\u2019int\u00e9r\u00eat",
"liability_type": "Type de passif",
"liability_direction": "Sens du passif",
"currentBalance": "Solde courant",
"next_expected_match": "Prochaine association attendue",
"expected_info": "Prochaine op\u00e9ration attendue",
"start_date": "Date de d\u00e9but",
"end_date": "Date de fin",
"payment_info": "Informations sur le paiement"
},
"config": {
"html_language": "fr",
"week_in_year_fns": "'Semaine' w, yyyy",
"month_and_day_fns": "d MMMM y",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "Montant en devise \u00e9trang\u00e8re",
"interest_date": "Date de valeur (int\u00e9r\u00eats)",
"name": "Nom",
"amount": "Montant",
"iban": "Num\u00e9ro IBAN",
"BIC": "Code BIC",
"notes": "Notes",
"location": "Emplacement",
"repeat_freq": "R\u00e9p\u00e9titions",
"skip": "Ignorer",
"startdate": "Date de d\u00e9but",
"enddate": "Date de fin",
"object_group": "Groupe",
"attachments": "Documents joints",
"deletePermanently": "Supprimer d\u00e9finitivement",
"active": "Actif",
"include_net_worth": "Inclure dans l'avoir net",
"cc_type": "Plan de paiement de carte de cr\u00e9dit",
"account_number": "Num\u00e9ro de compte",
"cc_monthly_payment_date": "Date de paiement mensuelle de la carte de cr\u00e9dit",
"virtual_balance": "Solde virtuel",
"opening_balance": "Solde initial",
"opening_balance_date": "Date du solde initial",
"date": "Date",
"interest": "Int\u00e9r\u00eat",
"interest_period": "P\u00e9riode d\u2019int\u00e9r\u00eat",
"currency_id": "Devise",
"liability_type": "Type de passif",
"account_role": "R\u00f4le du compte",
"liability_direction": "Sens du passif",
"book_date": "Date de r\u00e9servation",
"permDeleteWarning": "Supprimer quelque chose dans Firefly est permanent et ne peut pas \u00eatre annul\u00e9.",
"account_areYouSure_js": "\u00cates-vous s\u00fbr de vouloir supprimer le compte nomm\u00e9 \"{name}\" ?",
"also_delete_piggyBanks_js": "Aucune tirelire|La seule tirelire li\u00e9e \u00e0 ce compte sera aussi supprim\u00e9e.|Les {count} tirelires li\u00e9es \u00e0 ce compte seront aussi supprim\u00e9es.",
"also_delete_transactions_js": "Aucune op\u00e9ration|La seule op\u00e9ration li\u00e9e \u00e0 ce compte sera aussi supprim\u00e9e.|Les {count} op\u00e9rations li\u00e9es \u00e0 ce compte seront aussi supprim\u00e9es.",
"process_date": "Date de traitement",
"due_date": "\u00c9ch\u00e9ance",
"payment_date": "Date de paiement",
"invoice_date": "Date de facturation",
"amount_min": "Montant minimum",
"amount_max": "Montant maximum",
"start_date": "D\u00e9but de l'\u00e9tendue",
"end_date": "Fin de l'\u00e9tendue",
"extension_date": "Date d'extension"
}
}

View File

@ -1,258 +0,0 @@
{
"firefly": {
"Transfer": "\u00c1tvezet\u00e9s",
"Withdrawal": "K\u00f6lts\u00e9g",
"Deposit": "Bev\u00e9tel",
"date_and_time": "Date and time",
"no_currency": "(nincs p\u00e9nznem)",
"date": "D\u00e1tum",
"time": "Time",
"no_budget": "(nincs k\u00f6lts\u00e9gkeret)",
"destination_account": "C\u00e9lsz\u00e1mla",
"source_account": "Forr\u00e1s sz\u00e1mla",
"single_split": "Feloszt\u00e1s",
"create_new_transaction": "Create a new transaction",
"balance": "Egyenleg",
"transaction_journal_extra": "Extra information",
"transaction_journal_meta": "Meta-inform\u00e1ci\u00f3",
"basic_journal_information": "Basic transaction information",
"bills_to_pay": "Fizetend\u0151 sz\u00e1ml\u00e1k",
"left_to_spend": "Elk\u00f6lthet\u0151",
"attachments": "Mell\u00e9kletek",
"net_worth": "Nett\u00f3 \u00e9rt\u00e9k",
"bill": "Sz\u00e1mla",
"no_bill": "(no bill)",
"tags": "C\u00edmk\u00e9k",
"internal_reference": "Internal reference",
"external_url": "External URL",
"no_piggy_bank": "(nincs malacpersely)",
"paid": "Kifizetve",
"notes": "Megjegyz\u00e9sek",
"yourAccounts": "Banksz\u00e1ml\u00e1k",
"go_to_asset_accounts": "Eszk\u00f6zsz\u00e1ml\u00e1k megtekint\u00e9se",
"delete_account": "Fi\u00f3k t\u00f6rl\u00e9se",
"transaction_table_description": "Tranzakci\u00f3kat tartalmaz\u00f3 t\u00e1bl\u00e1zat",
"account": "Banksz\u00e1mla",
"description": "Le\u00edr\u00e1s",
"amount": "\u00d6sszeg",
"budget": "K\u00f6lts\u00e9gkeret",
"category": "Kateg\u00f3ria",
"opposing_account": "Ellenoldali sz\u00e1mla",
"budgets": "K\u00f6lts\u00e9gkeretek",
"categories": "Kateg\u00f3ri\u00e1k",
"go_to_budgets": "Ugr\u00e1s a k\u00f6lts\u00e9gkeretekhez",
"income": "J\u00f6vedelem \/ bev\u00e9tel",
"go_to_deposits": "Ugr\u00e1s a bev\u00e9telekre",
"go_to_categories": "Ugr\u00e1s a kateg\u00f3ri\u00e1khoz",
"expense_accounts": "K\u00f6lts\u00e9gsz\u00e1ml\u00e1k",
"go_to_expenses": "Ugr\u00e1s a kiad\u00e1sokra",
"go_to_bills": "Ugr\u00e1s a sz\u00e1ml\u00e1khoz",
"bills": "Sz\u00e1ml\u00e1k",
"last_thirty_days": "Elm\u00falt harminc nap",
"last_seven_days": "Utols\u00f3 h\u00e9t nap",
"go_to_piggies": "Ugr\u00e1s a malacperselyekhez",
"saved": "Mentve",
"piggy_banks": "Malacperselyek",
"piggy_bank": "Malacpersely",
"amounts": "Mennyis\u00e9gek",
"left": "Maradv\u00e1ny",
"spent": "Elk\u00f6lt\u00f6tt",
"Default asset account": "Alap\u00e9rtelmezett eszk\u00f6zsz\u00e1mla",
"search_results": "Keres\u00e9si eredm\u00e9nyek",
"include": "Include?",
"transaction": "Tranzakci\u00f3",
"account_role_defaultAsset": "Alap\u00e9rtelmezett eszk\u00f6zsz\u00e1mla",
"account_role_savingAsset": "Megtakar\u00edt\u00e1si sz\u00e1mla",
"account_role_sharedAsset": "Megosztott eszk\u00f6zsz\u00e1mla",
"clear_location": "Hely t\u00f6rl\u00e9se",
"account_role_ccAsset": "Hitelk\u00e1rtya",
"account_role_cashWalletAsset": "K\u00e9szp\u00e9nz",
"daily_budgets": "Daily budgets",
"weekly_budgets": "Weekly budgets",
"monthly_budgets": "Monthly budgets",
"journals_in_period_for_account_js": "All transactions for account {title} between {start} and {end}",
"quarterly_budgets": "Quarterly budgets",
"create_new_expense": "\u00daj k\u00f6lts\u00e9gsz\u00e1mla l\u00e9trehoz\u00e1sa",
"create_new_revenue": "\u00daj j\u00f6vedelemsz\u00e1mla l\u00e9trehoz\u00e1sa",
"create_new_liabilities": "Create new liability",
"half_year_budgets": "Half-yearly budgets",
"yearly_budgets": "Yearly budgets",
"split_transaction_title": "Felosztott tranzakci\u00f3 le\u00edr\u00e1sa",
"errors_submission": "There was something wrong with your submission. Please check out the errors.",
"flash_error": "Hiba!",
"store_transaction": "Store transaction",
"flash_success": "Siker!",
"create_another": "A t\u00e1rol\u00e1s ut\u00e1n t\u00e9rjen vissza ide \u00faj l\u00e9trehoz\u00e1s\u00e1hoz.",
"update_transaction": "Tranzakci\u00f3 friss\u00edt\u00e9se",
"after_update_create_another": "A friss\u00edt\u00e9s ut\u00e1n t\u00e9rjen vissza ide a szerkeszt\u00e9s folytat\u00e1s\u00e1hoz.",
"transaction_updated_no_changes": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") did not receive any changes.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") has been updated.",
"spent_x_of_y": "Spent {amount} of {total}",
"search": "Keres\u00e9s",
"create_new_asset": "\u00daj eszk\u00f6zsz\u00e1mla l\u00e9trehoz\u00e1sa",
"asset_accounts": "Eszk\u00f6zsz\u00e1ml\u00e1k",
"reset_after": "\u0170rlap t\u00f6rl\u00e9se a bek\u00fcld\u00e9s ut\u00e1n",
"bill_paid_on": "Paid on {date}",
"first_split_decides": "The first split determines the value of this field",
"first_split_overrules_source": "The first split may overrule the source account",
"first_split_overrules_destination": "The first split may overrule the destination account",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> mentve.",
"custom_period": "Custom period",
"reset_to_current": "Reset to current period",
"select_period": "Select a period",
"location": "Hely",
"other_budgets": "Custom timed budgets",
"journal_links": "Tranzakci\u00f3 \u00f6sszekapcsol\u00e1sok",
"go_to_withdrawals": "Ugr\u00e1s a k\u00f6lts\u00e9gekhez",
"revenue_accounts": "J\u00f6vedelemsz\u00e1ml\u00e1k",
"add_another_split": "M\u00e1sik feloszt\u00e1s hozz\u00e1ad\u00e1sa",
"actions": "M\u0171veletek",
"earned": "Megkeresett",
"empty": "(\u00fcres)",
"edit": "Szerkeszt\u00e9s",
"never": "Soha",
"account_type_Loan": "Hitel",
"account_type_Mortgage": "Jelz\u00e1log",
"stored_new_account_js": "New account \"<a href=\"accounts\/show\/{ID}\">{name}<\/a>\" stored!",
"account_type_Debt": "Ad\u00f3ss\u00e1g",
"liability_direction_null_short": "Unknown",
"delete": "T\u00f6rl\u00e9s",
"store_new_asset_account": "\u00daj eszk\u00f6zsz\u00e1mla t\u00e1rol\u00e1sa",
"store_new_expense_account": "\u00daj k\u00f6lts\u00e9gsz\u00e1mla t\u00e1rol\u00e1sa",
"store_new_liabilities_account": "\u00daj k\u00f6telezetts\u00e9g elt\u00e1rol\u00e1sa",
"store_new_revenue_account": "\u00daj j\u00f6vedelemsz\u00e1mla l\u00e9trehoz\u00e1sa",
"mandatoryFields": "K\u00f6telez\u0151 mez\u0151k",
"optionalFields": "Nem k\u00f6telez\u0151 mez\u0151k",
"reconcile_this_account": "Sz\u00e1mla egyeztet\u00e9se",
"interest_calc_weekly": "Per week",
"interest_calc_monthly": "Havonta",
"interest_calc_quarterly": "Per quarter",
"interest_calc_half-year": "Per half year",
"interest_calc_yearly": "\u00c9vente",
"liability_direction_credit": "I am owed this debt",
"liability_direction_debit": "I owe this debt to somebody else",
"liability_direction_credit_short": "Owed this debt",
"liability_direction_debit_short": "Owe this debt",
"account_type_debt": "Debt",
"account_type_loan": "Loan",
"left_in_debt": "Amount due",
"account_type_mortgage": "Mortgage",
"save_transactions_by_moving_js": "No transactions|Save this transaction by moving it to another account. |Save these transactions by moving them to another account.",
"none_in_select_list": "(nincs)",
"transaction_expand_split": "Expand split",
"transaction_collapse_split": "Collapse split",
"default_group_title_name": "(nem csoportos\u00edtott)",
"bill_repeats_weekly": "Repeats weekly",
"bill_repeats_monthly": "Repeats monthly",
"bill_repeats_quarterly": "Repeats quarterly",
"bill_repeats_half-year": "Repeats every half year",
"bill_repeats_yearly": "Repeats yearly",
"bill_repeats_weekly_other": "Repeats every other week",
"bill_repeats_monthly_other": "Repeats every other month",
"bill_repeats_quarterly_other": "Repeats every other quarter",
"bill_repeats_half-year_other": "Repeats yearly",
"bill_repeats_yearly_other": "Repeats every other year",
"bill_repeats_weekly_skip": "Repeats every {skip} weeks",
"bill_repeats_monthly_skip": "Repeats every {skip} months",
"bill_repeats_quarterly_skip": "Repeats every {skip} quarters",
"bill_repeats_half-year_skip": "Repeats every {skip} half years",
"bill_repeats_yearly_skip": "Repeats every {skip} years",
"not_expected_period": "Nem v\u00e1rhat\u00f3 ebben az id\u0151szakban",
"subscriptions": "Subscriptions",
"bill_expected_date_js": "Expected {date}",
"inactive": "Inakt\u00edv",
"forever": "Forever",
"extension_date_is": "Extension date is {date}",
"create_new_bill": "\u00daj sz\u00e1mla l\u00e9trehoz\u00e1sa",
"store_new_bill": "\u00daj sz\u00e1mla t\u00e1rol\u00e1sa",
"repeat_freq_yearly": "\u00e9ves",
"repeat_freq_half-year": "f\u00e9l\u00e9vente",
"repeat_freq_quarterly": "negyed\u00e9ves",
"repeat_freq_monthly": "havi",
"repeat_freq_weekly": "heti",
"credit_card_type_monthlyFull": "Full payment every month",
"update_liabilities_account": "K\u00f6telezetts\u00e9g friss\u00edt\u00e9se",
"update_expense_account": "K\u00f6lts\u00e9gsz\u00e1mla friss\u00edt\u00e9se",
"update_revenue_account": "J\u00f6vedelemsz\u00e1mla friss\u00edt\u00e9se",
"update_undefined_account": "Update account",
"update_asset_account": "Eszk\u00f6zsz\u00e1mla friss\u00edt\u00e9se",
"updated_account_js": "Updated account \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
},
"list": {
"piggy_bank": "Malacpersely",
"percentage": "%",
"amount": "\u00d6sszeg",
"lastActivity": "Utols\u00f3 aktivit\u00e1s",
"name": "N\u00e9v",
"role": "Szerepk\u00f6r",
"description": "Le\u00edr\u00e1s",
"date": "D\u00e1tum",
"source_account": "Forr\u00e1s banksz\u00e1mla",
"destination_account": "C\u00e9l banksz\u00e1mla",
"category": "Kateg\u00f3ria",
"iban": "IBAN",
"interest": "Kamat",
"interest_period": "Interest period",
"liability_type": "A k\u00f6telezetts\u00e9g t\u00edpusa",
"liability_direction": "Liability in\/out",
"currentBalance": "Aktu\u00e1lis egyenleg",
"next_expected_match": "K\u00f6vetkez\u0151 v\u00e1rhat\u00f3 egyez\u00e9s",
"expected_info": "Next expected transaction",
"start_date": "Start date",
"end_date": "End date",
"payment_info": "Payment information"
},
"config": {
"html_language": "hu",
"week_in_year_fns": "'Week' w, yyyy",
"month_and_day_fns": "MMMM d, y",
"quarter_fns": "'Q'Q, yyyy",
"half_year_fns": "'H{half}', yyyy"
},
"form": {
"foreign_amount": "K\u00fclf\u00f6ldi \u00f6sszeg",
"interest_date": "Kamatfizet\u00e9si id\u0151pont",
"name": "N\u00e9v",
"amount": "\u00d6sszeg",
"iban": "IBAN",
"BIC": "BIC",
"notes": "Megjegyz\u00e9sek",
"location": "Hely",
"repeat_freq": "Ism\u00e9tl\u0151d\u00e9sek",
"skip": "Kihagy\u00e1s",
"startdate": "Kezd\u0151 d\u00e1tum",
"enddate": "End date",
"object_group": "Csoport",
"attachments": "Mell\u00e9kletek",
"deletePermanently": "V\u00e9gleges t\u00f6rl\u00e9s",
"active": "Akt\u00edv",
"include_net_worth": "Befoglalva a nett\u00f3 \u00e9rt\u00e9kbe",
"cc_type": "Hitelk\u00e1rtya fizet\u00e9si terv",
"account_number": "Sz\u00e1mlasz\u00e1m",
"cc_monthly_payment_date": "Hitelk\u00e1rtya havi fizet\u00e9s d\u00e1tuma",
"virtual_balance": "Virtu\u00e1lis egyenleg",
"opening_balance": "Nyit\u00f3 egyenleg",
"opening_balance_date": "Nyit\u00f3 egyenleg d\u00e1tuma",
"date": "D\u00e1tum",
"interest": "Kamat",
"interest_period": "Kamatperi\u00f3dus",
"currency_id": "P\u00e9nznem",
"liability_type": "Liability type",
"account_role": "Banksz\u00e1mla szerepk\u00f6re",
"liability_direction": "Liability in\/out",
"book_date": "K\u00f6nyvel\u00e9s d\u00e1tuma",
"permDeleteWarning": "A Firefly III-b\u00f3l t\u00f6rt\u00e9n\u0151 t\u00f6rl\u00e9s v\u00e9gleges \u00e9s nem vonhat\u00f3 vissza.",
"account_areYouSure_js": "Are you sure you want to delete the account named \"{name}\"?",
"also_delete_piggyBanks_js": "No piggy banks|The only piggy bank connected to this account will be deleted as well.|All {count} piggy banks connected to this account will be deleted as well.",
"also_delete_transactions_js": "No transactions|The only transaction connected to this account will be deleted as well.|All {count} transactions connected to this account will be deleted as well.",
"process_date": "Feldolgoz\u00e1s d\u00e1tuma",
"due_date": "Lej\u00e1rati id\u0151pont",
"payment_date": "Fizet\u00e9s d\u00e1tuma",
"invoice_date": "Sz\u00e1mla d\u00e1tuma",
"amount_min": "Minim\u00e1lis \u00f6sszeg",
"amount_max": "Maxim\u00e1lis \u00f6sszeg",
"start_date": "Tartom\u00e1ny kezdete",
"end_date": "Tartom\u00e1ny v\u00e9ge",
"extension_date": "Extension date"
}
}

Some files were not shown because too many files have changed in this diff Show More