mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-15 16:57:09 +00:00
5.5.0-beta.2
This commit is contained in:
@@ -1,5 +1,25 @@
|
||||
#!/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.
|
||||
@@ -8,8 +28,11 @@ 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
|
||||
@@ -27,4 +50,4 @@ yarn prod
|
||||
cp -r fonts ../public
|
||||
|
||||
# remove built stuff
|
||||
rm -rf public
|
||||
rm -rf public
|
||||
|
@@ -18,5 +18,7 @@
|
||||
"/public/js/register.js": "/public/js/register.js",
|
||||
"/public/js/register.js.map": "/public/js/register.js.map",
|
||||
"/public/js/transactions/create.js": "/public/js/transactions/create.js",
|
||||
"/public/js/transactions/create.js.map": "/public/js/transactions/create.js.map"
|
||||
"/public/js/transactions/create.js.map": "/public/js/transactions/create.js.map",
|
||||
"/public/js/transactions/edit.js": "/public/js/transactions/edit.js",
|
||||
"/public/js/transactions/edit.js.map": "/public/js/transactions/edit.js.map"
|
||||
}
|
||||
|
@@ -10,16 +10,17 @@
|
||||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"admin-lte": "^3.0",
|
||||
"axios": "^0.21",
|
||||
"cross-env": "^7.0",
|
||||
"laravel-mix": "^5.0.9",
|
||||
"laravel-mix-bundle-analyzer": "^1.0.5",
|
||||
"lodash": "^4.17.20",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"node-forge": ">=0.10.0",
|
||||
"resolve-url-loader": "^3.1.2",
|
||||
"sass": "^1.32.2",
|
||||
"sass-loader": "^10.1.0",
|
||||
"sass": "^1.32.8",
|
||||
"sass-loader": "^10.1.1",
|
||||
"vue": "^2.6.12",
|
||||
"vue-i18n": "^8.22.2",
|
||||
"vue-template-compiler": "^2.6.12"
|
||||
@@ -27,20 +28,22 @@
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.15.1",
|
||||
"@johmun/vue-tags-input": "^2.1.0",
|
||||
"@popperjs/core": "^2.6.0",
|
||||
"@popperjs/core": "^2.8.6",
|
||||
"bootstrap": "^4.5.3",
|
||||
"chart.js": "^2.9.4",
|
||||
"icheck-bootstrap": "^3.0.1",
|
||||
"jquery": "^3.5.1",
|
||||
"jquery-ui": "^1.12.1",
|
||||
"leaflet": "^1.7.1",
|
||||
"overlayscrollbars": "^1.13.1",
|
||||
"popper.js": "^1.16.1",
|
||||
"tempusdominus": "^5.16.0",
|
||||
"v-calendar": "^2.2.1",
|
||||
"v-calendar": "^2.2.4",
|
||||
"vue-chartjs": "^3.5.1",
|
||||
"vue-router": "^3.4.9",
|
||||
"vue-simple-suggest": "^1.10.3",
|
||||
"vue-typeahead-bootstrap": "^2.5.5",
|
||||
"vue2-leaflet": "^2.6.0",
|
||||
"vuex": "^3.6.0"
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,25 @@
|
||||
#!/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.
|
||||
|
59
frontend/src/app.scss
vendored
59
frontend/src/app.scss
vendored
@@ -25,10 +25,59 @@
|
||||
// iCheck
|
||||
@import '~icheck-bootstrap/icheck-bootstrap.css';
|
||||
// AdminLTE
|
||||
@import 'dist/css/adminlte.css';
|
||||
//@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';
|
||||
|
||||
// Variables and Mixins
|
||||
// ---------------------------------------------------
|
||||
@import '~admin-lte/build/scss/variables';
|
||||
|
||||
// Firefly III colors (?)
|
||||
@import 'scss/variables';
|
||||
|
||||
@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 '../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 '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';
|
||||
|
||||
|
||||
|
||||
// Bootstrap
|
||||
// Already imported by AdminLTE
|
||||
//@import '~bootstrap/scss/bootstrap';
|
||||
|
20
frontend/src/classic/adminlte.js
vendored
20
frontend/src/classic/adminlte.js
vendored
@@ -1,3 +1,23 @@
|
||||
/*
|
||||
* adminlte.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/>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* AdminLTE v3.0.5 (https://adminlte.io)
|
||||
* Copyright 2014-2020 Colorlib <http://colorlib.com>
|
||||
|
@@ -23,9 +23,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Edit"
|
||||
}
|
||||
export default {
|
||||
name: "Edit"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@@ -26,10 +26,10 @@
|
||||
<h3 class="card-title">Title thing</h3>
|
||||
<div class="card-tools">
|
||||
<div class="input-group input-group-sm" style="width: 150px;">
|
||||
<input type="text" name="table_search" class="form-control float-right" placeholder="Search">
|
||||
<input class="form-control float-right" name="table_search" placeholder="Search" type="text">
|
||||
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-default">
|
||||
<button class="btn btn-default" type="submit">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -43,7 +43,7 @@
|
||||
<tr>
|
||||
<th scope="col"> </th>
|
||||
<th scope="col">{{ $t('list.name') }}</th>
|
||||
<th scope="col" v-if="'asset' === $props.accountTypes">{{ $t('list.role') }}</th>
|
||||
<th v-if="'asset' === $props.accountTypes" scope="col">{{ $t('list.role') }}</th>
|
||||
<th scope="col">{{ $t('list.iban') }}</th>
|
||||
<th scope="col" style="text-align: right;">{{ $t('list.currentBalance') }}</th>
|
||||
<th scope="col">{{ $t('list.balanceDiff') }}</th>
|
||||
|
@@ -19,15 +19,15 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
I am a show
|
||||
</div>
|
||||
<div>
|
||||
I am a show
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Show"
|
||||
}
|
||||
export default {
|
||||
name: "Show"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@@ -24,91 +24,91 @@
|
||||
|
||||
|
||||
<script>
|
||||
import FormatLabel from "../charts/FormatLabel";
|
||||
import FormatLabel from "../charts/FormatLabel";
|
||||
|
||||
export default {
|
||||
name: "DefaultBarOptions",
|
||||
data() {
|
||||
return {}
|
||||
export default {
|
||||
name: "DefaultBarOptions",
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
getDefaultOptions() {
|
||||
return {
|
||||
type: 'bar',
|
||||
layout: {
|
||||
padding: {
|
||||
left: 50,
|
||||
right: 50,
|
||||
top: 0,
|
||||
bottom: 0
|
||||
},
|
||||
},
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
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>
|
||||
|
||||
<style scoped>
|
||||
|
@@ -105,7 +105,7 @@ export default {
|
||||
callback: function (value, index, values) {
|
||||
// date format
|
||||
let dateObj = new Date(value);
|
||||
let options = { year: 'numeric', month: 'long', day: 'numeric' };
|
||||
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);
|
||||
|
@@ -23,9 +23,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DefaultPieOptions"
|
||||
}
|
||||
export default {
|
||||
name: "DefaultPieOptions"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@@ -18,56 +18,56 @@
|
||||
- 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 = "";
|
||||
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;
|
||||
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;
|
||||
},
|
||||
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>
|
||||
|
@@ -26,8 +26,8 @@
|
||||
|
||||
<td style="vertical-align: middle">
|
||||
<div class="progress progress active">
|
||||
<div class="progress-bar bg-success progress-bar-striped" role="progressbar"
|
||||
:aria-valuenow="budgetLimit.pctGreen" aria-valuemin="0" aria-valuemax="100" :style="'width: '+ budgetLimit.pctGreen + '%;'">
|
||||
<div :aria-valuenow="budgetLimit.pctGreen" :style="'width: '+ budgetLimit.pctGreen + '%;'"
|
||||
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-success progress-bar-striped" role="progressbar">
|
||||
<span v-if="budgetLimit.pctGreen > 35">
|
||||
Spent
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent) }}
|
||||
@@ -38,8 +38,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="progress-bar bg-warning progress-bar-striped" role="progressbar"
|
||||
:aria-valuenow="budgetLimit.pctOrange" aria-valuemin="0" aria-valuemax="100" :style="'width: '+ budgetLimit.pctOrange + '%;'">
|
||||
<div :aria-valuenow="budgetLimit.pctOrange" :style="'width: '+ budgetLimit.pctOrange + '%;'"
|
||||
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-warning progress-bar-striped" role="progressbar">
|
||||
<span v-if="budgetLimit.pctRed <= 50 && budgetLimit.pctOrange > 35">
|
||||
Spent
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent) }}
|
||||
@@ -48,15 +48,25 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="progress-bar bg-danger progress-bar-striped" role="progressbar"
|
||||
:aria-valuenow="budgetLimit.pctRed" aria-valuemin="0" aria-valuemax="100" :style="'width: '+ budgetLimit.pctRed + '%;'">
|
||||
<span v-if="budgetLimit.pctOrange <= 50 && budgetLimit.pctRed > 35">
|
||||
<div :aria-valuenow="budgetLimit.pctRed" :style="'width: '+ budgetLimit.pctRed + '%;'"
|
||||
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-danger progress-bar-striped" role="progressbar">
|
||||
<span v-if="budgetLimit.pctOrange <= 50 && budgetLimit.pctRed > 35" class="text-muted">
|
||||
Spent
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent) }}
|
||||
of
|
||||
{{ 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">
|
||||
|
||||
Spent
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent) }}
|
||||
of
|
||||
{{ 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) }}
|
||||
@@ -65,8 +75,8 @@
|
||||
</small>
|
||||
</td>
|
||||
|
||||
<td style="width:10%;" class="align-middle d-none d-lg-table-cell">
|
||||
<span class="text-success" v-if="parseFloat(budgetLimit.amount) + parseFloat(budgetLimit.spent) > 0">
|
||||
<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',
|
||||
@@ -74,10 +84,10 @@
|
||||
}).format(parseFloat(budgetLimit.amount) + parseFloat(budgetLimit.spent))
|
||||
}}
|
||||
</span>
|
||||
<span class="text-muted" v-if="0.0 === parseFloat(budgetLimit.amount) + parseFloat(budgetLimit.spent)">
|
||||
<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 class="text-danger" v-if="parseFloat(budgetLimit.amount) + parseFloat(budgetLimit.spent) < 0">
|
||||
<span v-if="parseFloat(budgetLimit.amount) + parseFloat(budgetLimit.spent) < 0" class="text-danger">
|
||||
{{
|
||||
Intl.NumberFormat(locale, {
|
||||
style: 'currency',
|
||||
@@ -93,7 +103,7 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "BudgetLimitRow",
|
||||
mounted() {
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
},
|
||||
data() {
|
||||
|
@@ -26,13 +26,13 @@
|
||||
<div class="card-body table-responsive p-0">
|
||||
<table class="table table-sm">
|
||||
<tbody>
|
||||
<BudgetLimitRow v-bind:key="key" v-for="(budgetLimit, key) in budgetLimits" :budgetLimit="budgetLimit" />
|
||||
<BudgetRow v-bind:key="key" v-for="(budget, key) in budgets" :budget="budget" />
|
||||
<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 href="./budgets" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_budgets') }}</a>
|
||||
<a class="btn btn-default button-sm" href="./budgets"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_budgets') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -40,6 +40,7 @@
|
||||
<script>
|
||||
import BudgetLimitRow from "./BudgetLimitRow";
|
||||
import BudgetRow from "./BudgetRow";
|
||||
|
||||
export default {
|
||||
name: "BudgetListGroup",
|
||||
components: {BudgetLimitRow, BudgetRow},
|
||||
|
@@ -18,6 +18,7 @@
|
||||
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
|
||||
<template>
|
||||
<tr>
|
||||
<td style="width:25%;">
|
||||
@@ -34,7 +35,7 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "BudgetRow",
|
||||
mounted() {
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
},
|
||||
data() {
|
||||
|
@@ -30,43 +30,41 @@
|
||||
</div>
|
||||
<date-picker
|
||||
v-model="range"
|
||||
mode="date"
|
||||
rows="2"
|
||||
: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
|
||||
class="btn btn-secondary btn-sm"
|
||||
@click="togglePopover({ placement: 'auto-start', positionFixed:true })"
|
||||
:title="$t('firefly.custom_period')" class="btn btn-secondary btn-sm"
|
||||
@click="togglePopover({ placement: 'auto-start', positionFixed: true })"
|
||||
><i class="fas fa-calendar-alt"></i></button>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
<button :title="$t('firefly.reset_to_current')"
|
||||
class="btn btn-secondary"
|
||||
@click="resetDate"
|
||||
><i class="fas fa-history"></i></button>
|
||||
|
||||
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
<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">
|
||||
<i class="fas fa-list"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||
<a class="dropdown-item" href="#">(prev period)</a>
|
||||
<a class="dropdown-item" href="#">(next period)</a>
|
||||
<a class="dropdown-item" href="#">(this week?)</a>
|
||||
<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 type="hidden"
|
||||
<input v-on="inputEvents.start"
|
||||
:class="isDragging ? 'text-gray-600' : 'text-gray-900'"
|
||||
:value="inputValue.start"
|
||||
v-on="inputEvents.start"
|
||||
type="hidden"
|
||||
/>
|
||||
<input type="hidden"
|
||||
<input v-on="inputEvents.end"
|
||||
:class="isDragging ? 'text-gray-600' : 'text-gray-900'"
|
||||
:value="inputValue.end"
|
||||
v-on="inputEvents.end"
|
||||
type="hidden"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -76,28 +74,109 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
export default {
|
||||
name: "Calendar",
|
||||
created() {
|
||||
// this.locale = localStorage.locale ?? 'en-US';
|
||||
// this.$store.commit('increment');
|
||||
// console.log(this.$store.state.count);
|
||||
// get dates for current period (history button):
|
||||
// get dates for optional periods (dropdown) + descriptions.
|
||||
this.ready = true;
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
locale: 'en-US',
|
||||
ready: false,
|
||||
range: {
|
||||
start: new Date(window.sessionStart),
|
||||
end: new Date(window.sessionEnd),
|
||||
start: null,
|
||||
end: null,
|
||||
},
|
||||
defaultRange: {
|
||||
start: new Date(window.sessionStart),
|
||||
end: new Date(window.sessionEnd),
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
},
|
||||
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.periods = [];
|
||||
// create periods.
|
||||
// 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
|
||||
},
|
||||
range: function (value) {
|
||||
//console.log('User updated range');
|
||||
this.setStart(value.start);
|
||||
this.setEnd(value.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -62,8 +62,5 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "Dashboard",
|
||||
created() {},
|
||||
computed: {},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
@@ -24,35 +24,93 @@
|
||||
<h3 class="card-title">{{ $t('firefly.yourAccounts') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<canvas id="mainAccountsChart" style="min-height: 400px; height: 400px; max-height: 400px; max-width: 100%;"></canvas>
|
||||
<div v-if="!loading">
|
||||
<MainAccountChart v-if="!loading && !error" :chart-data="dataCollection" :options="chartOptions"/>
|
||||
</div>
|
||||
<div v-if="loading && !error" class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
<div v-if="error" class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./accounts/asset" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_asset_accounts') }}</a>
|
||||
<a class="btn btn-default button-sm" href="./accounts/asset"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_asset_accounts') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import DataConverter from "../charts/DataConverter";
|
||||
import DefaultLineOptions from "../charts/DefaultLineOptions";
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
import MainAccountChart from "./MainAccountChart";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
export default {
|
||||
name: "MainAccount",
|
||||
components: {MainAccountChart},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
error: false,
|
||||
ready: false,
|
||||
dataCollection: {},
|
||||
chartOptions: {}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
axios.get('./api/v1/chart/account/overview?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
.then(response => {
|
||||
|
||||
let chartData = DataConverter.methods.convertChart(response.data);
|
||||
chartData = DataConverter.methods.colorizeLineData(chartData);
|
||||
let lineChartCanvas = $('#mainAccountsChart').get(0).getContext('2d');
|
||||
new Chart(lineChartCanvas, {
|
||||
type: 'line',
|
||||
data: chartData,
|
||||
options: DefaultLineOptions.methods.getDefaultOptions()
|
||||
this.ready = true;
|
||||
this.chartOptions = DefaultLineOptions.methods.getDefaultOptions();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'start',
|
||||
'end'
|
||||
]),
|
||||
'datesReady': function () {
|
||||
return null !== this.start && null !== this.end && this.ready;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
datesReady: function (value) {
|
||||
if (true === value) {
|
||||
// console.log(this.chartOptions);
|
||||
this.initialiseChart();
|
||||
}
|
||||
},
|
||||
start: function () {
|
||||
this.initialiseChart();
|
||||
},
|
||||
end: function () {
|
||||
this.initialiseChart();
|
||||
},
|
||||
},
|
||||
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 url = './api/v1/chart/account/overview?start=' + startStr + '&end=' + endStr;
|
||||
// console.log('URL is ' + url);
|
||||
axios.get(url)
|
||||
.then(response => {
|
||||
let chartData = DataConverter.methods.convertChart(response.data);
|
||||
chartData = DataConverter.methods.colorizeLineData(chartData);
|
||||
this.dataCollection = chartData;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
// console.log('Has error!');
|
||||
// console.log(error);
|
||||
this.error = true;
|
||||
// console.error(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
<!--
|
||||
- MainAccountChart.vue
|
||||
- Copyright (c) 2020 james@firefly-iii.org
|
||||
@@ -20,21 +19,17 @@
|
||||
-->
|
||||
|
||||
<script>
|
||||
import {Line} from 'vue-chartjs'
|
||||
|
||||
export default {
|
||||
extends: Line,
|
||||
props: ['options', 'chartData'],
|
||||
import {Line, mixins} from 'vue-chartjs'
|
||||
|
||||
mounted() {
|
||||
// this.chartData is created in the mixin.
|
||||
// If you want to pass options please create a local options object
|
||||
this.renderChart(this.chartData, this.options)
|
||||
}
|
||||
}
|
||||
const {reactiveProp} = mixins
|
||||
|
||||
export default {
|
||||
extends: Line,
|
||||
mixins: [reactiveProp],
|
||||
props: ['options'],
|
||||
mounted() {
|
||||
this.renderChart(this.chartData, this.options)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
@@ -19,17 +19,54 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="row">
|
||||
<div v-bind:class="{ 'col-lg-12': 1 === accounts.length, 'col-lg-6': 2 === accounts.length, 'col-lg-4': accounts.length > 2 }"
|
||||
v-for="account in accounts">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><a :href="account.uri">{{ account.title }}</a></h3>
|
||||
<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">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<transaction-list-large :transactions="account.transactions" v-if="1===accounts.length" :account_id="account.id"/>
|
||||
<transaction-list-medium :transactions="account.transactions" v-if="2===accounts.length" :account_id="account.id"/>
|
||||
<transaction-list-small :transactions="account.transactions" v-if="accounts.length > 2" :account_id="account.id"/>
|
||||
</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">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</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>
|
||||
<transaction-list-large v-if="1===accounts.length" :account_id="account.id" :transactions="account.transactions"/>
|
||||
<transaction-list-medium v-if="2===accounts.length" :account_id="account.id" :transactions="account.transactions"/>
|
||||
<transaction-list-small v-if="accounts.length > 2" :account_id="account.id" :transactions="account.transactions"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -37,53 +74,101 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
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() {
|
||||
axios.get('./api/v1/preferences/frontpageAccounts')
|
||||
.then(response => {
|
||||
this.loadAccounts(response);
|
||||
}
|
||||
);
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
this.ready = true;
|
||||
},
|
||||
methods:
|
||||
{
|
||||
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: '',
|
||||
uri: '',
|
||||
transactions: []
|
||||
});
|
||||
this.loadSingleAccount(key, accountIds[key]);
|
||||
}
|
||||
}
|
||||
},
|
||||
loadSingleAccount(key, accountId) {
|
||||
axios.get('./api/v1/accounts/' + accountId)
|
||||
.then(response => {
|
||||
this.accounts[key].title = response.data.data.attributes.name;
|
||||
this.accounts[key].uri = './accounts/show/' + response.data.data.id;
|
||||
this.loadTransactions(key, accountId);
|
||||
}
|
||||
);
|
||||
},
|
||||
loadTransactions(key, accountId) {
|
||||
axios.get('./api/v1/accounts/' + accountId + '/transactions?page=1&limit=10')
|
||||
.then(response => {
|
||||
this.accounts[key].transactions = response.data.data;
|
||||
}
|
||||
);
|
||||
},
|
||||
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: '',
|
||||
current_balance: '',
|
||||
currency_code: 'EUR',
|
||||
transactions: []
|
||||
});
|
||||
this.loadSingleAccount(key, accountIds[key]);
|
||||
}
|
||||
}
|
||||
},
|
||||
loadSingleAccount(key, accountId) {
|
||||
axios.get('./api/v1/accounts/' + accountId)
|
||||
.then(response => {
|
||||
this.accounts[key].title = response.data.data.attributes.name;
|
||||
this.accounts[key].url = './accounts/show/' + response.data.data.id;
|
||||
this.accounts[key].current_balance = response.data.data.attributes.current_balance;
|
||||
this.accounts[key].currency_code = response.data.data.attributes.currency_code;
|
||||
|
||||
this.loadTransactions(key, accountId);
|
||||
}
|
||||
);
|
||||
},
|
||||
loadTransactions(key, accountId) {
|
||||
let startStr = this.start.toISOString().split('T')[0];
|
||||
let endStr = this.end.toISOString().split('T')[0];
|
||||
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>
|
||||
|
||||
|
@@ -23,7 +23,20 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.bills') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<!-- body if loading -->
|
||||
<div v-if="loading && !error" class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if error -->
|
||||
<div v-if="error" class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</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>
|
||||
@@ -34,47 +47,101 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="bill in this.bills">
|
||||
<td><a :href="'./bills/show' + bill.id" :title="bill.attributes.name">{{ bill.attributes.name }}</a>
|
||||
~{{
|
||||
Intl.NumberFormat(locale, {style: 'currency', currency: bill.attributes.currency_code}).format((parseFloat(bill.attributes.amount_min) +
|
||||
parseFloat(bill.attributes.amount_max)) / 2)
|
||||
}}
|
||||
<br />
|
||||
<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="payDate in bill.attributes.pay_dates">
|
||||
{{ new Intl.DateTimeFormat(locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(new Date(payDate)) }}
|
||||
<br />
|
||||
</span>
|
||||
<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 href="./bills" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_bills') }}</a>
|
||||
<a class="btn btn-default button-sm" href="./bills"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_bills') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
export default {
|
||||
name: "MainBillsList",
|
||||
computed: {
|
||||
locale() {
|
||||
return this.$store.getters.locale;
|
||||
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() {
|
||||
axios.get('./api/v1/bills?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
.then(response => {
|
||||
this.loadBills(response.data.data);
|
||||
}
|
||||
);
|
||||
this.ready = true;
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
},
|
||||
components: {},
|
||||
methods: {
|
||||
loadBills(data) {
|
||||
initialiseBills: function () {
|
||||
this.loading = true;
|
||||
this.bills = [];
|
||||
let startStr = this.start.toISOString().split('T')[0];
|
||||
let endStr = this.end.toISOString().split('T')[0];
|
||||
|
||||
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) {
|
||||
|
||||
@@ -85,12 +152,9 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.error = false;
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
bills: []
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -1,59 +0,0 @@
|
||||
<!--
|
||||
- MainBudgetChart.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.budgets') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div style="position: relative;">
|
||||
<canvas id="mainBudgetChart" style="min-height: 400px; height: 400px; max-height: 400px; max-width: 100%;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./budgets" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_budgets') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DefaultBarOptions from "../charts/DefaultBarOptions";
|
||||
import DataConverter from "../charts/DataConverter";
|
||||
export default {
|
||||
name: "MainBudget",
|
||||
created() {
|
||||
axios.get('./api/v1/chart/budget/overview?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
.then(response => {
|
||||
let chartData = DataConverter.methods.convertChart(response.data);
|
||||
let stackedBarChartCanvas = $('#mainBudgetChart').get(0).getContext('2d')
|
||||
new Chart(stackedBarChartCanvas, {
|
||||
type: 'bar',
|
||||
data: chartData,
|
||||
options: DefaultBarOptions.methods.getDefaultOptions()
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -21,42 +21,55 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- daily budgets (will be the exception, I expect) -->
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12" v-if="budgetLimits.daily.length > 0">
|
||||
<BudgetListGroup :title="$t('firefly.daily_budgets')" :budgetLimits=budgetLimits.daily
|
||||
<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 class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12" v-if="budgetLimits.weekly.length > 0">
|
||||
<BudgetListGroup :title="$t('firefly.weekly_budgets')" :budgetLimits=budgetLimits.weekly
|
||||
<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 class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12" v-if="budgetLimits.monthly.length > 0">
|
||||
<BudgetListGroup :title="$t('firefly.monthly_budgets')" :budgetLimits=budgetLimits.monthly
|
||||
<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 class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12" v-if="budgetLimits.quarterly.length > 0">
|
||||
<BudgetListGroup :title="$t('firefly.quarterly_budgets')" :budgetLimits=budgetLimits.quarterly
|
||||
<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 class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12" v-if="budgetLimits.half_year.length > 0">
|
||||
<BudgetListGroup :title="$t('firefly.half_year_budgets')" :budgetLimits=budgetLimits.half_year
|
||||
<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 class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12" v-if="budgetLimits.yearly.length > 0">
|
||||
<BudgetListGroup :title="$t('firefly.yearly_budgets')" :budgetLimits=budgetLimits.yearly
|
||||
<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 class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12" v-if="budgetLimits.other.length > 0 || rawBudgets.length > 0">
|
||||
<BudgetListGroup :title="$t('firefly.other_budgets')" :budgetLimits=budgetLimits.other :budgets="rawBudgets"
|
||||
<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">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BudgetListGroup from "./BudgetListGroup";
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
export default {
|
||||
name: "MainBudgetList",
|
||||
@@ -76,19 +89,59 @@ export default {
|
||||
budgets: {},
|
||||
rawBudgets: [],
|
||||
locale: 'en-US',
|
||||
ready: false,
|
||||
loading: true,
|
||||
error: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.ready = true;
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
this.collectData();
|
||||
},
|
||||
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:
|
||||
{
|
||||
collectData() {
|
||||
this.getBudgets();
|
||||
},
|
||||
getBudgets() {
|
||||
axios.get('./api/v1/budgets?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
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];
|
||||
axios.get('./api/v1/budgets?start=' + startStr + '&end=' + endStr)
|
||||
.then(response => {
|
||||
this.parseBudgets(response.data);
|
||||
}
|
||||
@@ -117,12 +170,13 @@ export default {
|
||||
}
|
||||
this.getBudgetLimits();
|
||||
},
|
||||
|
||||
|
||||
getBudgetLimits() {
|
||||
axios.get('./api/v1/budgets/limits?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
let startStr = this.start.toISOString().split('T')[0];
|
||||
let endStr = this.end.toISOString().split('T')[0];
|
||||
axios.get('./api/v1/budget-limits?start=' + startStr + '&end=' + endStr)
|
||||
.then(response => {
|
||||
this.parseBudgetLimits(response.data);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
@@ -174,53 +228,8 @@ export default {
|
||||
|
||||
let period = data.data[key].attributes.period ?? 'other';
|
||||
this.budgetLimits[period].push(obj);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// // loop budgets (and do what?)
|
||||
// for (let key in data.included) {
|
||||
// if (data.included.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
||||
// let obj = {
|
||||
// name: data.included[key].attributes.name,
|
||||
// id: data.included[key].id,
|
||||
// };
|
||||
// this.budgets[data.included[key].id] = obj;
|
||||
// }
|
||||
// }
|
||||
|
||||
// loop budget limits:
|
||||
// for (let key in data.data) {
|
||||
// if (data.data.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
||||
// let pctGreen = 0;
|
||||
// let pctOrange = 0;
|
||||
// let pctRed = 0;
|
||||
//
|
||||
|
||||
//
|
||||
// let obj = {
|
||||
// id: data.data[key].id,
|
||||
// amount: data.data[key].attributes.amount,
|
||||
// budget_id: data.data[key].attributes.budget_id,
|
||||
// currency_id: data.data[key].attributes.currency_id,
|
||||
// currency_code: data.data[key].attributes.currency_code,
|
||||
// period: data.data[key].attributes.period,
|
||||
// start: new Date(data.data[key].attributes.start),
|
||||
// end: new Date(data.data[key].attributes.end),
|
||||
// spent: data.data[key].attributes.spent,
|
||||
// pctGreen: pctGreen,
|
||||
// pctOrange: pctOrange,
|
||||
// pctRed: pctRed,
|
||||
// };
|
||||
//
|
||||
//
|
||||
//
|
||||
// let period = data.data[key].attributes.period ?? 'other';
|
||||
// this.budgetLimits[period].push(obj);
|
||||
// }
|
||||
// }
|
||||
|
||||
},
|
||||
filterBudgets(budgetId, currencyId) {
|
||||
for (let key in this.rawBudgets) {
|
||||
|
@@ -1,60 +0,0 @@
|
||||
<!--
|
||||
- MainCategoryChart.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>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<canvas id="mainCategoryChart" style="min-height: 400px; height: 400px; max-height: 400px; max-width: 100%;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./categories" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_categories') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DefaultBarOptions from "../charts/DefaultBarOptions";
|
||||
import DataConverter from "../charts/DataConverter";
|
||||
export default {
|
||||
name: "MainCategory",
|
||||
created() {
|
||||
axios.get('./api/v1/chart/category/overview?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
.then(response => {
|
||||
let chartData = DataConverter.methods.convertChart(response.data);
|
||||
chartData = DataConverter.methods.colorizeLineData(chartData);
|
||||
let stackedBarChartCanvas = $('#mainCategoryChart').get(0).getContext('2d')
|
||||
new Chart(stackedBarChartCanvas, {
|
||||
type: 'bar',
|
||||
data: chartData,
|
||||
options: DefaultBarOptions.methods.getDefaultOptions()
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -23,22 +23,32 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.categories') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<!-- body if loading -->
|
||||
<div v-if="loading && !error" class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if error -->
|
||||
<div v-if="error" class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if normal -->
|
||||
<div v-if="!loading && !error" class="card-body table-responsive p-0">
|
||||
<table class="table table-sm">
|
||||
<tbody>
|
||||
<tr v-for="category in sortedList">
|
||||
<td style="width:20%;">
|
||||
<a :href="'./categories/show/' + category.id">{{ category.name }}</a>
|
||||
<!--<p>Spent: {{ category.spentPct }}</p>
|
||||
<p>earned: {{ category.earnedPct }}</p>
|
||||
-->
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<!-- SPENT -->
|
||||
<div class="progress" v-if="category.spentPct > 0">
|
||||
<div class="progress-bar progress-bar-striped bg-danger" role="progressbar" :aria-valuenow="category.spentPct"
|
||||
:style="{ width: category.spentPct + '%'}" aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
<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>
|
||||
@@ -49,12 +59,13 @@
|
||||
</div>
|
||||
|
||||
<!-- EARNED -->
|
||||
<div class="progress justify-content-end" v-if="category.earnedPct > 0" title="hello2">
|
||||
<div v-if="category.earnedPct > 0" class="progress justify-content-end" title="hello2">
|
||||
<span v-if="category.earnedPct <= 20">
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: category.currency_code}).format(category.earned) }}
|
||||
</span>
|
||||
<div class="progress-bar progress-bar-striped bg-success" role="progressbar" :aria-valuenow="category.earnedPct" :style="{ width: category.earnedPct + '%'}" aria-valuemin="0"
|
||||
aria-valuemax="100" title="hello">
|
||||
<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>
|
||||
@@ -70,12 +81,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
export default {
|
||||
name: "MainCategoryList",
|
||||
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
this.getCategories();
|
||||
this.ready = true;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -83,17 +98,55 @@ export default {
|
||||
categories: [],
|
||||
sortedList: [],
|
||||
spent: 0,
|
||||
earned: 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() {
|
||||
axios.get('./api/v1/categories?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
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];
|
||||
axios.get('./api/v1/categories?start=' + startStr + '&end=' + endStr)
|
||||
.then(response => {
|
||||
this.parseCategories(response.data);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
).catch(error => {
|
||||
this.error = true;
|
||||
});
|
||||
},
|
||||
parseCategories(data) {
|
||||
for (let key in data.data) {
|
||||
|
@@ -1,56 +0,0 @@
|
||||
<!--
|
||||
- MainCrebitChart.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">
|
||||
<!-- debit = expense -->
|
||||
<h3 class="card-title">{{ $t('firefly.income') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<transaction-list-small :transactions="this.transactions" />
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./accounts/revenue" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_deposits') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MainCredit",
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
transactions: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
axios.get('./api/v1/transactions?type=deposit&limit=10&start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
.then(response => {
|
||||
this.transactions = response.data.data;
|
||||
}
|
||||
);
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
computed: {},
|
||||
}
|
||||
</script>
|
@@ -23,16 +23,29 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.revenue_accounts') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<!-- body if loading -->
|
||||
<div v-if="loading && !error" class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if error -->
|
||||
<div v-if="error" class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if normal -->
|
||||
<div v-if="!loading && !error" class="card-body table-responsive p-0">
|
||||
<table class="table table-sm">
|
||||
<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 class="progress" v-if="entry.pct > 0">
|
||||
<div class="progress-bar progress-bar-striped bg-success" role="progressbar" :aria-valuenow="entry.pct"
|
||||
:style="{ width: entry.pct + '%'}" aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
<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 progress-bar-striped bg-success"
|
||||
role="progressbar">
|
||||
<span v-if="entry.pct > 20">
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float) }}
|
||||
</span>
|
||||
@@ -47,34 +60,76 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./transactions/deposit" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_deposits') }}</a>
|
||||
<a class="btn btn-default button-sm" href="./transactions/deposit"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_deposits') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
|
||||
export default {
|
||||
name: "MainCreditList",
|
||||
data() {
|
||||
return {
|
||||
locale: 'en-US',
|
||||
income: [],
|
||||
max: 0
|
||||
max: 0,
|
||||
loading: true,
|
||||
error: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
this.getExpenses();
|
||||
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: {
|
||||
getExpenses() {
|
||||
axios.get('./api/v1/insight/income/date/basic?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
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];
|
||||
axios.get('./api/v1/insight/income/revenue?start=' + startStr + '&end=' + endStr)
|
||||
.then(response => {
|
||||
// do something with response.
|
||||
this.parseExpenses(response.data);
|
||||
});
|
||||
this.parseIncome(response.data);
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
this.error = true
|
||||
});
|
||||
},
|
||||
parseExpenses(data) {
|
||||
parseIncome(data) {
|
||||
for (let mainKey in data) {
|
||||
if (data.hasOwnProperty(mainKey) && /^0$|^[1-9]\d*$/.test(mainKey) && mainKey <= 4294967294) {
|
||||
// contains currency info and entries.
|
||||
@@ -83,12 +138,11 @@ export default {
|
||||
this.max = data[mainKey].difference_float;
|
||||
current.pct = 100;
|
||||
}
|
||||
if(0 !== parseInt(mainKey)) {
|
||||
if (0 !== parseInt(mainKey)) {
|
||||
// calc percentage:
|
||||
current.pct = (data[mainKey].difference_float / this.max) * 100;
|
||||
}
|
||||
this.income.push(current);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,62 +0,0 @@
|
||||
<!--
|
||||
- MainDebit.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">
|
||||
<!-- debit = expense -->
|
||||
<h3 class="card-title">{{ $t('firefly.expense_accounts') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<canvas id="mainDebitChart" style="min-height: 400px; height: 400px; max-height: 400px; max-width: 100%;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./transactions/withdrawals" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_expenses') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DefaultBarOptions from "../charts/DefaultBarOptions";
|
||||
import DataConverter from "../charts/DataConverter";
|
||||
|
||||
export default {
|
||||
name: "MainDebit",
|
||||
created() {
|
||||
axios.get('./api/v1/chart/account/expense?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
.then(response => {
|
||||
let chartData = DataConverter.methods.convertChart(response.data);
|
||||
chartData = DataConverter.methods.colorizeLineData(chartData);
|
||||
let stackedBarChartCanvas = $('#mainDebitChart').get(0).getContext('2d')
|
||||
new Chart(stackedBarChartCanvas, {
|
||||
type: 'bar',
|
||||
data: chartData,
|
||||
options: DefaultBarOptions.methods.getDefaultOptions()
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -23,16 +23,29 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.expense_accounts') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<!-- body if loading -->
|
||||
<div v-if="loading && !error" class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if error -->
|
||||
<div v-if="error" class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if normal -->
|
||||
<div v-if="!loading && !error" class="card-body table-responsive p-0">
|
||||
<table class="table table-sm">
|
||||
<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 class="progress" v-if="entry.pct > 0">
|
||||
<div class="progress-bar progress-bar-striped bg-danger" role="progressbar" :aria-valuenow="entry.pct"
|
||||
:style="{ width: entry.pct + '%'}" aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
<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 progress-bar-striped bg-danger"
|
||||
role="progressbar">
|
||||
<span v-if="entry.pct > 20">
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float) }}
|
||||
</span>
|
||||
@@ -47,32 +60,74 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./transactions/withdrawal" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_withdrawals') }}</a>
|
||||
<a class="btn btn-default button-sm" href="./transactions/withdrawal"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_withdrawals') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
|
||||
export default {
|
||||
name: "MainDebitList",
|
||||
data() {
|
||||
return {
|
||||
locale: 'en-US',
|
||||
expenses: [],
|
||||
max: 0
|
||||
max: 0,
|
||||
loading: true,
|
||||
error: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
this.getExpenses();
|
||||
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() {
|
||||
axios.get('./api/v1/insight/expense/date/basic?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
this.expenses = [];
|
||||
let startStr = this.start.toISOString().split('T')[0];
|
||||
let endStr = this.end.toISOString().split('T')[0];
|
||||
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) {
|
||||
@@ -83,7 +138,7 @@ export default {
|
||||
this.max = data[mainKey].difference_float;
|
||||
current.pct = 100;
|
||||
}
|
||||
if(0 !== parseInt(mainKey)) {
|
||||
if (0 !== parseInt(mainKey)) {
|
||||
// calc percentage:
|
||||
current.pct = (data[mainKey].difference_float / this.max) * 100;
|
||||
}
|
||||
|
@@ -23,7 +23,21 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.piggy_banks') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
|
||||
<!-- body if loading -->
|
||||
<div v-if="loading && !error" class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if error -->
|
||||
<div v-if="error" class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</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>
|
||||
@@ -34,7 +48,8 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="piggy in this.piggy_banks">
|
||||
<td>{{ piggy.attributes.name }}
|
||||
<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 }}
|
||||
@@ -43,8 +58,9 @@
|
||||
<td>
|
||||
<div class="progress-group">
|
||||
<div class="progress progress-sm">
|
||||
<div class="progress-bar progress-bar-striped primary" v-if="piggy.attributes.pct < 100" :style="{'width': piggy.attributes.pct + '%'}"></div>
|
||||
<div class="progress-bar progress-bar-striped bg-success" v-if="100 === piggy.attributes.pct" :style="{'width': piggy.attributes.pct + '%'}"></div>
|
||||
<div v-if="piggy.attributes.pct < 100" :style="{'width': piggy.attributes.pct + '%'}" class="progress-bar progress-bar-striped 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">
|
||||
@@ -65,7 +81,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./piggy-banks" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_piggies') }}</a>
|
||||
<a class="btn btn-default button-sm" href="./piggy-banks"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_piggies') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -73,17 +89,24 @@
|
||||
<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;
|
||||
}
|
||||
);
|
||||
},
|
||||
computed: {
|
||||
locale() {
|
||||
return this.$store.getters.locale;
|
||||
}
|
||||
).catch(error => {
|
||||
this.error = true
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
loadPiggyBanks(data) {
|
||||
@@ -100,11 +123,6 @@ export default {
|
||||
return b.attributes.pct - a.attributes.pct;
|
||||
});
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
piggy_banks: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -25,9 +25,11 @@
|
||||
<span class="info-box-icon"><i class="far fa-bookmark text-info"></i></span>
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">{{ $t("firefly.balance") }}</span>
|
||||
<span v-if="!loading && !error" class="info-box-text">{{ $t("firefly.balance") }}</span>
|
||||
<span v-if="loading && !error" class="info-box-text"><i class="fas fa-spinner fa-spin"></i></span>
|
||||
<span v-if="error" class="info-box-text"><i class="fas fa-exclamation-triangle text-danger"></i></span>
|
||||
<!-- balance in preferred currency -->
|
||||
<span class="info-box-number" v-for="balance in prefCurrencyBalances" :title="balance.sub_title">{{ balance.value_parsed }}</span>
|
||||
<span v-for="balance in prefCurrencyBalances" :title="balance.sub_title" class="info-box-number">{{ balance.value_parsed }}</span>
|
||||
|
||||
<div class="progress bg-info">
|
||||
<div class="progress-bar" style="width: 0"></div>
|
||||
@@ -48,10 +50,11 @@
|
||||
<span class="info-box-icon"><i class="far fa-calendar-alt text-teal"></i></span>
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">{{ $t('firefly.bills_to_pay') }}</span>
|
||||
|
||||
<span v-if="!loading && !error" class="info-box-text">{{ $t('firefly.bills_to_pay') }}</span>
|
||||
<span v-if="loading && !error" class="info-box-text"><i class="fas fa-spinner fa-spin"></i></span>
|
||||
<span v-if="error" class="info-box-text"><i class="fas fa-exclamation-triangle text-danger"></i></span>
|
||||
<!-- bills unpaid, in preferred currency. -->
|
||||
<span class="info-box-number" v-for="balance in prefBillsUnpaid">{{ balance.value_parsed }}</span>
|
||||
<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>
|
||||
@@ -72,10 +75,11 @@
|
||||
<span class="info-box-icon"><i class="fas fa-money-bill text-success"></i></span>
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">{{ $t('firefly.left_to_spend') }}</span>
|
||||
|
||||
<span v-if="!loading && !error" class="info-box-text">{{ $t('firefly.left_to_spend') }}</span>
|
||||
<span v-if="loading && !error" class="info-box-text"><i class="fas fa-spinner fa-spin"></i></span>
|
||||
<span v-if="error" class="info-box-text"><i class="fas fa-exclamation-triangle text-danger"></i></span>
|
||||
<!-- left to spend in preferred currency -->
|
||||
<span class="info-box-number" v-for="left in prefLeftToSpend" :title="left.sub_title">{{ left.value_parsed }}</span>
|
||||
<span v-for="left in prefLeftToSpend" :title="left.sub_title" class="info-box-number">{{ left.value_parsed }}</span>
|
||||
|
||||
<div class="progress bg-success">
|
||||
<div class="progress-bar" style="width: 0"></div>
|
||||
@@ -97,8 +101,10 @@
|
||||
<span class="info-box-icon"><i class="fas fa-money-bill text-success"></i></span>
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text"><span>{{ $t('firefly.net_worth') }}</span></span>
|
||||
<span class="info-box-number" v-for="nw in prefNetWorth" :title="nw.sub_title">{{ nw.value_parsed }}</span>
|
||||
<span v-if="!loading && !error" class="info-box-text">{{ $t('firefly.net_worth') }}</span>
|
||||
<span v-if="loading && !error" class="info-box-text"><i class="fas fa-spinner fa-spin"></i></span>
|
||||
<span v-if="error" class="info-box-text"><i class="fas fa-exclamation-triangle text-danger"></i></span>
|
||||
<span v-for="nw in prefNetWorth" :title="nw.sub_title" class="info-box-number">{{ nw.value_parsed }}</span>
|
||||
|
||||
<div class="progress bg-success">
|
||||
<div class="progress-bar" style="width: 0"></div>
|
||||
@@ -117,6 +123,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
export default {
|
||||
name: "TopBoxes",
|
||||
props: {},
|
||||
@@ -128,9 +137,19 @@ export default {
|
||||
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 () {
|
||||
@@ -170,8 +189,25 @@ export default {
|
||||
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.prepareComponent();
|
||||
this.ready = true;
|
||||
},
|
||||
methods: {
|
||||
filterOnCurrency(array) {
|
||||
@@ -205,11 +241,24 @@ export default {
|
||||
* Prepare the component.
|
||||
*/
|
||||
prepareComponent() {
|
||||
axios.get('./api/v1/summary/basic?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
this.error = false;
|
||||
this.loading = true;
|
||||
this.summary = [];
|
||||
this.balances = [];
|
||||
this.billsPaid = [];
|
||||
this.billsUnpaid = [];
|
||||
this.leftToSpend = [];
|
||||
this.netWorth = [];
|
||||
let startStr = this.start.toISOString().split('T')[0];
|
||||
let endStr = this.end.toISOString().split('T')[0];
|
||||
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();
|
||||
|
@@ -21,85 +21,85 @@
|
||||
<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-toggle="collapse" data-parent="#accordion" 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 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>
|
||||
<div class="card card-secondary">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" 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 class="row">
|
||||
<div class="col-lg-4">
|
||||
A
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-secondary">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion" 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 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>
|
||||
|
37
frontend/src/components/partials/Alert.vue
Normal file
37
frontend/src/components/partials/Alert.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<!--
|
||||
- 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>
|
||||
<i v-if="'danger' === type" class="icon fas fa-ban"></i>
|
||||
<i v-if="'success' === type" class="icon fas fa-thumbs-up"></i>
|
||||
<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>
|
166
frontend/src/components/shared/transactions.js
vendored
Normal file
166
frontend/src/components/shared/transactions.js
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* transactions.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/>.
|
||||
*/
|
||||
|
||||
export function getDefaultErrors() {
|
||||
return {
|
||||
description: [],
|
||||
amount: [],
|
||||
source: [],
|
||||
destination: [],
|
||||
currency: [],
|
||||
foreign_currency: [],
|
||||
foreign_amount: [],
|
||||
date: [],
|
||||
custom_dates: [],
|
||||
budget: [],
|
||||
category: [],
|
||||
bill: [],
|
||||
tags: [],
|
||||
piggy_bank: [],
|
||||
internal_reference: [],
|
||||
external_url: [],
|
||||
notes: [],
|
||||
location: []
|
||||
};
|
||||
}
|
||||
|
||||
export function getDefaultTransaction() {
|
||||
return {
|
||||
// basic
|
||||
description: '',
|
||||
transaction_journal_id: 0,
|
||||
// accounts:
|
||||
source_account_id: null,
|
||||
source_account_name: null,
|
||||
source_account_type: null,
|
||||
|
||||
source_account_currency_id: null,
|
||||
source_account_currency_code: null,
|
||||
source_account_currency_symbol: null,
|
||||
|
||||
destination_account_id: null,
|
||||
destination_account_name: null,
|
||||
destination_account_type: null,
|
||||
|
||||
destination_account_currency_id: null,
|
||||
destination_account_currency_code: null,
|
||||
destination_account_currency_symbol: null,
|
||||
|
||||
source_account: {
|
||||
id: 0,
|
||||
name: "",
|
||||
name_with_balance: "",
|
||||
type: "",
|
||||
currency_id: 0,
|
||||
currency_name: '',
|
||||
currency_code: '',
|
||||
currency_decimal_places: 2
|
||||
},
|
||||
destination_account: {
|
||||
id: 0,
|
||||
name: "",
|
||||
type: "",
|
||||
currency_id: 0,
|
||||
currency_name: '',
|
||||
currency_code: '',
|
||||
currency_decimal_places: 2
|
||||
},
|
||||
|
||||
// amount:
|
||||
amount: '',
|
||||
currency_id: 0,
|
||||
foreign_amount: '',
|
||||
foreign_currency_id: 0,
|
||||
|
||||
// meta data
|
||||
category: null,
|
||||
budget_id: 0,
|
||||
bill_id: 0,
|
||||
piggy_bank_id: 0,
|
||||
tags: [],
|
||||
|
||||
// optional date fields (6x):
|
||||
interest_date: null,
|
||||
book_date: null,
|
||||
process_date: null,
|
||||
due_date: null,
|
||||
payment_date: null,
|
||||
invoice_date: null,
|
||||
|
||||
// optional other fields:
|
||||
internal_reference: null,
|
||||
external_url: null,
|
||||
external_id: null,
|
||||
notes: null,
|
||||
|
||||
// transaction links:
|
||||
links: [],
|
||||
attachments: [],
|
||||
// location:
|
||||
zoom_level: null,
|
||||
longitude: null,
|
||||
latitude: null,
|
||||
|
||||
// error handling
|
||||
errors: {},
|
||||
}
|
||||
}
|
||||
|
||||
export function toW3CString(date) {
|
||||
// https://gist.github.com/tristanlins/6585391
|
||||
let year = date.getFullYear();
|
||||
let month = date.getMonth();
|
||||
month++;
|
||||
if (month < 10) {
|
||||
month = '0' + month;
|
||||
}
|
||||
let day = date.getDate();
|
||||
if (day < 10) {
|
||||
day = '0' + day;
|
||||
}
|
||||
let hours = date.getHours();
|
||||
if (hours < 10) {
|
||||
hours = '0' + hours;
|
||||
}
|
||||
let minutes = date.getMinutes();
|
||||
if (minutes < 10) {
|
||||
minutes = '0' + minutes;
|
||||
}
|
||||
let seconds = date.getSeconds();
|
||||
if (seconds < 10) {
|
||||
seconds = '0' + seconds;
|
||||
}
|
||||
let offset = -date.getTimezoneOffset();
|
||||
let offsetHours = Math.abs(Math.floor(offset / 60));
|
||||
let offsetMinutes = Math.abs(offset) - offsetHours * 60;
|
||||
if (offsetHours < 10) {
|
||||
offsetHours = '0' + offsetHours;
|
||||
}
|
||||
if (offsetMinutes < 10) {
|
||||
offsetMinutes = '0' + offsetMinutes;
|
||||
}
|
||||
let offsetSign = '+';
|
||||
if (offset < 0) {
|
||||
offsetSign = '-';
|
||||
}
|
||||
return year + '-' + month + '-' + day +
|
||||
'T' + hours + ':' + minutes + ':' + seconds +
|
||||
offsetSign + offsetHours + ':' + offsetMinutes;
|
||||
}
|
23
frontend/src/components/store/index.js
vendored
23
frontend/src/components/store/index.js
vendored
@@ -21,6 +21,8 @@
|
||||
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';
|
||||
|
||||
Vue.use(Vuex)
|
||||
const debug = process.env.NODE_ENV !== 'production'
|
||||
@@ -31,7 +33,14 @@ export default new Vuex.Store(
|
||||
transactions: {
|
||||
namespaced: true,
|
||||
modules: {
|
||||
create: transactions_create
|
||||
create: transactions_create,
|
||||
edit: transactions_edit
|
||||
}
|
||||
},
|
||||
dashboard: {
|
||||
namespaced: true,
|
||||
modules: {
|
||||
index: dashboard_index
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -43,7 +52,7 @@ export default new Vuex.Store(
|
||||
},
|
||||
mutations: {
|
||||
setCurrencyPreference(state, payload) {
|
||||
console.log('setCurrencyPreference', payload);
|
||||
//console.log('setCurrencyPreference', payload);
|
||||
state.currencyPreference = payload.payload;
|
||||
},
|
||||
initialiseStore(state) {
|
||||
@@ -77,9 +86,9 @@ export default new Vuex.Store(
|
||||
actions: {
|
||||
updateCurrencyPreference(context) {
|
||||
if (localStorage.currencyPreference) {
|
||||
console.log('set from local storage.');
|
||||
console.log(localStorage.currencyPreference);
|
||||
console.log({payload: JSON.parse(localStorage.currencyPreference)});
|
||||
//console.log('set from local storage.');
|
||||
//console.log(localStorage.currencyPreference);
|
||||
//console.log({payload: JSON.parse(localStorage.currencyPreference)});
|
||||
context.commit('setCurrencyPreference', {payload: JSON.parse(localStorage.currencyPreference)});
|
||||
return;
|
||||
}
|
||||
@@ -93,8 +102,8 @@ export default new Vuex.Store(
|
||||
decimal_places: parseInt(response.data.data.attributes.decimal_places),
|
||||
};
|
||||
localStorage.currencyPreference = JSON.stringify(currencyResponse);
|
||||
console.log('getCurrencyPreference from server')
|
||||
console.log(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.');
|
||||
|
218
frontend/src/components/store/modules/dashboard/index.js
vendored
Normal file
218
frontend/src/components/store/modules/dashboard/index.js
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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
|
||||
const state = () => (
|
||||
{
|
||||
viewRange: 'default',
|
||||
start: null,
|
||||
end: null,
|
||||
// default range:
|
||||
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) {
|
||||
if ('default' === context.state.viewRange) {
|
||||
axios.get('./api/v1/preferences/viewRange')
|
||||
.then(response => {
|
||||
let viewRange = response.data.data.attributes.data;
|
||||
context.commit('setViewRange', viewRange);
|
||||
// call another action:
|
||||
context.dispatch('setDatesFromViewRange');
|
||||
}
|
||||
).catch(error => {
|
||||
// console.log(error);
|
||||
context.commit('setViewRange', '1M');
|
||||
// call another action:
|
||||
context.dispatch('setDatesFromViewRange');
|
||||
});
|
||||
}
|
||||
},
|
||||
setDatesFromViewRange(context) {
|
||||
// console.log('Must set dates from viewRange "' + context.state.viewRange + '"');
|
||||
// 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));
|
||||
}
|
||||
|
||||
if (null !== context.getters.end && null !== context.getters.start) {
|
||||
return;
|
||||
}
|
||||
let start;
|
||||
let end;
|
||||
let viewRange = context.getters.viewRange;
|
||||
// console.log('Will recreate view range on ' + viewRange);
|
||||
switch (viewRange) {
|
||||
case '1D':
|
||||
// one day:
|
||||
start = new Date;
|
||||
end = new Date(start.getTime());
|
||||
start.setHours(0, 0, 0, 0);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
break;
|
||||
case '1W':
|
||||
// this week:
|
||||
start = new Date;
|
||||
end = new Date(start.getTime());
|
||||
// start of week
|
||||
let diff = start.getDate() - start.getDay() + (start.getDay() === 0 ? -6 : 1);
|
||||
start.setDate(diff);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
|
||||
// end of week
|
||||
let lastday = end.getDate() - (end.getDay() - 1) + 6;
|
||||
end.setDate(lastday);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
break;
|
||||
case '1M':
|
||||
// this month:
|
||||
start = new Date;
|
||||
start = new Date(start.getFullYear(), start.getMonth(), 1);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
end = new Date(start.getFullYear(), start.getMonth() + 1, 0);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
break;
|
||||
case '3M':
|
||||
// this quarter
|
||||
start = new Date;
|
||||
end = new Date;
|
||||
let quarter = Math.floor((start.getMonth() + 3) / 3) - 1;
|
||||
// start and end months? I'm sure this could be better:
|
||||
let startMonths = [0, 3, 6, 9];
|
||||
let endMonths = [2, 5, 8, 11];
|
||||
// set start to the correct month, day one:
|
||||
start = new Date(start.getFullYear(), startMonths[quarter], 1);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
|
||||
// set end to the correct month, day one
|
||||
end = new Date(end.getFullYear(), endMonths[quarter], 1);
|
||||
// then to the last day of the month:
|
||||
end = new Date(end.getFullYear(), end.getMonth() + 1, 0);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
break;
|
||||
case '6M':
|
||||
// this half-year
|
||||
start = new Date;
|
||||
end = new Date;
|
||||
let half = start.getMonth() <= 5 ? 0 : 1;
|
||||
|
||||
let startHalf = [0, 6];
|
||||
let endHalf = [5, 11];
|
||||
// set start to the correct month, day one:
|
||||
start = new Date(start.getFullYear(), startHalf[half], 1);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
|
||||
// set end to the correct month, day one
|
||||
end = new Date(end.getFullYear(), endHalf[half], 1);
|
||||
// then to the last day of the month:
|
||||
end = new Date(end.getFullYear(), end.getMonth() + 1, 0);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
break;
|
||||
case '1Y':
|
||||
// this year
|
||||
start = new Date;
|
||||
end = new Date;
|
||||
start = new Date(start.getFullYear(), 0, 1);
|
||||
|
||||
end = new Date(end.getFullYear(), 11, 31);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
@@ -20,15 +20,13 @@
|
||||
|
||||
const lodashClonedeep = require('lodash.clonedeep');
|
||||
|
||||
import {getDefaultTransaction, getDefaultErrors} from '../../../shared/transactions';
|
||||
|
||||
// initial state
|
||||
const state = () => ({
|
||||
transactionType: 'any',
|
||||
date: new Date,
|
||||
groupTitle: '',
|
||||
transactions: [],
|
||||
allowedOpposingTypes: {},
|
||||
accountToTransaction: {},
|
||||
sourceAllowedTypes: ['Asset account', 'Loan', 'Debt', 'Mortgage', 'Revenue account'],
|
||||
destinationAllowedTypes: ['Asset account', 'Loan', 'Debt', 'Mortgage', 'Expense account'],
|
||||
customDateFields: {
|
||||
interest_date: false,
|
||||
book_date: false,
|
||||
@@ -37,60 +35,8 @@ const state = () => ({
|
||||
payment_date: false,
|
||||
invoice_date: false,
|
||||
},
|
||||
defaultTransaction: {
|
||||
// basic
|
||||
description: '',
|
||||
|
||||
// accounts:
|
||||
source_account: {
|
||||
id: 0,
|
||||
name: "",
|
||||
name_with_balance: "",
|
||||
type: "",
|
||||
currency_id: 0,
|
||||
currency_name: '',
|
||||
currency_code: '',
|
||||
currency_decimal_places: 2
|
||||
},
|
||||
destination_account: {
|
||||
id: 0,
|
||||
name: "",
|
||||
type: "",
|
||||
currency_id: 0,
|
||||
currency_name: '',
|
||||
currency_code: '',
|
||||
currency_decimal_places: 2
|
||||
},
|
||||
|
||||
// amount:
|
||||
amount: '',
|
||||
currency_id: 0,
|
||||
foreign_amount: '',
|
||||
foreign_currency_id: 0,
|
||||
|
||||
// meta data
|
||||
budget_id: 0,
|
||||
bill_id: 0,
|
||||
piggy_bank_id: 0,
|
||||
tags: [],
|
||||
|
||||
// optional date fields (6x):
|
||||
interest_date: null,
|
||||
book_date: null,
|
||||
process_date: null,
|
||||
due_date: null,
|
||||
payment_date: null,
|
||||
invoice_date: null,
|
||||
|
||||
// optional other fields:
|
||||
internal_reference: null,
|
||||
external_url: null,
|
||||
notes: null,
|
||||
|
||||
// transaction links:
|
||||
links: [],
|
||||
attachments: []
|
||||
},
|
||||
defaultTransaction: getDefaultTransaction(),
|
||||
defaultErrors: getDefaultErrors()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -100,12 +46,17 @@ const getters = {
|
||||
transactions: state => {
|
||||
return state.transactions;
|
||||
},
|
||||
date: state => {
|
||||
return state.date;
|
||||
groupTitle: state => {
|
||||
return state.groupTitle;
|
||||
},
|
||||
transactionType: state => {
|
||||
return state.transactionType;
|
||||
},
|
||||
accountToTransaction: state => {
|
||||
// TODO better architecture here, does not need the store.
|
||||
// possible API point!!
|
||||
return state.accountToTransaction;
|
||||
},
|
||||
defaultTransaction: state => {
|
||||
return state.defaultTransaction;
|
||||
},
|
||||
@@ -132,62 +83,35 @@ const getters = {
|
||||
}
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
calcTransactionType(context) {
|
||||
let source = context.state.transactions[0].source_account;
|
||||
let dest = context.state.transactions[0].destination_account;
|
||||
if (null === source || null === dest) {
|
||||
// console.log('transactionType any');
|
||||
context.commit('setTransactionType', 'any');
|
||||
return;
|
||||
}
|
||||
if ('' === source.type || '' === dest.type) {
|
||||
// console.log('transactionType any');
|
||||
context.commit('setTransactionType', 'any');
|
||||
return;
|
||||
}
|
||||
|
||||
// ok so type is set on both:
|
||||
let expectedDestinationTypes = context.state.accountToTransaction[source.type];
|
||||
if ('undefined' !== typeof expectedDestinationTypes) {
|
||||
let transactionType = expectedDestinationTypes[dest.type];
|
||||
if ('undefined' !== typeof expectedDestinationTypes[dest.type]) {
|
||||
// console.log('Found a type: ' + transactionType);
|
||||
context.commit('setTransactionType', transactionType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// console.log('Found no type for ' + source.type + ' --> ' + dest.type);
|
||||
if ('Asset account' !== source.type) {
|
||||
console.log('Drop ID from source. TODO');
|
||||
|
||||
// source.id =null
|
||||
// context.commit('updateField', {field: 'source_account',index: })
|
||||
// context.state.transactions[0].source_account.id = null;
|
||||
}
|
||||
if ('Asset account' !== dest.type) {
|
||||
console.log('Drop ID from destination. TODO');
|
||||
//context.state.transactions[0].destination_account.id = null;
|
||||
}
|
||||
|
||||
context.commit('setTransactionType', 'any');
|
||||
}
|
||||
}
|
||||
const actions = {}
|
||||
|
||||
// mutations
|
||||
const mutations = {
|
||||
addTransaction(state) {
|
||||
let newTransaction = lodashClonedeep(state.defaultTransaction);
|
||||
newTransaction.errors = lodashClonedeep(state.defaultErrors);
|
||||
state.transactions.push(newTransaction);
|
||||
},
|
||||
setDate(state, payload) {
|
||||
state.date = payload.date;
|
||||
resetErrors(state, payload) {
|
||||
//console.log('resetErrors for index ' + payload.index);
|
||||
state.transactions[payload.index].errors = lodashClonedeep(state.defaultErrors);
|
||||
},
|
||||
resetTransactions(state) {
|
||||
state.transactions = [];
|
||||
},
|
||||
setGroupTitle(state, payload) {
|
||||
state.groupTitle = payload.groupTitle;
|
||||
},
|
||||
setCustomDateFields(state, payload) {
|
||||
state.customDateFields = payload;
|
||||
},
|
||||
deleteTransaction(state, payload) {
|
||||
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;
|
||||
@@ -201,6 +125,11 @@ const mutations = {
|
||||
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;
|
||||
|
40
frontend/src/components/store/modules/transactions/edit.js
vendored
Normal file
40
frontend/src/components/store/modules/transactions/edit.js
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
File diff suppressed because it is too large
Load Diff
687
frontend/src/components/transactions/Edit.vue
Normal file
687
frontend/src/components/transactions/Edit.vue
Normal file
@@ -0,0 +1,687 @@
|
||||
<!--
|
||||
- Edit.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"/>
|
||||
<SplitPills :transactions="transactions"/>
|
||||
|
||||
<div class="tab-content">
|
||||
<SplitForm
|
||||
v-for="(transaction, index) in this.transactions"
|
||||
v-bind:key="index"
|
||||
:count="transactions.length"
|
||||
:transaction="transaction"
|
||||
:allowed-opposing-types="allowedOpposingTypes"
|
||||
:custom-fields="customFields"
|
||||
:date="date"
|
||||
:time="time"
|
||||
:index="index"
|
||||
:transaction-type="transactionType"
|
||||
:destination-allowed-types="destinationAllowedTypes"
|
||||
:source-allowed-types="sourceAllowedTypes"
|
||||
:allow-switch="false"
|
||||
:submitted-transaction="submittedTransaction"
|
||||
v-on:uploaded-attachments="uploadedAttachment($event)"
|
||||
v-on:set-marker-location="storeLocation($event)"
|
||||
v-on:set-account="storeAccountValue($event)"
|
||||
v-on:set-date="storeDate($event)"
|
||||
v-on:set-time="storeTime($event)"
|
||||
v-on:set-field="storeField($event)"
|
||||
v-on:remove-transaction="removeTransaction($event)"
|
||||
v-on:selected-attachments="selectedAttachments($event)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- bottom buttons etc -->
|
||||
<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">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="text-xs d-none d-lg-block d-xl-block">
|
||||
|
||||
</div>
|
||||
<button class="btn btn-outline-primary btn-block" @click="addTransaction"><i class="far fa-clone"></i> {{ $t('firefly.add_another_split') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="text-xs d-none d-lg-block d-xl-block">
|
||||
|
||||
</div>
|
||||
<button :disabled="!enableSubmit" class="btn btn-info btn-block" @click="submitTransaction">
|
||||
<span v-if="enableSubmit"><i class="far fa-save"></i> {{ $t('firefly.update_transaction') }}</span>
|
||||
<span v-if="!enableSubmit"><i class="fas fa-spinner fa-spin"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
||||
</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.after_update_create_another') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const lodashClonedeep = require('lodash.clonedeep');
|
||||
import Alert from '../partials/Alert';
|
||||
import SplitPills from "./SplitPills";
|
||||
import SplitForm from "./SplitForm";
|
||||
import TransactionGroupTitle from "./TransactionGroupTitle";
|
||||
import {getDefaultErrors, getDefaultTransaction, toW3CString} from '../shared/transactions';
|
||||
|
||||
export default {
|
||||
name: "Edit",
|
||||
created() {
|
||||
let parts = window.location.pathname.split('/');
|
||||
this.groupId = parseInt(parts[parts.length - 1]);
|
||||
|
||||
this.getTransactionGroup();
|
||||
this.getAllowedOpposingTypes();
|
||||
this.getCustomFields();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
successMessage: '',
|
||||
errorMessage: '',
|
||||
|
||||
// transaction props
|
||||
transactions: [],
|
||||
originalTransactions: [],
|
||||
groupTitle: '',
|
||||
originalGroupTitle: '',
|
||||
transactionType: 'any',
|
||||
groudId: 0,
|
||||
|
||||
// errors in the group title:
|
||||
groupTitleErrors: [],
|
||||
|
||||
// which custom fields to show TODO
|
||||
customFields: {},
|
||||
|
||||
// group ID + title once submitted:
|
||||
returnedGroupId: 0,
|
||||
returnedGroupTitle: '',
|
||||
|
||||
// date and time of the transaction, TODO
|
||||
date: new Date,
|
||||
time: new Date,
|
||||
originalDate: new Date,
|
||||
originalTime: new Date,
|
||||
|
||||
// things the process is done working on (3 phases):
|
||||
submittedTransaction: false,
|
||||
submittedLinks: false,
|
||||
submittedAttachments: false,
|
||||
|
||||
// meta data for accounts
|
||||
allowedOpposingTypes: {},
|
||||
destinationAllowedTypes: [],
|
||||
sourceAllowedTypes: [],
|
||||
|
||||
// states for the form (makes sense right)
|
||||
enableSubmit: true,
|
||||
createAnother: false,
|
||||
resetFormAfter: false,
|
||||
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Alert,
|
||||
SplitPills,
|
||||
SplitForm,
|
||||
TransactionGroupTitle
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Grap transaction group from URL and submit GET.
|
||||
*/
|
||||
getTransactionGroup: function () {
|
||||
axios.get('./api/v1/transactions/' + this.groupId)
|
||||
.then(response => {
|
||||
this.parseTransactionGroup(response.data);
|
||||
}
|
||||
).catch(error => {
|
||||
// console.log('I failed :(');
|
||||
// console.log(error);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Parse transaction group. Title is easy, transactions have their own method.
|
||||
* @param response
|
||||
*/
|
||||
parseTransactionGroup: function (response) {
|
||||
// console.log('Will now parse');
|
||||
// console.log(response);
|
||||
let attributes = response.data.attributes;
|
||||
let transactions = attributes.transactions.reverse();
|
||||
this.groupTitle = attributes.group_title;
|
||||
this.originalGroupTitle = attributes.group_title;
|
||||
|
||||
for (let i in transactions) {
|
||||
if (transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let result = this.parseTransaction(parseInt(i), transactions[i]);
|
||||
this.transactions.push(result);
|
||||
this.originalTransactions.push(lodashClonedeep(result));
|
||||
// pick up the links of this transaction:
|
||||
this.parseLinks(parseInt(result.transaction_journal_id), parseInt(i));
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Parse a single transaction.
|
||||
*
|
||||
* @param index
|
||||
* @param array
|
||||
*/
|
||||
parseTransaction: function (index, array) {
|
||||
//console.log('index: ' + index);
|
||||
if (0 === index) {
|
||||
this.transactionType = array.type.charAt(0).toUpperCase() + array.type.slice(1);
|
||||
this.sourceAllowedTypes = [array.source_type];
|
||||
this.destinationAllowedTypes = [array.destination_type];
|
||||
this.date = new Date(array.date);
|
||||
this.time = new Date(array.date);
|
||||
this.originalDate = new Date(array.date);
|
||||
this.originalTime = new Date(array.date);
|
||||
}
|
||||
let result = getDefaultTransaction();
|
||||
// parsing here:
|
||||
result.description = array.description;
|
||||
result.transaction_journal_id = parseInt(array.transaction_journal_id);
|
||||
// accounts:
|
||||
result.source_account_id = array.source_id;
|
||||
result.source_account_name = array.source_name;
|
||||
result.source_account_type = array.source_type;
|
||||
|
||||
result.destination_account_id = array.destination_id;
|
||||
result.destination_account_name = array.destination_name;
|
||||
result.destination_account_type = array.destination_type;
|
||||
|
||||
// amount:
|
||||
result.amount = array.amount;
|
||||
result.currency_id = array.currency_id;
|
||||
result.foreign_amount = array.foreign_amount;
|
||||
result.foreign_currency_id = array.foreign_currency_id;
|
||||
|
||||
// meta data
|
||||
result.category = array.category_name;
|
||||
result.budget_id = array.budget_id;
|
||||
result.bill_id = array.bill_id;
|
||||
|
||||
result.tags = array.tags;
|
||||
|
||||
// optional date fields (6x):
|
||||
result.interest_date = array.interest_date ? array.interest_date.substr(0, 10) : '';
|
||||
result.book_date = array.book_date ? array.book_date.substr(0, 10) : '';
|
||||
result.process_date = array.process_date ? array.process_date.substr(0, 10) : '';
|
||||
result.due_date = array.due_date ? array.due_date.substr(0, 10) : '';
|
||||
result.payment_date = array.payment_date ? array.payment_date.substr(0, 10) : '';
|
||||
result.invoice_date = array.invoice_date ? array.invoice_date.substr(0, 10) : '';
|
||||
|
||||
// optional other fields:
|
||||
result.internal_reference = array.internal_reference;
|
||||
result.external_url = array.external_uri;
|
||||
result.external_id = array.external_id;
|
||||
result.notes = array.notes;
|
||||
// location:
|
||||
result.location = {
|
||||
zoom_level: array.zoom_level,
|
||||
longitude: array.longitude,
|
||||
latitude: array.latitude,
|
||||
};
|
||||
result.zoom_level = array.zoom_level;
|
||||
result.longitude = array.longitude;
|
||||
result.latitude = array.latitude;
|
||||
|
||||
// error handling
|
||||
result.errors = getDefaultErrors();
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* Get the links of this transaction group from the API.
|
||||
*/
|
||||
parseLinks: function (journalId, index) {
|
||||
axios.get('./api/v1/transactions/' + journalId + '/links')
|
||||
.then(response => {
|
||||
let links = response.data.data;
|
||||
for (let i in links) {
|
||||
if (links.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
this.parseLink(links[i], journalId, index);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Process individual link from the API.
|
||||
*/
|
||||
parseLink: function (link, journalId, index) {
|
||||
let promises = [];
|
||||
let opposingId = parseInt(link.attributes.inward_id);
|
||||
let linkDirection = 'inward';
|
||||
if (opposingId === journalId) {
|
||||
opposingId = parseInt(link.attributes.outward_id);
|
||||
linkDirection = 'outward';
|
||||
}
|
||||
// add meta data to promise context.
|
||||
promises.push(new Promise((resolve) => {
|
||||
resolve(
|
||||
{
|
||||
link: link,
|
||||
journalId: journalId,
|
||||
opposingId: opposingId,
|
||||
index: index,
|
||||
direction: linkDirection
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
// get stuff from the API:
|
||||
promises.push(axios.get('./api/v1/transaction-journals/' + opposingId));
|
||||
promises.push(axios.get('./api/v1/transaction_links/' + link.attributes.link_type_id));
|
||||
|
||||
Promise.all(promises).then(responses => {
|
||||
let journals = responses[1].data.data.attributes.transactions;
|
||||
let opposingId = responses[0].opposingId;
|
||||
let journal = {};
|
||||
// loop over journals to get the correct one:
|
||||
for (let i in journals) {
|
||||
if (journals.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
if (journals[i].transaction_journal_id === opposingId) {
|
||||
journal = journals[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
let index = responses[0].index;
|
||||
let direction = responses[0].direction;
|
||||
let linkTypeId = responses[2].data.data.id;
|
||||
let object = {
|
||||
link_type_id: linkTypeId + '-' + direction,
|
||||
transaction_group_id: responses[1].data.data.id,
|
||||
transaction_journal_id: journal.transaction_journal_id,
|
||||
description: journal.description,
|
||||
type: journal.type,
|
||||
currency_code: journal.currency_code,
|
||||
amount: journal.amount
|
||||
};
|
||||
this.transactions[index].links.push(object);
|
||||
this.originalTransactions[index].links.push(object);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Get API value.
|
||||
*/
|
||||
getAllowedOpposingTypes: function () {
|
||||
axios.get('./api/v1/configuration/static/firefly.allowed_opposing_types')
|
||||
.then(response => {
|
||||
this.allowedOpposingTypes = response.data['firefly.allowed_opposing_types'];
|
||||
// console.log('Set allowedOpposingTypes');
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Get API value.
|
||||
*/
|
||||
getCustomFields: function () {
|
||||
axios.get('./api/v1/preferences/transaction_journal_optional_fields').then(response => {
|
||||
this.customFields = response.data.data.attributes.data;
|
||||
});
|
||||
},
|
||||
uploadedAttachment: function (payload) {
|
||||
console.log('event: uploadedAttachment');
|
||||
console.log(payload);
|
||||
},
|
||||
storeLocation: function (payload) {
|
||||
this.transactions[payload.index].zoom_level = payload.zoomLevel;
|
||||
this.transactions[payload.index].longitude = payload.lng;
|
||||
this.transactions[payload.index].latitude = payload.lat;
|
||||
},
|
||||
storeAccountValue: function (payload) {
|
||||
let direction = payload.direction;
|
||||
let index = payload.index;
|
||||
this.transactions[index][direction + '_account_id'] = payload.id;
|
||||
this.transactions[index][direction + '_account_type'] = payload.type;
|
||||
this.transactions[index][direction + '_account_name'] = payload.name;
|
||||
},
|
||||
storeDate: function (payload) {
|
||||
// console.log('event: storeDate');
|
||||
// console.log(payload);
|
||||
this.date = payload.date;
|
||||
},
|
||||
storeTime: function (payload) {
|
||||
this.time = payload.time;
|
||||
// console.log('event: storeTime');
|
||||
// console.log(payload);
|
||||
},
|
||||
storeField: function (payload) {
|
||||
let field = payload.field;
|
||||
if ('category' === field) {
|
||||
field = 'category_name';
|
||||
}
|
||||
// console.log('event: storeField(' + field + ')');
|
||||
this.transactions[payload.index][field] = payload.value;
|
||||
|
||||
},
|
||||
removeTransaction: function (payload) {
|
||||
this.transactions.splice(payload.index, 1);
|
||||
// this kills the original transactions.
|
||||
this.originalTransactions = [];
|
||||
},
|
||||
storeGroupTitle: function (payload) {
|
||||
this.groupTitle = payload;
|
||||
},
|
||||
selectedAttachments: function (payload) {
|
||||
for (let i in this.transactions) {
|
||||
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
if (parseInt(this.transactions[i].transaction_journal_id) === parseInt(payload)) {
|
||||
// console.log('selectedAttachments ' + payload);
|
||||
this.transactions[i].selectedAttachments = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
addTransaction: function () {
|
||||
let newTransaction = getDefaultTransaction();
|
||||
newTransaction.errors = getDefaultErrors();
|
||||
this.transactions.push(newTransaction);
|
||||
},
|
||||
submitTransaction: function () {
|
||||
let submission = {transactions: []};
|
||||
let shouldSubmit = false;
|
||||
let shouldLinks = false;
|
||||
let shouldUpload = false;
|
||||
if (this.groupTitle !== this.originalGroupTitle) {
|
||||
submission.group_title = this.groupTitle;
|
||||
shouldSubmit = true;
|
||||
}
|
||||
for (let i in this.transactions) {
|
||||
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
// original transaction present?
|
||||
let currentTransaction = this.transactions[i];
|
||||
let originalTransaction = this.originalTransactions.hasOwnProperty(i) ? this.originalTransactions[i] : {};
|
||||
|
||||
let diff = {};
|
||||
|
||||
// compare basic fields:
|
||||
let basicFields = [
|
||||
'description',
|
||||
'source_account_id', 'source_account_name',
|
||||
'destination_account_id', 'destination_account_name',
|
||||
'amount', 'foreign_amount', 'foreign_currency_id',
|
||||
'category_name', 'budget_id', 'bill_id',
|
||||
'interest_date', 'book_date', 'due_date', 'payment_date', 'invoice_date',
|
||||
'external_url', 'internal_reference', 'external_id', 'notes',
|
||||
'zoom_level', 'longitude', 'latitude'
|
||||
];
|
||||
|
||||
for (let ii in basicFields) {
|
||||
if (basicFields.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
|
||||
let fieldName = basicFields[ii];
|
||||
if (currentTransaction[fieldName] !== originalTransaction[fieldName]) {
|
||||
// console.log('Index ' + i + ': Field ' + fieldName + ' updated ("' + originalTransaction[fieldName] + '" > "' + currentTransaction[fieldName] + '")');
|
||||
// console.log(originalTransaction[fieldName]);
|
||||
// console.log(currentTransaction[fieldName]);
|
||||
diff[fieldName] = currentTransaction[fieldName];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0 !== currentTransaction.piggy_bank_id) {
|
||||
diff.piggy_bank_id = currentTransaction.piggy_bank_id;
|
||||
}
|
||||
if (JSON.stringify(currentTransaction.tags) !== JSON.stringify(originalTransaction.tags)) {
|
||||
// console.log('tags are different');
|
||||
// console.log(currentTransaction.tags);
|
||||
// console.log(originalTransaction.tags);
|
||||
diff.tags = currentTransaction.tags;
|
||||
}
|
||||
|
||||
// compare links:
|
||||
let newLinks = this.compareLinks(currentTransaction.links);
|
||||
let originalLinks = this.compareLinks(originalTransaction.links);
|
||||
// console.log('links are?');
|
||||
// console.log(newLinks);
|
||||
// console.log(originalLinks);
|
||||
if (newLinks !== originalLinks) {
|
||||
// console.log('links are different!');
|
||||
// console.log(newLinks);
|
||||
// console.log(originalLinks);
|
||||
shouldLinks = true;
|
||||
}
|
||||
// this.transactions[i].selectedAttachments
|
||||
// console.log(typeof currentTransaction.selectedAttachments);
|
||||
// console.log(currentTransaction.selectedAttachments);
|
||||
if (typeof currentTransaction.selectedAttachments !== 'undefined' && true === currentTransaction.selectedAttachments) {
|
||||
// must upload!
|
||||
shouldUpload = true;
|
||||
}
|
||||
|
||||
let dateStr = 'invalid';
|
||||
if (
|
||||
this.date.toISOString() !== this.originalDate.toISOString() ||
|
||||
this.time.toISOString() !== this.originalTime.toISOString()
|
||||
) {
|
||||
// set date and time!
|
||||
shouldSubmit = true;
|
||||
let theDate = this.date;
|
||||
// update time in date object.
|
||||
theDate.setHours(this.time.getHours());
|
||||
theDate.setMinutes(this.time.getMinutes());
|
||||
theDate.setSeconds(this.time.getSeconds());
|
||||
dateStr = toW3CString(theDate);
|
||||
submission.date = dateStr;
|
||||
}
|
||||
|
||||
if (Object.keys(diff).length !== 0) {
|
||||
diff.transaction_journal_id = originalTransaction.transaction_journal_id;
|
||||
submission.transactions.push(diff);
|
||||
shouldSubmit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('submitTransaction');
|
||||
console.log(shouldUpload);
|
||||
console.log(shouldLinks);
|
||||
console.log(shouldSubmit);
|
||||
if (shouldSubmit) {
|
||||
this.submitUpdate(submission);
|
||||
}
|
||||
//console.log(submission);
|
||||
},
|
||||
compareLinks: function (array) {
|
||||
let compare = [];
|
||||
for (let i in array) {
|
||||
if (array.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
compare.push(
|
||||
{
|
||||
amount: array[i].amount,
|
||||
currency_code: array[i].currency_code,
|
||||
description: array[i].description,
|
||||
link_type_id: array[i].link_type_id,
|
||||
transaction_group_id: array[i].transaction_group_id,
|
||||
type: array[i].type,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
// console.log('compareLinks');
|
||||
// console.log(compare);
|
||||
return JSON.stringify(compare);
|
||||
},
|
||||
submitUpdate: function (submission) {
|
||||
console.log('submitUpdate');
|
||||
const url = './api/v1/transactions/' + this.groupId;
|
||||
axios.put(url, submission)
|
||||
.then(response => {
|
||||
// console.log('Response is OK!');
|
||||
// report the transaction is submitted.
|
||||
this.submittedTransaction = true;
|
||||
|
||||
// // submit links and attachments (can only be done when the transaction is created)
|
||||
// this.submitTransactionLinks(data, response);
|
||||
// this.submitAttachments(data, response);
|
||||
//
|
||||
// // meanwhile, store the ID and the title in some easy to access variables.
|
||||
// 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;
|
||||
}
|
||||
)
|
||||
.catch(error => {
|
||||
console.log('error :(');
|
||||
console.log(error.response.data);
|
||||
// oh noes Firefly III has something to bitch about.
|
||||
this.enableSubmit = true;
|
||||
// report the transaction is submitted.
|
||||
this.submittedTransaction = true;
|
||||
// // also report attachments and links are submitted:
|
||||
this.submittedAttachments = true;
|
||||
this.submittedLinks = true;
|
||||
//
|
||||
// but report an error because error:
|
||||
this.inError = true;
|
||||
this.parseErrors(error.response.data);
|
||||
}
|
||||
);
|
||||
},
|
||||
parseErrors: function (errors) {
|
||||
for (let i in this.transactions) {
|
||||
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
setTransactionError: function (payload) {
|
||||
this.transactions[payload.index].errors[payload.field] = payload.errors;
|
||||
},
|
||||
resetErrors(payload) {
|
||||
this.transactions[payload.index].errors = lodashClonedeep(getDefaultErrors());
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
459
frontend/src/components/transactions/SplitForm.vue
Normal file
459
frontend/src/components/transactions/SplitForm.vue
Normal file
@@ -0,0 +1,459 @@
|
||||
<!--
|
||||
- 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 class="btn btn-danger btn-xs" @click="removeTransaction"><i class="fas fa-trash-alt"></i></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"
|
||||
:allowed-opposing-types="allowedOpposingTypes"
|
||||
:destination-allowed-types="destinationAllowedTypes"
|
||||
:errors="transaction.errors.source"
|
||||
:index="index"
|
||||
:source-allowed-types="sourceAllowedTypes"
|
||||
direction="source"
|
||||
/>
|
||||
</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"
|
||||
:allowed-opposing-types="allowedOpposingTypes"
|
||||
:destination-allowed-types="destinationAllowedTypes"
|
||||
:errors="transaction.errors.destination"
|
||||
:index="index"
|
||||
: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"
|
||||
: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"
|
||||
:time="splitTime"
|
||||
/>
|
||||
</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"
|
||||
: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"
|
||||
:submitted_transaction="submittedTransaction"
|
||||
:transaction_journal_id="transaction.transaction_journal_id"
|
||||
/>
|
||||
<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: false
|
||||
},
|
||||
customFields: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
date: {
|
||||
type: Date,
|
||||
required: true
|
||||
},
|
||||
time: {
|
||||
type: Date,
|
||||
required: true
|
||||
},
|
||||
transactionType: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
submittedTransaction: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}, // need to know if transaction is submitted.
|
||||
sourceAllowedTypes: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: []
|
||||
}, // allowed source account types.
|
||||
destinationAllowedTypes: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: []
|
||||
},
|
||||
allowedOpposingTypes: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: {}
|
||||
},
|
||||
// allow switch?
|
||||
allowSwitch: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
}
|
||||
|
||||
},
|
||||
// watch: {
|
||||
// allowedOpposingTypes: function() {
|
||||
// console.log('SplitForm noticed change in allowedOpposingTypes');
|
||||
// }
|
||||
// },
|
||||
methods: {
|
||||
removeTransaction: function () {
|
||||
// console.log('Will remove transaction ' + this.index);
|
||||
this.$emit('remove-transaction', {index: this.index});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
splitDate: function () {
|
||||
return this.date;
|
||||
},
|
||||
splitTime: function () {
|
||||
return this.time;
|
||||
},
|
||||
sourceAccount: function () {
|
||||
return {
|
||||
id: this.transaction.source_account_id,
|
||||
name: this.transaction.source_account_name,
|
||||
type: this.transaction.source_account_type,
|
||||
};
|
||||
},
|
||||
destinationAccount: function () {
|
||||
return {
|
||||
id: this.transaction.destination_account_id,
|
||||
name: this.transaction.destination_account_name,
|
||||
type: this.transaction.destination_account_type,
|
||||
};
|
||||
},
|
||||
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>
|
41
frontend/src/components/transactions/SplitPills.vue
Normal file
41
frontend/src/components/transactions/SplitPills.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<!--
|
||||
- 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 v-if="transactions.length > 1" class="row">
|
||||
<div class="col">
|
||||
<!-- tabs -->
|
||||
<ul class="nav nav-pills ml-auto p-2">
|
||||
<li v-for="(transaction, index) in this.transactions" class="nav-item"><a :class="'nav-link' + (0===index ? ' active' : '')" :href="'#split_' + index"
|
||||
data-toggle="tab">
|
||||
<span v-if="'' !== transaction.description">{{ transaction.description }}</span>
|
||||
<span v-if="'' === transaction.description">Split {{ index + 1 }}</span>
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SplitPills",
|
||||
props: ['transactions']
|
||||
}
|
||||
</script>
|
@@ -21,10 +21,10 @@
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<div class="text-xs d-none d-lg-block d-xl-block">
|
||||
<span class="text-muted" v-if="'any' !== this.transactionType">
|
||||
<span v-if="'any' !== this.transactionType" class="text-muted">
|
||||
{{ $t('firefly.' + this.transactionType) }}
|
||||
</span>
|
||||
<span class="text-muted" v-if="'any' === this.transactionType"> </span>
|
||||
<span v-if="'any' === this.transactionType" class="text-muted"> </span>
|
||||
</div>
|
||||
<div class="btn-group d-flex">
|
||||
<button class="btn btn-light" @click="switchAccounts">↔</button>
|
||||
@@ -33,34 +33,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
name: "SwitchAccount",
|
||||
props: ['index'],
|
||||
props: ['index', 'transactionType'],
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
|
||||
switchAccounts() {
|
||||
let source = this.transactions[this.index].source_account;
|
||||
let dest = this.transactions[this.index].destination_account;
|
||||
|
||||
this.updateField({field: 'source_account', index: this.index, value: dest});
|
||||
this.updateField({field: 'destination_account', index: this.index, value: source});
|
||||
|
||||
// trigger other components.
|
||||
|
||||
this.$emit('switch-accounts', this.index);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['transactions', 'transactionType']),
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -20,28 +20,44 @@
|
||||
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<div class="text-xs d-none d-lg-block d-xl-block">
|
||||
{{ $t('firefly.' + this.direction + '_account') }}
|
||||
<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">
|
||||
|
||||
</div>
|
||||
<vue-typeahead-bootstrap
|
||||
v-model="value.name"
|
||||
v-if="visible"
|
||||
v-model="accountName"
|
||||
:data="accounts"
|
||||
:showOnFocus=true
|
||||
:inputClass="errors.length > 0 ? 'is-invalid' : ''"
|
||||
:inputName="direction + '[]'"
|
||||
:serializer="item => item.name_with_balance"
|
||||
:minMatchingChars="3"
|
||||
:placeholder="$t('firefly.' + this.direction + '_account')"
|
||||
@input="lookupAccount"
|
||||
:placeholder="$t('firefly.' + direction + '_account')"
|
||||
:serializer="item => item.name_with_balance"
|
||||
:showOnFocus=true
|
||||
@hit="selectedAccount = $event"
|
||||
@input="lookupAccount"
|
||||
>
|
||||
<template slot="append">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-secondary" v-on:click="clearAccount" type="button"><i class="far fa-trash-alt"></i></button>
|
||||
|
||||
<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"><i class="far fa-trash-alt"></i></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>
|
||||
|
||||
@@ -49,80 +65,131 @@
|
||||
|
||||
import VueTypeaheadBootstrap from 'vue-typeahead-bootstrap';
|
||||
import {debounce} from 'lodash';
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
name: "TransactionAccount",
|
||||
components: {VueTypeaheadBootstrap},
|
||||
props: ['index', 'direction', 'value'],
|
||||
props: [
|
||||
'index',
|
||||
'direction',
|
||||
'value',
|
||||
'errors',
|
||||
'sourceAllowedTypes',
|
||||
'destinationAllowedTypes',
|
||||
'allowedOpposingTypes'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
query: '',
|
||||
accounts: [],
|
||||
accountTypes: [],
|
||||
initialSet: [],
|
||||
selectedAccount: {}
|
||||
selectedAccount: {},
|
||||
account: this.value,
|
||||
accountName: '',
|
||||
selectedAccountTrigger: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.selectedAccountTrigger = true;
|
||||
this.accountName = this.account.name ?? '';
|
||||
this.createInitialSet();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
'setDestinationAllowedTypes',
|
||||
'setSourceAllowedTypes'
|
||||
],
|
||||
),
|
||||
...mapActions(
|
||||
[
|
||||
'calcTransactionType'
|
||||
]
|
||||
),
|
||||
getACURL: function (types, query) {
|
||||
return document.getElementsByTagName('base')[0].href + 'api/v1/autocomplete/accounts?types=' + types.join(',') + '&query=' + query;
|
||||
return './api/v1/autocomplete/accounts?types=' + types.join(',') + '&query=' + query;
|
||||
},
|
||||
clearAccount: function () {
|
||||
this.accounts = this.initialSet;
|
||||
this.value = {name: ''};
|
||||
this.account = {name: '', type: 'no_type', id: null, currency_id: null, currency_code: null, currency_symbol: null};
|
||||
this.accountName = '';
|
||||
},
|
||||
lookupAccount: debounce(function () {
|
||||
//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.value.name))
|
||||
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 () {
|
||||
let types = this.sourceAllowedTypes;
|
||||
if ('destination' === this.direction) {
|
||||
types = this.destinationAllowedTypes;
|
||||
}
|
||||
// console.log(this.direction + ' initial set searches for');
|
||||
// console.log(types);
|
||||
|
||||
axios.get(this.getACURL(types, ''))
|
||||
.then(response => {
|
||||
// console.log('initial set of accounts. ' + this.direction);
|
||||
this.accounts = response.data;
|
||||
this.initialSet = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedAccount: function (value) {
|
||||
this.value = value;
|
||||
this.value.name = this.value.name_with_balance;
|
||||
// allowedOpposingTypes: function () {
|
||||
// console.log(this.direction + ' account noticed change in allowedOpposingTypes');
|
||||
// },
|
||||
sourceAllowedTypes: function (value) {
|
||||
// console.log(this.direction + ' account noticed change in sourceAllowedTypes');
|
||||
// console.log(value);
|
||||
this.createInitialSet();
|
||||
},
|
||||
value: function (value) {
|
||||
this.updateField({field: this.accountKey, index: this.index, value: value});
|
||||
// set the opposing account allowed set.
|
||||
destinationAllowedTypes: function (value) {
|
||||
// console.log(this.direction + ' account noticed change in destinationAllowedTypes');
|
||||
// console.log(value);
|
||||
this.createInitialSet();
|
||||
},
|
||||
selectedAccount: function (value) {
|
||||
this.selectedAccountTrigger = true;
|
||||
this.account = value;
|
||||
|
||||
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,
|
||||
}
|
||||
);
|
||||
this.accountName = this.account.name_with_balance;
|
||||
},
|
||||
accountName: function (value) {
|
||||
if (false === this.selectedAccountTrigger) {
|
||||
// console.log('Save to change name!');
|
||||
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,
|
||||
}
|
||||
);
|
||||
this.accountTrigger = false;
|
||||
this.account = {name: value, type: null, id: null, currency_id: null, currency_code: null, currency_symbol: null};
|
||||
}
|
||||
this.selectedAccountTrigger = false;
|
||||
},
|
||||
account: function (value) {
|
||||
let opposingAccounts = [];
|
||||
let type = value.type ? value.type : 'no_type';
|
||||
if ('undefined' !== typeof this.allowedOpposingTypes[this.direction]) {
|
||||
@@ -132,91 +199,46 @@ export default {
|
||||
}
|
||||
|
||||
if ('source' === this.direction) {
|
||||
this.setDestinationAllowedTypes(opposingAccounts);
|
||||
this.$emit('set-dest-types', opposingAccounts);
|
||||
}
|
||||
if ('destination' === this.direction) {
|
||||
this.setSourceAllowedTypes(opposingAccounts);
|
||||
this.$emit('set-src-types', opposingAccounts);
|
||||
}
|
||||
|
||||
this.calcTransactionType();
|
||||
},
|
||||
// account: function (value) {
|
||||
// //this.value.name = value;
|
||||
// //console.log('watch account in direction ' + this.direction + ' change to "' + value + '"');
|
||||
// // this.account = value ? value.name_with_balance : null;
|
||||
// // // console.log('this.account (' + this.direction + ') = "' + this.account + '"');
|
||||
// //
|
||||
// //
|
||||
// // // set the opposing account allowed set.
|
||||
// // // console.log('opposing:');
|
||||
// // let opposingAccounts = [];
|
||||
// // let type = value.type ? value.type : 'no_type';
|
||||
// // if ('undefined' !== typeof this.allowedOpposingTypes[this.direction]) {
|
||||
// // if ('undefined' !== typeof this.allowedOpposingTypes[this.direction][type]) {
|
||||
// // opposingAccounts = this.allowedOpposingTypes[this.direction][type];
|
||||
// // }
|
||||
// // }
|
||||
// //
|
||||
// // if ('source' === this.direction) {
|
||||
// // this.setDestinationAllowedTypes(opposingAccounts);
|
||||
// // }
|
||||
// // if ('destination' === this.direction) {
|
||||
// // this.setSourceAllowedTypes(opposingAccounts);
|
||||
// // }
|
||||
//
|
||||
//
|
||||
// //
|
||||
// // this.calcTransactionType();
|
||||
//
|
||||
//
|
||||
// }
|
||||
|
||||
// selectedAccount: function (value) {
|
||||
|
||||
// },
|
||||
// sourceAllowedTypes: function (value) {
|
||||
// if ('source' === this.direction) {
|
||||
// // console.log('do update initial set in direction ' + this.direction + ' because allowed types changed');
|
||||
// // update initial set:
|
||||
// this.createInitialSet();
|
||||
// }
|
||||
// },
|
||||
// destinationAllowedTypes: function (value) {
|
||||
// if ('destination' === this.direction) {
|
||||
// // console.log('do update initial set in direction ' + this.direction + ' because allowed types changed');
|
||||
// // update initial set:
|
||||
// this.createInitialSet();
|
||||
// }
|
||||
// }
|
||||
value: function (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 ?? '';
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'transactionType',
|
||||
'transactions',
|
||||
'defaultTransaction',
|
||||
'sourceAllowedTypes',
|
||||
'destinationAllowedTypes',
|
||||
'allowedOpposingTypes'
|
||||
]),
|
||||
// 'transactionType',
|
||||
// 'sourceAllowedTypes',
|
||||
// 'destinationAllowedTypes',
|
||||
// 'allowedOpposingTypes'
|
||||
accountKey: {
|
||||
get() {
|
||||
return 'source' === this.direction ? 'source_account' : 'destination_account';
|
||||
}
|
||||
},
|
||||
// selectedAccount: {
|
||||
// get() {
|
||||
// return this.transactions[this.index][this.accountKey];
|
||||
// },
|
||||
// set(value) {
|
||||
// // console.log('set selectedAccount for ' + this.direction);
|
||||
// // console.log(value);
|
||||
// this.updateField({field: this.accountKey, index: this.index, value: value});
|
||||
// }
|
||||
// }
|
||||
visible: {
|
||||
get() {
|
||||
// index 0 is always visible:
|
||||
if (0 === this.index) {
|
||||
return true;
|
||||
}
|
||||
if ('source' === this.direction) {
|
||||
return 'any' === this.transactionType || 'Deposit' === this.transactionType
|
||||
}
|
||||
if ('destination' === this.direction) {
|
||||
return 'any' === this.transactionType || 'Withdrawal' === this.transactionType;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -22,134 +22,92 @@
|
||||
<div class="form-group">
|
||||
<div class="text-xs">{{ $t('firefly.amount') }}</div>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<div v-if="currencySymbol" class="input-group-prepend">
|
||||
<div class="input-group-text">{{ currencySymbol }}</div>
|
||||
</div>
|
||||
<input type="hidden" name="currency_id[]" :value="currencyId"/>
|
||||
<input
|
||||
v-model="transactionAmount"
|
||||
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
|
||||
:placeholder="$t('firefly.amount')"
|
||||
:title="$t('firefly.amount')"
|
||||
autocomplete="off"
|
||||
class="form-control"
|
||||
name="amount[]"
|
||||
type="number"
|
||||
v-model="amount"
|
||||
:placeholder="$t('firefly.amount')"
|
||||
>
|
||||
</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 {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
//const {mapRootState, mapRootGetters, mapRootActions, mapRootMutations} = createHelpers('');
|
||||
|
||||
|
||||
export default {
|
||||
name: "TransactionAmount",
|
||||
props: ['index'],
|
||||
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);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatNumber(str) {
|
||||
return parseFloat(str).toFixed(this.fractionDigits);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currencySymbol: ''
|
||||
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':
|
||||
// take currency from source:
|
||||
this.currencyId = this.transactions[this.index].source_account.currency_id;
|
||||
this.currencySymbol = this.transactions[this.index].source_account.currency_symbol;
|
||||
return;
|
||||
this.currencySymbol = this.srcCurrencySymbol;
|
||||
break;
|
||||
case 'Deposit':
|
||||
// take currency from destination:
|
||||
this.currencyId = this.transactions[this.index].destination_account.currency_id;
|
||||
this.currencySymbol = this.transactions[this.index].destination_account.currency_symbol;
|
||||
return;
|
||||
this.currencySymbol = this.dstCurrencySymbol;
|
||||
}
|
||||
},
|
||||
destinationAllowedTypes: function (value) {
|
||||
// aka source was updated. if source is asset/loan/debt/mortgage use it to set the currency:
|
||||
if ('undefined' !== typeof this.transactions[this.index].source_account.type) {
|
||||
if (['Asset account', 'Loan', 'Debt', 'Mortgage'].indexOf(this.transactions[this.index].source_account.type) !== -1) {
|
||||
// get currency pref from source account
|
||||
this.currencyId = this.transactions[this.index].source_account.currency_id;
|
||||
this.currencySymbol = this.transactions[this.index].source_account.currency_symbol;
|
||||
}
|
||||
}
|
||||
},
|
||||
sourceAllowedTypes: function (value) {
|
||||
// aka destination was updated. if destination is asset/loan/debt/mortgage use it to set the currency:
|
||||
// unless its already known to be a transfer
|
||||
if ('undefined' !== typeof this.transactions[this.index].destination_account.type && 'Transfer' !== this.transactionType) {
|
||||
if (['Asset account', 'Loan', 'Debt', 'Mortgage'].indexOf(this.transactions[this.index].destination_account.type) !== -1) {
|
||||
// get currency pref from destination account
|
||||
this.currencyId = this.transactions[this.index].destination_account.currency_id;
|
||||
this.currencySymbol = this.transactions[this.index].destination_account.currency_symbol;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
created: function () {
|
||||
this.updateCurrency();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
updateCurrency: function () {
|
||||
if (0 === this.currencyId) {
|
||||
// use default currency from store.
|
||||
this.currencySymbol = this.currencyPreference.symbol;
|
||||
this.currencyId = this.currencyPreference.id;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currencyPreference: {
|
||||
get() {
|
||||
return this.$store.state.currencyPreference;
|
||||
}
|
||||
},
|
||||
...mapGetters([
|
||||
'transactionType',
|
||||
'transactions',
|
||||
'destinationAllowedTypes',
|
||||
'sourceAllowedTypes',
|
||||
]),
|
||||
amount: {
|
||||
get() {
|
||||
return this.transactions[this.index].amount;
|
||||
},
|
||||
set(value) {
|
||||
this.updateField({field: 'amount', index: this.index, value: value});
|
||||
}
|
||||
},
|
||||
currencyId: {
|
||||
get() {
|
||||
return this.transactions[this.index].currency_id;
|
||||
},
|
||||
set(value) {
|
||||
this.updateField({field: 'currency_id', index: this.index, value: value});
|
||||
}
|
||||
},
|
||||
selectedTransactionType: {
|
||||
get() {
|
||||
return this.transactionType;
|
||||
},
|
||||
set(value) {
|
||||
// console.log('set selectedAccount for ' + this.direction);
|
||||
// console.log(value);
|
||||
// this.updateField({field: this.accountKey, index: this.index, value: value});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -19,24 +19,102 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<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
|
||||
type="file"
|
||||
ref="att"
|
||||
class="form-control"
|
||||
multiple
|
||||
@change="selectedFile"
|
||||
name="attachments[]"
|
||||
:placeholder="$t('firefly.attachment')"
|
||||
class="form-control"/>
|
||||
type="file"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TransactionAttachments"
|
||||
name: "TransactionAttachments",
|
||||
props: ['transaction_journal_id', 'customFields'],
|
||||
data() {
|
||||
return {
|
||||
availableFields: this.customFields
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
customFields: function (value) {
|
||||
this.availableFields = value;
|
||||
},
|
||||
transaction_journal_id: function (value) {
|
||||
if (!this.showField) {
|
||||
// console.log('Field is hidden. Emit event!');
|
||||
this.$emit('uploaded-attachments', value);
|
||||
return;
|
||||
}
|
||||
// console.log('transaction_journal_id changed to ' + value);
|
||||
// do upload!
|
||||
if (0 !== value) {
|
||||
this.doUpload();
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showField: function () {
|
||||
if ('attachments' in this.availableFields) {
|
||||
return this.availableFields.attachments;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectedFile: function() {
|
||||
this.$emit('selected-attachments', this.transaction_journal_id);
|
||||
},
|
||||
doUpload: function () {
|
||||
// console.log('Now in doUpload() for ' + this.$refs.att.files.length + ' files.');
|
||||
for (let i in this.$refs.att.files) {
|
||||
if (this.$refs.att.files.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.$refs.att.files[i];
|
||||
let fileReader = new FileReader();
|
||||
let theParent = this; // dont ask me why i need to do this.
|
||||
fileReader.onloadend = function (evt) {
|
||||
if (evt.target.readyState === FileReader.DONE) {
|
||||
// do upload here
|
||||
const uri = './api/v1/attachments';
|
||||
const data = {
|
||||
filename: current.name,
|
||||
attachable_type: 'TransactionJournal',
|
||||
attachable_id: theParent.transaction_journal_id,
|
||||
};
|
||||
// create new attachment:
|
||||
axios.post(uri, data).then(response => {
|
||||
// upload actual file:
|
||||
const uploadUri = './api/v1/attachments/' + response.data.data.id + '/upload';
|
||||
axios
|
||||
.post(uploadUri, new Blob([evt.target.result]))
|
||||
.then(attachmentResponse => {
|
||||
// TODO feedback etc.
|
||||
// console.log('Uploaded a file. Emit event!');
|
||||
// console.log(attachmentResponse);
|
||||
theParent.$emit('uploaded-attachments', this.transaction_journal_id);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
fileReader.readAsArrayBuffer(current);
|
||||
}
|
||||
}
|
||||
if (0 === this.$refs.att.files.length) {
|
||||
// console.log('No files to upload. Emit event!');
|
||||
this.$emit('uploaded-attachments', this.transaction_journal_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -26,43 +26,38 @@
|
||||
<div class="input-group">
|
||||
<select
|
||||
ref="bill"
|
||||
v-model="bill"
|
||||
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
|
||||
:title="$t('firefly.bill')"
|
||||
v-model="value"
|
||||
autocomplete="off"
|
||||
class="form-control"
|
||||
name="bill_id[]"
|
||||
v-on:submit.prevent
|
||||
>
|
||||
<option v-for="bill in this.billList" :value="bill.id" :label="bill.name">{{ bill.name }}</option>
|
||||
<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>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
props: ['value', 'index'],
|
||||
props: ['value', 'index', 'errors'],
|
||||
name: "TransactionBill",
|
||||
data() {
|
||||
return {
|
||||
billList: []
|
||||
billList: [],
|
||||
bill: this.value,
|
||||
emitEvent: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.collectData();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
collectData() {
|
||||
this.billList.push(
|
||||
{
|
||||
@@ -94,18 +89,17 @@ export default {
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value: function(value) {
|
||||
this.updateField({field: 'bill_id', index: this.index, value: value});
|
||||
value: function (value) {
|
||||
this.emitEvent = false;
|
||||
this.bill = value;
|
||||
},
|
||||
bill: function (value) {
|
||||
if (true === this.emitEvent) {
|
||||
this.$emit('set-field', {field: 'bill_id', index: this.index, value: value});
|
||||
}
|
||||
this.emitEvent = true;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(
|
||||
[
|
||||
'transactionType',
|
||||
'transactions',
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -26,42 +26,37 @@
|
||||
<div class="input-group">
|
||||
<select
|
||||
ref="budget"
|
||||
v-model="budget"
|
||||
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
|
||||
:title="$t('firefly.budget')"
|
||||
v-model="value"
|
||||
autocomplete="off"
|
||||
class="form-control"
|
||||
name="budget_id[]"
|
||||
v-on:submit.prevent
|
||||
>
|
||||
<option v-for="budget in this.budgetList" :value="budget.id" :label="budget.name">{{ budget.name }}</option>
|
||||
<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>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
props: ['index', 'value'],
|
||||
props: ['index', 'value', 'errors'],
|
||||
name: "TransactionBudget",
|
||||
data() {
|
||||
return {
|
||||
budgetList: []
|
||||
budgetList: [],
|
||||
budget: this.value,
|
||||
emitEvent: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.collectData();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
collectData() {
|
||||
this.budgetList.push(
|
||||
{
|
||||
@@ -94,16 +89,12 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
value: function (value) {
|
||||
this.updateField({field: 'budget_id', index: this.index, value: value});
|
||||
this.emitEvent = false;
|
||||
this.budget = value;
|
||||
},
|
||||
budget: function (value) {
|
||||
this.$emit('set-field', {field: 'budget_id', index: this.index, value: value});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(
|
||||
[
|
||||
'transactionType',
|
||||
'transactions',
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -25,42 +25,44 @@
|
||||
</div>
|
||||
|
||||
<vue-typeahead-bootstrap
|
||||
inputName="category[]"
|
||||
v-model="value"
|
||||
v-model="category"
|
||||
:data="categories"
|
||||
:placeholder="$t('firefly.category')"
|
||||
:showOnFocus=true
|
||||
: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"
|
||||
>
|
||||
<template slot="append">
|
||||
<div class="input-group-append">
|
||||
<button v-on:click="clearCategory" class="btn btn-outline-secondary" type="button"><i class="far fa-trash-alt"></i></button>
|
||||
<button class="btn btn-outline-secondary" tabindex="-1" type="button" v-on:click="clearCategory"><i class="far fa-trash-alt"></i></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 {createNamespacedHelpers} from "vuex";
|
||||
import VueTypeaheadBootstrap from 'vue-typeahead-bootstrap';
|
||||
import {debounce} from "lodash";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
props: ['value', 'index'],
|
||||
props: ['value', 'index', 'errors'],
|
||||
components: {VueTypeaheadBootstrap},
|
||||
name: "TransactionCategory",
|
||||
data() {
|
||||
return {
|
||||
categories: [],
|
||||
initialSet: []
|
||||
initialSet: [],
|
||||
category: this.value,
|
||||
emitEvent: true
|
||||
}
|
||||
},
|
||||
|
||||
@@ -75,13 +77,8 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
clearCategory: function () {
|
||||
this.value = '';
|
||||
this.category = '';
|
||||
},
|
||||
getACURL: function (query) {
|
||||
// update autocomplete URL:
|
||||
@@ -97,22 +94,20 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
value: function (value) {
|
||||
this.updateField({field: 'category', index: this.index, value: value});
|
||||
this.emitEvent = false;
|
||||
this.category = value ?? '';
|
||||
},
|
||||
category: function (value) {
|
||||
this.$emit('set-field', {field: 'category', index: this.index, value: value});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(
|
||||
[
|
||||
'transactionType',
|
||||
'transactions',
|
||||
]
|
||||
),
|
||||
selectedCategory: {
|
||||
get() {
|
||||
return this.categories[this.index].name;
|
||||
},
|
||||
set(value) {
|
||||
this.value = value.name;
|
||||
this.category = value.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -20,21 +20,21 @@
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="form-group" v-for="(enabled, name) in enabledDates">
|
||||
<div class="text-xs d-none d-lg-block d-xl-block" v-if="enabled">
|
||||
<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 class="input-group" v-if="enabled">
|
||||
<div v-if="enabled && isDateField(name)" class="input-group">
|
||||
<input
|
||||
class="form-control"
|
||||
type="date"
|
||||
:ref="name"
|
||||
:title="$t('form.' + name)"
|
||||
:value="getFieldValue(name)"
|
||||
@change="setFieldValue($event, name)"
|
||||
autocomplete="off"
|
||||
:name="name + '[]'"
|
||||
:placeholder="$t('form.' + name)"
|
||||
:title="$t('form.' + name)"
|
||||
:value="getFieldValue(name)"
|
||||
autocomplete="off"
|
||||
class="form-control"
|
||||
type="date"
|
||||
@change="setFieldValue($event, name)"
|
||||
v-on:submit.prevent
|
||||
>
|
||||
</div>
|
||||
@@ -43,33 +43,67 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
export default {
|
||||
name: "TransactionCustomDates",
|
||||
props: ['enabledDates', 'index'],
|
||||
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: {
|
||||
...mapGetters(
|
||||
[
|
||||
'transactions'
|
||||
]
|
||||
),
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
isDateField: function (name) {
|
||||
return this.dateFields.includes(name)
|
||||
},
|
||||
getFieldValue(field) {
|
||||
return this.transactions()[parseInt(this.index)][field] ?? '';
|
||||
return this.dates[field] ?? '';
|
||||
},
|
||||
setFieldValue(event, field) {
|
||||
this.updateField({index: this.index, field: field, value: event.target.value});
|
||||
}
|
||||
this.$emit('set-field', {field: field, index: this.index, value: event.target.value});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -25,87 +25,96 @@
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input
|
||||
class="form-control"
|
||||
type="date"
|
||||
ref="date"
|
||||
:title="$t('firefly.date')"
|
||||
v-model="localDate"
|
||||
v-model="dateStr"
|
||||
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
|
||||
:disabled="index > 0"
|
||||
:placeholder="dateStr"
|
||||
:title="$t('firefly.date')"
|
||||
autocomplete="off"
|
||||
name="date[]"
|
||||
:placeholder="localDate"
|
||||
v-on:submit.prevent
|
||||
type="date"
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
type="time"
|
||||
ref="time"
|
||||
:title="$t('firefly.time')"
|
||||
v-model="localTime"
|
||||
v-model="timeStr"
|
||||
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
|
||||
:disabled="index > 0"
|
||||
:placeholder="timeStr"
|
||||
:title="$t('firefly.time')"
|
||||
autocomplete="off"
|
||||
name="time[]"
|
||||
:placeholder="localTime"
|
||||
v-on:submit.prevent
|
||||
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" v-if="'' !== timeZone">{{ timeZone }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
props: ['index', 'errors', 'date', 'time'],
|
||||
name: "TransactionDate",
|
||||
props: ['index'],
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
'setDate'
|
||||
],
|
||||
),
|
||||
created() {
|
||||
this.timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
localDate: this.date,
|
||||
localTime: this.time,
|
||||
timeZone: ''
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'transactionType',
|
||||
'date'
|
||||
]),
|
||||
localDate: {
|
||||
dateStr: {
|
||||
get() {
|
||||
return this.date.toISOString().split('T')[0];
|
||||
if (this.localDate instanceof Date && !isNaN(this.localDate)) {
|
||||
return this.localDate.toISOString().split('T')[0];
|
||||
}
|
||||
return '';
|
||||
},
|
||||
set(value) {
|
||||
// bit of a hack but meh.
|
||||
let newDate = new Date(value);
|
||||
let current = new Date(this.date.getTime());
|
||||
current.setFullYear(newDate.getFullYear());
|
||||
current.setMonth(newDate.getMonth());
|
||||
current.setDate(newDate.getDate());
|
||||
this.setDate({date: current});
|
||||
if ('' === value) {
|
||||
// reset to today
|
||||
this.localDate = new Date();
|
||||
this.$emit('set-date', {date: this.localDate});
|
||||
return;
|
||||
}
|
||||
this.localDate = new Date(value);
|
||||
this.$emit('set-date', {date: this.localDate});
|
||||
}
|
||||
},
|
||||
localTime: {
|
||||
timeStr: {
|
||||
get() {
|
||||
return ('0' + this.date.getHours()).slice(-2) + ':' + ('0' + this.date.getMinutes()).slice(-2) + ':' + ('0' + this.date.getSeconds()).slice(-2);
|
||||
if (this.localTime instanceof Date && !isNaN(this.localTime)) {
|
||||
return ('0' + this.localTime.getHours()).slice(-2) + ':' + ('0' + this.localTime.getMinutes()).slice(-2) + ':' + ('0' + this.localTime.getSeconds()).slice(-2);
|
||||
}
|
||||
return '';
|
||||
},
|
||||
set(value) {
|
||||
if ('' === value) {
|
||||
this.localTime.setHours(0);
|
||||
this.localTime.setMinutes(0);
|
||||
this.localTime.setSeconds(0);
|
||||
this.$emit('set-time', {time: this.localTime});
|
||||
return;
|
||||
}
|
||||
// bit of a hack but meh.
|
||||
let current = new Date(this.date.getTime());
|
||||
let current = new Date(this.localTime.getTime());
|
||||
let parts = value.split(':');
|
||||
current.setHours(parseInt(parts[0]));
|
||||
current.setMinutes(parseInt(parts[1]));
|
||||
current.setSeconds(parseInt(parts[2]));
|
||||
this.setDate({date: current});
|
||||
this.localTime = current;
|
||||
this.$emit('set-time', {time: this.localTime});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -20,50 +20,46 @@
|
||||
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<div class="text-xs d-none d-lg-block d-xl-block">
|
||||
{{ $t('firefly.description') }}
|
||||
</div>
|
||||
<vue-typeahead-bootstrap
|
||||
inputName="description[]"
|
||||
v-model="value"
|
||||
v-model="description"
|
||||
:data="descriptions"
|
||||
:inputClass="errors.length > 0 ? 'is-invalid' : ''"
|
||||
:minMatchingChars="3"
|
||||
:placeholder="$t('firefly.description')"
|
||||
:serializer="item => item.description"
|
||||
:showOnFocus=true
|
||||
autofocus
|
||||
:minMatchingChars="3"
|
||||
:serializer="item => item.description"
|
||||
inputName="description[]"
|
||||
@input="lookupDescription"
|
||||
>
|
||||
<template slot="append">
|
||||
<div class="input-group-append">
|
||||
<button v-on:click="clearDescription" class="btn btn-outline-secondary" type="button"><i class="far fa-trash-alt"></i></button>
|
||||
<button class="btn btn-outline-secondary" tabindex="-1" type="button" v-on:click="clearDescription"><i class="far fa-trash-alt"></i></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 {createNamespacedHelpers} from "vuex";
|
||||
import VueTypeaheadBootstrap from 'vue-typeahead-bootstrap';
|
||||
import {debounce} from "lodash";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
props: ['index', 'value'],
|
||||
props: ['index', 'value', 'errors'],
|
||||
components: {VueTypeaheadBootstrap},
|
||||
name: "TransactionDescription",
|
||||
data() {
|
||||
return {
|
||||
descriptions: [],
|
||||
initialSet: []
|
||||
initialSet: [],
|
||||
description: this.value,
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
axios.get(this.getACURL(''))
|
||||
.then(response => {
|
||||
@@ -73,13 +69,8 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
clearDescription: function () {
|
||||
this.value = '';
|
||||
this.description = '';
|
||||
},
|
||||
getACURL: function (query) {
|
||||
// update autocomplete URL:
|
||||
@@ -95,16 +86,11 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
value: function (value) {
|
||||
this.updateField({field: 'description', index: this.index, value: value});
|
||||
this.description = value;
|
||||
},
|
||||
description: function (value) {
|
||||
this.$emit('set-field', {field: 'description', index: this.index, value: value});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(
|
||||
[
|
||||
'transactionType',
|
||||
'transactions',
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -19,42 +19,53 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<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
|
||||
type="url"
|
||||
name="external_url[]"
|
||||
v-model="url"
|
||||
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
|
||||
:placeholder="$t('firefly.external_url')"
|
||||
v-model="value"
|
||||
class="form-control"/>
|
||||
name="external_url[]"
|
||||
type="url"
|
||||
/>
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"><i class="far fa-trash-alt"></i></button>
|
||||
<button class="btn btn-outline-secondary" tabindex="-1" type="button"><i class="far fa-trash-alt"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
props: ['index', 'value'],
|
||||
props: ['index', 'value', 'errors', 'customFields'],
|
||||
name: "TransactionExternalUrl",
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
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.updateField({field: 'external_url', index: this.index, value: value});
|
||||
this.url = value;
|
||||
},
|
||||
url: function (value) {
|
||||
this.$emit('set-field', {field: 'external_url', index: this.index, value: value});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -20,136 +20,76 @@
|
||||
|
||||
<template>
|
||||
<!-- FOREIGN AMOUNT -->
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="foreign_currency_id[]" :value="currencyId"/>
|
||||
<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"
|
||||
class="form-control"
|
||||
:disabled="0===currencyId"
|
||||
name="foreign_amount[]"
|
||||
type="number"
|
||||
v-model="amount"
|
||||
:placeholder="$t('form.foreign_amount')"
|
||||
>
|
||||
</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 {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
//const {mapRootState, mapRootGetters, mapRootActions, mapRootMutations} = createHelpers('');
|
||||
|
||||
|
||||
export default {
|
||||
name: "TransactionForeignAmount",
|
||||
props: ['index'],
|
||||
props: {
|
||||
index: {},
|
||||
errors: {},
|
||||
value: {},
|
||||
transactionType: {},
|
||||
sourceCurrencyId: {},
|
||||
destinationCurrencyId: {},
|
||||
fractionDigits: {
|
||||
type: Number,
|
||||
default: 2
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currencySymbol: '',
|
||||
allCurrencies: [],
|
||||
selectableCurrencies: [],
|
||||
amount: this.value,
|
||||
emitEvent: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if ('' !== this.amount) {
|
||||
this.emitEvent = false;
|
||||
this.amount = this.formatNumber(this.amount);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatNumber(str) {
|
||||
return parseFloat(str).toFixed(this.fractionDigits);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
transactionType: function (value) {
|
||||
// switch (value) {
|
||||
// case 'Transfer':
|
||||
// case 'Withdrawal':
|
||||
// // take currency from source:
|
||||
// //this.currencyId = this.transactions[this.index].source_account.currency_id;
|
||||
// this.currencySymbol = this.transactions[this.index].source_account.currency_symbol;
|
||||
// return;
|
||||
// case 'Deposit':
|
||||
// // take currency from destination:
|
||||
// this.currencyId = this.transactions[this.index].destination_account.currency_id;
|
||||
// this.currencySymbol = this.transactions[this.index].destination_account.currency_symbol;
|
||||
// return;
|
||||
// }
|
||||
},
|
||||
destinationAllowedTypes: function (value) {
|
||||
// // aka source was updated. if source is asset/loan/debt/mortgage use it to set the currency:
|
||||
// if ('undefined' !== typeof this.transactions[this.index].source_account.type) {
|
||||
// if (['Asset account', 'Loan', 'Debt', 'Mortgage'].indexOf(this.transactions[this.index].source_account.type) !== -1) {
|
||||
// // get currency pref from source account
|
||||
// this.currencyId = this.transactions[this.index].source_account.currency_id;
|
||||
// this.currencySymbol = this.transactions[this.index].source_account.currency_symbol;
|
||||
// }
|
||||
// }
|
||||
},
|
||||
sourceAllowedTypes: function (value) {
|
||||
// // aka destination was updated. if destination is asset/loan/debt/mortgage use it to set the currency:
|
||||
// // unless its already known to be a transfer
|
||||
// if ('undefined' !== typeof this.transactions[this.index].destination_account.type && 'Transfer' !== this.transactionType) {
|
||||
// if (['Asset account', 'Loan', 'Debt', 'Mortgage'].indexOf(this.transactions[this.index].destination_account.type) !== -1) {
|
||||
// // get currency pref from destination account
|
||||
// this.currencyId = this.transactions[this.index].destination_account.currency_id;
|
||||
// this.currencySymbol = this.transactions[this.index].destination_account.currency_symbol;
|
||||
// }
|
||||
// }
|
||||
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;
|
||||
|
||||
},
|
||||
created: function () {
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
|
||||
// updateCurrency: function () {
|
||||
// if (0 === this.currencyId) {
|
||||
// // use default currency from store.
|
||||
// this.currencySymbol = this.currencyPreference.symbol;
|
||||
// this.currencyId = this.currencyPreference.id;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currencyPreference: {
|
||||
isVisible: {
|
||||
get() {
|
||||
return this.$store.state.currencyPreference;
|
||||
return !('Transfer' === this.transactionType && this.sourceCurrencyId === this.destinationCurrencyId);
|
||||
}
|
||||
},
|
||||
...mapGetters([
|
||||
'transactionType',
|
||||
'transactions',
|
||||
'destinationAllowedTypes',
|
||||
'sourceAllowedTypes',
|
||||
]),
|
||||
amount: {
|
||||
get() {
|
||||
return this.transactions[this.index].foreign_amount;
|
||||
},
|
||||
set(value) {
|
||||
this.updateField({field: 'foreign_amount', index: this.index, value: value});
|
||||
}
|
||||
},
|
||||
currencyId: {
|
||||
get() {
|
||||
return this.transactions[this.index].foreign_currency_id;
|
||||
},
|
||||
set(value) {
|
||||
this.updateField({field: 'foreign_currency_id', index: this.index, value: value});
|
||||
}
|
||||
},
|
||||
selectedTransactionType: {
|
||||
get() {
|
||||
return this.transactionType;
|
||||
},
|
||||
set(value) {
|
||||
// console.log('set selectedAccount for ' + this.direction);
|
||||
// console.log(value);
|
||||
// this.updateField({field: this.accountKey, index: this.index, value: value});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -20,10 +20,10 @@
|
||||
|
||||
<template>
|
||||
<!-- FOREIGN Currency -->
|
||||
<div class="form-group" v-if="selectIsVisible">
|
||||
<div v-if="isVisible" class="form-group">
|
||||
<div class="text-xs"> </div>
|
||||
<div class="input-group">
|
||||
<select name="foreign_currency_id[]" v-model="currencyId" class="form-control">
|
||||
<select v-model="selectedCurrency" class="form-control" name="foreign_currency_id[]">
|
||||
<option v-for="currency in selectableCurrencies" :label="currency.name" :value="currency.id">{{ currency.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -31,77 +31,53 @@
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
name: "TransactionForeignCurrency",
|
||||
props: ['index'],
|
||||
props: [
|
||||
'index',
|
||||
'transactionType',
|
||||
'sourceCurrencyId',
|
||||
'destinationCurrencyId',
|
||||
'selectedCurrencyId',
|
||||
'value'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
selectedCurrency: this.value,
|
||||
allCurrencies: [],
|
||||
selectableCurrencies: [],
|
||||
dstCurrencyId: this.destinationCurrencyId,
|
||||
srcCurrencyId: this.sourceCurrencyId,
|
||||
lockedCurrency: 0,
|
||||
selectIsVisible: true
|
||||
emitEvent: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: function (value) {
|
||||
this.selectedCurrency = value;
|
||||
},
|
||||
sourceCurrencyId: function (value) {
|
||||
this.srcCurrencyId = value;
|
||||
},
|
||||
destinationCurrencyId: function (value) {
|
||||
this.dstCurrencyId = value;
|
||||
},
|
||||
selectedCurrency: function (value) {
|
||||
this.$emit('set-field', {field: 'foreign_currency_id', index: this.index, value: value});
|
||||
},
|
||||
transactionType: function (value) {
|
||||
this.lockedCurrency = 0;
|
||||
if ('Transfer' === value) {
|
||||
// take currency from destination:
|
||||
this.currencyId = this.transactions[this.index].destination_account.currency_id;
|
||||
this.currencySymbol = this.transactions[this.index].destination_account.currency_symbol;
|
||||
this.lockedCurrency = this.currencyId;
|
||||
this.lockedCurrency = this.dstCurrencyId;
|
||||
this.selectedCurrency = this.dstCurrencyId;
|
||||
}
|
||||
this.filterCurrencies();
|
||||
this.checkVisibility();
|
||||
},
|
||||
destinationAllowedTypes: function (value) {
|
||||
this.lockedCurrency = 0;
|
||||
if ('Transfer' === this.transactionType) {
|
||||
// take currency from destination:
|
||||
this.currencyId = this.transactions[this.index].destination_account.currency_id;
|
||||
this.currencySymbol = this.transactions[this.index].destination_account.currency_symbol;
|
||||
this.lockedCurrency = this.currencyId;
|
||||
}
|
||||
this.filterCurrencies();
|
||||
this.checkVisibility();
|
||||
},
|
||||
sourceAllowedTypes: function (value) {
|
||||
this.lockedCurrency = 0;
|
||||
if ('Transfer' === this.transactionType) {
|
||||
// take currency from destination:
|
||||
this.currencyId = this.transactions[this.index].destination_account.currency_id;
|
||||
this.currencySymbol = this.transactions[this.index].destination_account.currency_symbol;
|
||||
this.lockedCurrency = this.currencyId;
|
||||
}
|
||||
this.filterCurrencies();
|
||||
this.checkVisibility();
|
||||
},
|
||||
|
||||
},
|
||||
created: function () {
|
||||
this.getAllCurrencies();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
checkVisibility: function () {
|
||||
// have the same currency ID, but not zero, and is a transfer
|
||||
let sourceId = this.transactions[this.index].source_account.currency_id;
|
||||
let destId = this.transactions[this.index].destination_account.currency_id;
|
||||
this.selectIsVisible = true;
|
||||
if (sourceId === destId && 0 !== sourceId && 'Transfer' === this.transactionType) {
|
||||
this.selectIsVisible = false;
|
||||
this.currencyId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
getAllCurrencies: function () {
|
||||
axios.get('./api/v1/autocomplete/currencies')
|
||||
.then(response => {
|
||||
@@ -119,14 +95,13 @@ export default {
|
||||
let current = this.allCurrencies[key];
|
||||
if (current.id === this.lockedCurrency) {
|
||||
this.selectableCurrencies = [current];
|
||||
this.currencyId = current.id;
|
||||
this.selectedCurrency = current.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.selectableCurrencies = [
|
||||
{
|
||||
"id": 0,
|
||||
@@ -136,62 +111,15 @@ export default {
|
||||
for (let key in this.allCurrencies) {
|
||||
if (this.allCurrencies.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
||||
let current = this.allCurrencies[key];
|
||||
// add to array if not "locked" in place:
|
||||
if (this.transactions[this.index].currency_id !== current.id) {
|
||||
this.selectableCurrencies.push(current);
|
||||
}
|
||||
// deselect impossible currency.
|
||||
if (this.transactions[this.index].currency_id === current.id && this.currencyId === current.id) {
|
||||
this.currencyId = 0;
|
||||
}
|
||||
this.selectableCurrencies.push(current);
|
||||
}
|
||||
}
|
||||
//currency_id
|
||||
|
||||
// always add empty currency:
|
||||
// this.selectableCurrencies = this.allCurrencies;
|
||||
// this.selectableCurrencies.reverse();
|
||||
// this.selectableCurrencies.push(
|
||||
// ;
|
||||
// this.selectableCurrencies.reverse();
|
||||
|
||||
// remove
|
||||
|
||||
}
|
||||
|
||||
// updateCurrency: function () {
|
||||
// if (0 === this.currencyId) {
|
||||
// // use default currency from store.
|
||||
// this.currencySymbol = this.currencyPreference.symbol;
|
||||
// this.currencyId = this.currencyPreference.id;
|
||||
// }
|
||||
// }
|
||||
},
|
||||
computed: {
|
||||
currencyPreference: {
|
||||
get() {
|
||||
return this.$store.state.currencyPreference;
|
||||
}
|
||||
},
|
||||
...mapGetters([
|
||||
'transactionType',
|
||||
'transactions',
|
||||
'destinationAllowedTypes',
|
||||
'sourceAllowedTypes',
|
||||
]),
|
||||
currencyId: {
|
||||
get() {
|
||||
return this.transactions[this.index].foreign_currency_id;
|
||||
},
|
||||
set(value) {
|
||||
this.updateField({field: 'foreign_currency_id', index: this.index, value: value});
|
||||
}
|
||||
},
|
||||
normalCurrencyId: {
|
||||
get() {
|
||||
return this.transactions[this.index].currency_id;
|
||||
},
|
||||
},
|
||||
isVisible: function () {
|
||||
return !('Transfer' === this.transactionType && this.srcCurrencyId === this.dstCurrencyId);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
104
frontend/src/components/transactions/TransactionGroupTitle.vue
Normal file
104
frontend/src/components/transactions/TransactionGroupTitle.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<!--
|
||||
- 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"><i class="far fa-trash-alt"></i></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>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -19,47 +19,56 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<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
|
||||
type="text"
|
||||
name="internal_reference[]"
|
||||
v-model="value"
|
||||
v-model="reference"
|
||||
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
|
||||
:placeholder="$t('firefly.internal_reference')"
|
||||
class="form-control"/>
|
||||
name="internal_reference[]"
|
||||
type="text"
|
||||
/>
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"><i class="far fa-trash-alt"></i></button>
|
||||
<button class="btn btn-outline-secondary" tabindex="-1" type="button"><i class="far fa-trash-alt"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
props: ['index', 'value'],
|
||||
props: ['index', 'value', 'errors', 'customFields'],
|
||||
name: "TransactionInternalReference",
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
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.updateField({field: 'internal_reference', index: this.index, value: value});
|
||||
this.emitEvent = false;
|
||||
this.reference = value;
|
||||
},
|
||||
reference: function (value) {
|
||||
this.$emit('set-field', {field: 'internal_reference', index: this.index, value: value});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -19,18 +19,18 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<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="value.length === 0">
|
||||
<button data-toggle="modal" data-target="#linkModal" class="btn btn-default btn-xs"><i class="fas fa-plus"></i> Add transaction link</button>
|
||||
<p v-if="links.length === 0">
|
||||
<button class="btn btn-default btn-xs" data-target="#linkModal" @click="resetModal" data-toggle="modal"><i class="fas fa-plus"></i> Add transaction link</button>
|
||||
</p>
|
||||
<ul class="list-group" v-if="value.length > 0">
|
||||
<li class="list-group-item" v-for="transaction in value">
|
||||
<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>
|
||||
|
||||
@@ -59,24 +59,23 @@
|
||||
}}</span>)
|
||||
</span>
|
||||
<div class="btn-group btn-group-xs float-right">
|
||||
<a href="#" class="btn btn-xs btn-default"><i class="far fa-edit"></i></a>
|
||||
<a href="#" class="btn btn-xs btn-danger"><i class="far fa-trash-alt"></i></a>
|
||||
<button class="btn btn-xs btn-danger" @click="removeLink(index)" tabindex="-1"><i class="far fa-trash-alt"></i></button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="form-text" v-if="value.length > 0">
|
||||
<button data-toggle="modal" data-target="#linkModal" class="btn btn-default"><i class="fas fa-plus"></i></button>
|
||||
<div v-if="links.length > 0" class="form-text">
|
||||
<button class="btn btn-default" @click="resetModal" data-target="#linkModal" data-toggle="modal"><i class="fas fa-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- modal -->
|
||||
<div class="modal" tabindex="-1" id="linkModal">
|
||||
<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 type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<button aria-label="Close" class="close" data-dismiss="modal" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -94,10 +93,10 @@
|
||||
<div class="col">
|
||||
<form v-on:submit.prevent="search">
|
||||
<div class="input-group">
|
||||
<input autocomplete="off" maxlength="255" type="text" name="search" v-model="query" id="query"
|
||||
class="form-control" placeholder="Search query">
|
||||
<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 type="submit" class="btn btn-default"><i class="fas fa-search"></i> Search</button>
|
||||
<button class="btn btn-default" type="submit"><i class="fas fa-search"></i> Search</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -107,28 +106,28 @@
|
||||
<div class="col">
|
||||
<span v-if="searching"><i class="fas fa-spinner fa-spin"></i></span>
|
||||
<h4 v-if="searchResults.length > 0">Search results</h4>
|
||||
<table class="table table-sm" v-if="searchResults.length > 0">
|
||||
<table v-if="searchResults.length > 0" class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:33%" colspan="2">Include?</th>
|
||||
<th colspan="2" style="width:33%">Include?</th>
|
||||
<th>Transaction</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="result in searchResults">
|
||||
<td>
|
||||
<input type="checkbox" class="form-control"
|
||||
<input v-model="result.selected" class="form-control"
|
||||
type="checkbox"
|
||||
@change="selectTransaction($event)"
|
||||
v-model="result.selected"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
@change="selectLinkType($event)"
|
||||
class="form-control"
|
||||
v-model="result.link_type_id"
|
||||
class="form-control"
|
||||
@change="selectLinkType($event)"
|
||||
>
|
||||
<option v-for="linkType in linkTypes" :value="linkType.id + '-' + linkType.direction" :label="linkType.type">{{
|
||||
<option v-for="linkType in linkTypes" :label="linkType.type" :value="linkType.id + '-' + linkType.direction">{{
|
||||
linkType.type
|
||||
}}
|
||||
</option>
|
||||
@@ -175,7 +174,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<button class="btn btn-secondary" data-dismiss="modal" type="button">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -184,8 +183,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const lodashClonedeep = require('lodash.clonedeep');
|
||||
// TODO error handling
|
||||
export default {
|
||||
props: ['index', 'value'],
|
||||
props: ['index', 'value', 'errors', 'customFields'],
|
||||
name: "TransactionLinks",
|
||||
data() {
|
||||
return {
|
||||
@@ -194,27 +195,53 @@ export default {
|
||||
locale: 'en-US',
|
||||
linkTypes: [],
|
||||
query: '',
|
||||
searching: false
|
||||
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) {
|
||||
console.log('Selected transactions is now:');
|
||||
console.log(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];
|
||||
console.log(parts);
|
||||
console.log(current);
|
||||
if (parts[0] === current.id && parts[1] === current.direction) {
|
||||
return current.type;
|
||||
}
|
||||
@@ -245,27 +272,27 @@ export default {
|
||||
}
|
||||
},
|
||||
updateSelected(journalId, linkTypeId) {
|
||||
for (let i in this.value) {
|
||||
if (this.value.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.value[i];
|
||||
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.value[i].link_type_id = linkTypeId;
|
||||
this.links[i].link_type_id = linkTypeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
addToSelected(journal) {
|
||||
const result = this.value.find(({transaction_journal_id}) => transaction_journal_id === journal.transaction_journal_id);
|
||||
let result = this.links.find(({transaction_journal_id}) => transaction_journal_id === journal.transaction_journal_id);
|
||||
if (typeof result === 'undefined') {
|
||||
this.value.push(journal);
|
||||
this.links.push(journal);
|
||||
}
|
||||
},
|
||||
removeFromSelected(journal) {
|
||||
for (let i in this.value) {
|
||||
if (this.value.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.value[i];
|
||||
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.value.splice(parseInt(i), 1);
|
||||
this.links.splice(parseInt(i), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,6 +305,9 @@ export default {
|
||||
}
|
||||
);
|
||||
},
|
||||
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) {
|
||||
@@ -302,6 +332,10 @@ export default {
|
||||
}
|
||||
},
|
||||
search: function () {
|
||||
if('' === this.query) {
|
||||
this.searchResults = [];
|
||||
return;
|
||||
}
|
||||
this.searching = true;
|
||||
this.searchResults = [];
|
||||
let url = './api/v1/search/transactions?limit=10&query=' + this.query;
|
||||
@@ -329,9 +363,9 @@ export default {
|
||||
this.searching = false;
|
||||
},
|
||||
getJournalLinkType: function (journalId) {
|
||||
for (let i in this.value) {
|
||||
if (this.value.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.value[i];
|
||||
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;
|
||||
}
|
||||
@@ -340,9 +374,9 @@ export default {
|
||||
return '1-inward';
|
||||
},
|
||||
isJournalSelected: function (journalId) {
|
||||
for (let i in this.value) {
|
||||
if (this.value.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.value[i];
|
||||
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;
|
||||
}
|
||||
|
@@ -19,83 +19,91 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<table class="table table-striped table-sm">
|
||||
<caption style="display:none;">{{ $t('firefly.transaction_table_description') }}</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="text-left">{{ $t('firefly.description') }}</th>
|
||||
<th scope="col">{{ $t('firefly.opposing_account') }}</th>
|
||||
<th scope="col" class="text-right">{{ $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>
|
||||
<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 :href="'accounts/show/' + tr.destination_id" v-if="'withdrawal' === tr.type">{{ tr.destination_name }}</a>
|
||||
<a :href="'accounts/show/' + tr.source_id" v-if="'deposit' === tr.type">{{ tr.source_name }}</a>
|
||||
<a :href="'accounts/show/' + tr.destination_id" v-if="'transfer' === tr.type && tr.source_id === account_id">{{ tr.destination_name }}</a>
|
||||
<a :href="'accounts/show/' + tr.source_id" v-if="'transfer' === tr.type && tr.destination_id === account_id">{{ tr.source_name }}</a>
|
||||
<br />
|
||||
<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 && tr.source_id === account_id" :href="'accounts/show/' + tr.destination_id">{{ tr.destination_name }}</a>
|
||||
<a v-if="'transfer' === tr.type && tr.destination_id === account_id" :href="'accounts/show/' + tr.source_id">{{ tr.source_name }}</a>
|
||||
<br/>
|
||||
</span>
|
||||
</td>
|
||||
<td style="text-align:right;">
|
||||
</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('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1)}}<br>
|
||||
{{ 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('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount)}}<br>
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount) }}<br>
|
||||
</span>
|
||||
<span v-if="'transfer' === tr.type && tr.source_id === account_id" class="text-info">
|
||||
{{ Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1)}}<br>
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1) }}<br>
|
||||
</span>
|
||||
<span v-if="'transfer' === tr.type && tr.destination_id === account_id" class="text-info">
|
||||
{{ Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount)}}<br>
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount) }}<br>
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<span v-for="tr in transaction.attributes.transactions">
|
||||
<a :href="'categories/show/' + tr.category_id" v-if="0!==tr.category_id">{{ tr.category_name }}</a><br />
|
||||
<a v-if="0!==tr.category_id" :href="'categories/show/' + tr.category_id">{{ tr.category_name }}</a><br/>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<span v-for="tr in transaction.attributes.transactions">
|
||||
<a :href="'budgets/show/' + tr.budget_id" v-if="0!==tr.budget_id">{{ tr.budget_name }}</a><br />
|
||||
<a v-if="0!==tr.budget_id" :href="'budgets/show/' + tr.budget_id">{{ tr.budget_name }}</a><br/>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TransactionListLarge",
|
||||
props: {
|
||||
transactions: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
account_id: {
|
||||
type: Number,
|
||||
default: function () {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
}
|
||||
export default {
|
||||
name: "TransactionListLarge",
|
||||
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>
|
||||
|
||||
<style scoped>
|
||||
|
@@ -19,71 +19,79 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<table class="table table-striped table-sm">
|
||||
<caption style="display:none;">{{ $t('firefly.transaction_table_description') }}</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="text-left">{{ $t('firefly.description') }}</th>
|
||||
<th scope="col">{{ $t('firefly.opposing_account') }}</th>
|
||||
<th scope="col" class="text-right">{{ $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>
|
||||
<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 :href="'accounts/show/' + tr.destination_id" v-if="'withdrawal' === tr.type">{{ tr.destination_name }}</a>
|
||||
<a :href="'accounts/show/' + tr.source_id" v-if="'deposit' === tr.type">{{ tr.source_name }}</a>
|
||||
<a :href="'accounts/show/' + tr.destination_id" v-if="'transfer' === tr.type && tr.source_id === account_id">{{ tr.destination_name }}</a>
|
||||
<a :href="'accounts/show/' + tr.source_id" v-if="'transfer' === tr.type && tr.destination_id === account_id">{{ tr.source_name }}</a>
|
||||
<br />
|
||||
<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 && tr.source_id === account_id" :href="'accounts/show/' + tr.destination_id">{{ tr.destination_name }}</a>
|
||||
<a v-if="'transfer' === tr.type && tr.destination_id === account_id" :href="'accounts/show/' + tr.source_id">{{ tr.source_name }}</a>
|
||||
<br/>
|
||||
</span>
|
||||
</td>
|
||||
<td style="text-align:right;">
|
||||
</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('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1)}}<br>
|
||||
{{ 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('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount)}}<br>
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount) }}<br>
|
||||
</span>
|
||||
<span v-if="'transfer' === tr.type && tr.source_id === account_id" class="text-info">
|
||||
{{ Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1)}}<br>
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1) }}<br>
|
||||
</span>
|
||||
<span v-if="'transfer' === tr.type && tr.destination_id === account_id" class="text-info">
|
||||
{{ Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount)}}<br>
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount) }}<br>
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TransactionListMedium",
|
||||
props: {
|
||||
transactions: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
account_id: {
|
||||
type: Number,
|
||||
default: function () {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
}
|
||||
export default {
|
||||
name: "TransactionListMedium",
|
||||
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>
|
||||
|
||||
<style scoped>
|
||||
|
@@ -23,14 +23,15 @@
|
||||
<caption style="display:none;">{{ $t('firefly.transaction_table_description') }}</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="text-left">{{ $t('firefly.description') }}</th>
|
||||
<th scope="col" class="text-right">{{ $t('firefly.amount') }}</th>
|
||||
<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))">
|
||||
<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>
|
||||
@@ -67,8 +68,7 @@ export default {
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
methods: {},
|
||||
props: {
|
||||
transactions: {
|
||||
type: Array,
|
||||
|
171
frontend/src/components/transactions/TransactionLocation.vue
Normal file
171
frontend/src/components/transactions/TransactionLocation.vue
Normal file
@@ -0,0 +1,171 @@
|
||||
<!--
|
||||
- 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> </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/static/firefly.default_location').then(response => {
|
||||
this.zoom = parseInt(response.data['firefly.default_location'].zoom_level);
|
||||
this.center =
|
||||
[
|
||||
parseFloat(response.data['firefly.default_location'].latitude),
|
||||
parseFloat(response.data['firefly.default_location'].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>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -20,35 +20,53 @@
|
||||
|
||||
<template>
|
||||
|
||||
<div class="form-group">
|
||||
<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="value" class="form-control" :placeholder="$t('firefly.notes')"></textarea>
|
||||
<textarea
|
||||
v-model="notes"
|
||||
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
|
||||
:placeholder="$t('firefly.notes')"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
props: ['index', 'value'],
|
||||
props: ['index', 'value', 'errors', 'customFields'],
|
||||
name: "TransactionNotes",
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
data() {
|
||||
return {
|
||||
notes: this.value,
|
||||
availableFields: this.customFields,
|
||||
emitEvent: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showField: function () {
|
||||
if ('notes' in this.availableFields) {
|
||||
return this.availableFields.notes;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: function (value) {
|
||||
this.updateField({field: 'notes', index: this.index, value: value});
|
||||
this.emitEvent = false;
|
||||
this.notes = value;
|
||||
},
|
||||
customFields: function (value) {
|
||||
this.availableFields = value;
|
||||
},
|
||||
notes: function (value) {
|
||||
if (true === this.emitEvent) {
|
||||
this.$emit('set-field', {field: 'notes', index: this.index, value: value});
|
||||
}
|
||||
this.emitEvent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -26,43 +26,39 @@
|
||||
<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')"
|
||||
v-model="value"
|
||||
autocomplete="off"
|
||||
class="form-control"
|
||||
name="piggy_bank_id[]"
|
||||
v-on:submit.prevent
|
||||
>
|
||||
<option v-for="piggy in this.piggyList" :value="piggy.id" :label="piggy.name_with_balance">{{ piggy.name_with_balance }}</option>
|
||||
<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>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
props: ['index', 'value'],
|
||||
props: ['index', 'value', 'errors'],
|
||||
name: "TransactionPiggyBank",
|
||||
data() {
|
||||
return {
|
||||
piggyList: []
|
||||
piggyList: [],
|
||||
piggy_bank_id: this.value,
|
||||
emitEvent: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.collectData();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
collectData() {
|
||||
this.piggyList.push(
|
||||
{
|
||||
@@ -95,14 +91,15 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
value: function (value) {
|
||||
this.updateField({field: 'piggy_bank_id', index: this.index, value: value});
|
||||
this.emitEvent = false;
|
||||
this.piggy_bank_id = value;
|
||||
},
|
||||
piggy_bank_id: function (value) {
|
||||
if (true === this.emitEvent) {
|
||||
this.$emit('set-field', {field: 'piggy_bank_id', index: this.index, value: value});
|
||||
}
|
||||
this.emitEvent = true;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'transactionType',
|
||||
'transactions',
|
||||
])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -34,61 +34,72 @@
|
||||
@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 {createNamespacedHelpers} from "vuex";
|
||||
import VueTagsInput from "@johmun/vue-tags-input";
|
||||
import axios from "axios";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
export default {
|
||||
name: "TransactionTags",
|
||||
components: {
|
||||
VueTagsInput
|
||||
},
|
||||
props: ['value', 'index'],
|
||||
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.
|
||||
updateTags: true, // the idea is that this is always true, except when the tags-function sets the value.
|
||||
tagList: this.value,
|
||||
emitEvent: true
|
||||
};
|
||||
},
|
||||
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) {
|
||||
console.log('watch: value');
|
||||
console.log(value);
|
||||
this.updateField({field: 'tags', index: this.index, value: value});
|
||||
this.emitEvent = false;
|
||||
this.tagList = value;
|
||||
},
|
||||
tagList: function (value) {
|
||||
console.log('watch tagList');
|
||||
if (true === this.emitEvent) {
|
||||
this.$emit('set-field', {field: 'tags', index: this.index, value: value});
|
||||
}
|
||||
this.emitEvent = true;
|
||||
this.updateTags = false;
|
||||
this.tags = value;
|
||||
},
|
||||
tags: function (value) {
|
||||
if (this.updateTags) {
|
||||
console.log('watch: tags');
|
||||
|
||||
let shortList = [];
|
||||
for (let key in value) {
|
||||
if (value.hasOwnProperty(key)) {
|
||||
shortList.push({text: value[key].text});
|
||||
}
|
||||
}
|
||||
this.value = shortList;
|
||||
this.tagList = shortList;
|
||||
}
|
||||
this.updateTags = true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'updateField',
|
||||
],
|
||||
),
|
||||
initItems() {
|
||||
if (this.currentTag.length < 2) {
|
||||
return;
|
||||
|
1
frontend/src/i18n.js
vendored
1
frontend/src/i18n.js
vendored
@@ -40,6 +40,7 @@ module.exports = new vuei18n({
|
||||
'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'),
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"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",
|
||||
@@ -56,6 +58,7 @@
|
||||
"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",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "\u0422\u0440\u0438\u043c\u0435\u0441\u0435\u0447\u043d\u0438 \u0431\u044e\u0434\u0436\u0435\u0442\u0438",
|
||||
"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.",
|
||||
"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",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "Go to expenses",
|
||||
"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": "Saved",
|
||||
"piggy_banks": "Pokladni\u010dky",
|
||||
@@ -56,6 +58,7 @@
|
||||
"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": "Daily budgets",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Quarterly budgets",
|
||||
"half_year_budgets": "Half-yearly budgets",
|
||||
"yearly_budgets": "Yearly budgets",
|
||||
"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": "Update transaction",
|
||||
"after_update_create_another": "After updating, return here to continue editing.",
|
||||
"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": "Um\u00edst\u011bn\u00ed",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Transaction links",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "Zu Ausgaben wechseln",
|
||||
"go_to_bills": "Rechnungen anzeigen",
|
||||
"bills": "Rechnungen",
|
||||
"last_thirty_days": "Letzte 30 Tage",
|
||||
"last_seven_days": "Letzte sieben Tage",
|
||||
"go_to_piggies": "Sparschweine anzeigen",
|
||||
"saved": "Gespeichert",
|
||||
"piggy_banks": "Sparschweine",
|
||||
@@ -56,6 +58,7 @@
|
||||
"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",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Quartalsbudgets",
|
||||
"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.",
|
||||
"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",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"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",
|
||||
@@ -56,6 +58,7 @@
|
||||
"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",
|
||||
@@ -64,6 +67,24 @@
|
||||
"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",
|
||||
"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": "Store transaction",
|
||||
"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.",
|
||||
"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": "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}\">\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": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"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",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"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",
|
||||
@@ -56,6 +58,7 @@
|
||||
"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",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Quarterly budgets",
|
||||
"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.",
|
||||
"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",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"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",
|
||||
@@ -56,6 +58,7 @@
|
||||
"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",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Quarterly budgets",
|
||||
"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.",
|
||||
"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",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"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",
|
||||
@@ -56,6 +58,7 @@
|
||||
"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",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Presupuestos trimestrales",
|
||||
"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.",
|
||||
"reset_after": "Restablecer formulario despu\u00e9s del env\u00edo",
|
||||
"bill_paid_on": "Pagado el {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}\">La transacci\u00f3n #{ID} (\"{title}\")<\/a> ha sido almacenada.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"location": "Ubicaci\u00f3n",
|
||||
"other_budgets": "Presupuestos de tiempo personalizado",
|
||||
"journal_links": "Enlaces de transacciones",
|
||||
"go_to_withdrawals": "Ir a tus retiradas",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"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",
|
||||
@@ -56,6 +58,7 @@
|
||||
"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",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Quarterly budgets",
|
||||
"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.",
|
||||
"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",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"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",
|
||||
@@ -56,6 +58,7 @@
|
||||
"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",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Budgets trimestriels",
|
||||
"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.",
|
||||
"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",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"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",
|
||||
@@ -56,6 +58,7 @@
|
||||
"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",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Quarterly budgets",
|
||||
"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.",
|
||||
"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",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "Vai alle spese",
|
||||
"go_to_bills": "Vai alle tue bollette",
|
||||
"bills": "Bollette",
|
||||
"last_thirty_days": "Ultimi trenta giorni",
|
||||
"last_seven_days": "Ultimi sette giorni",
|
||||
"go_to_piggies": "Vai ai tuoi salvadanai",
|
||||
"saved": "Salvata",
|
||||
"piggy_banks": "Salvadanai",
|
||||
@@ -56,6 +58,7 @@
|
||||
"account_role_defaultAsset": "Conto attivit\u00e0 predefinito",
|
||||
"account_role_savingAsset": "Conto risparmio",
|
||||
"account_role_sharedAsset": "Conto attivit\u00e0 condiviso",
|
||||
"clear_location": "Rimuovi dalla posizione",
|
||||
"account_role_ccAsset": "Carta di credito",
|
||||
"account_role_cashWalletAsset": "Portafoglio",
|
||||
"daily_budgets": "Budget giornalieri",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Bilanci trimestrali",
|
||||
"half_year_budgets": "Bilanci semestrali",
|
||||
"yearly_budgets": "Budget annuali",
|
||||
"split_transaction_title": "Descrizione della transazione suddivisa",
|
||||
"errors_submission": "Errore durante l'invio. Controlla gli errori segnalati qui sotto.",
|
||||
"flash_error": "Errore!",
|
||||
"store_transaction": "Salva transazione",
|
||||
"flash_success": "Successo!",
|
||||
"create_another": "Dopo il salvataggio, torna qui per crearne un'altra.",
|
||||
"update_transaction": "Aggiorna transazione",
|
||||
"after_update_create_another": "Dopo l'aggiornamento, torna qui per continuare la modifica.",
|
||||
"reset_after": "Resetta il modulo dopo l'invio",
|
||||
"bill_paid_on": "Pagata il {date}",
|
||||
"first_split_decides": "La prima suddivisione determina il valore di questo campo",
|
||||
"first_split_overrules_source": "La prima suddivisione potrebbe sovrascrivere l'account di origine",
|
||||
"first_split_overrules_destination": "La prima suddivisione potrebbe sovrascrivere l'account di destinazione",
|
||||
"transaction_stored_link": "La <a href=\"transactions\/show\/{ID}\">transazione #{ID} (\"{title}\")<\/a> \u00e8 stata salvata.",
|
||||
"custom_period": "Periodo personalizzato",
|
||||
"reset_to_current": "Ripristina il periodo corrente",
|
||||
"select_period": "Seleziona il periodo",
|
||||
"location": "Posizione",
|
||||
"other_budgets": "Budget a periodi personalizzati",
|
||||
"journal_links": "Collegamenti della transazione",
|
||||
"go_to_withdrawals": "Vai ai tuoi prelievi",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "Go to expenses",
|
||||
"go_to_bills": "G\u00e5 til regningene dine",
|
||||
"bills": "Regninger",
|
||||
"last_thirty_days": "Tredve siste dager",
|
||||
"last_seven_days": "Syv siste dager",
|
||||
"go_to_piggies": "G\u00e5 til sparegrisene dine",
|
||||
"saved": "Saved",
|
||||
"piggy_banks": "Sparegriser",
|
||||
@@ -56,6 +58,7 @@
|
||||
"account_role_defaultAsset": "Standard aktivakonto",
|
||||
"account_role_savingAsset": "Sparekonto",
|
||||
"account_role_sharedAsset": "Delt aktivakonto",
|
||||
"clear_location": "T\u00f8m lokasjon",
|
||||
"account_role_ccAsset": "Kredittkort",
|
||||
"account_role_cashWalletAsset": "Kontant lommebok",
|
||||
"daily_budgets": "Daily budgets",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Quarterly budgets",
|
||||
"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": "Feil!",
|
||||
"store_transaction": "Store transaction",
|
||||
"flash_success": "Suksess!",
|
||||
"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.",
|
||||
"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": "Sted",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Transaksjonskoblinger",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "Ga naar je uitgaven",
|
||||
"go_to_bills": "Ga naar je contracten",
|
||||
"bills": "Contracten",
|
||||
"last_thirty_days": "Laatste dertig dagen",
|
||||
"last_seven_days": "Laatste zeven dagen",
|
||||
"go_to_piggies": "Ga naar je spaarpotjes",
|
||||
"saved": "Opgeslagen",
|
||||
"piggy_banks": "Spaarpotjes",
|
||||
@@ -56,6 +58,7 @@
|
||||
"account_role_defaultAsset": "Standaard betaalrekening",
|
||||
"account_role_savingAsset": "Spaarrekening",
|
||||
"account_role_sharedAsset": "Gedeelde betaalrekening",
|
||||
"clear_location": "Wis locatie",
|
||||
"account_role_ccAsset": "Credit card",
|
||||
"account_role_cashWalletAsset": "Cash",
|
||||
"daily_budgets": "Dagelijkse budgetten",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Driemaandelijkse budgetten",
|
||||
"half_year_budgets": "Halfjaarlijkse budgetten",
|
||||
"yearly_budgets": "Jaarlijkse budgetten",
|
||||
"split_transaction_title": "Beschrijving van de gesplitste transactie",
|
||||
"errors_submission": "Er ging iets mis. Check de errors.",
|
||||
"flash_error": "Fout!",
|
||||
"store_transaction": "Transactie opslaan",
|
||||
"flash_success": "Gelukt!",
|
||||
"create_another": "Terug naar deze pagina voor een nieuwe transactie.",
|
||||
"update_transaction": "Update transactie",
|
||||
"after_update_create_another": "Na het opslaan terug om door te gaan met wijzigen.",
|
||||
"reset_after": "Reset formulier na opslaan",
|
||||
"bill_paid_on": "Betaald op {date}",
|
||||
"first_split_decides": "De eerste split bepaalt wat hier staat",
|
||||
"first_split_overrules_source": "De eerste split kan de bronrekening overschrijven",
|
||||
"first_split_overrules_destination": "De eerste split kan de doelrekening overschrijven",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transactie #{ID} (\"{title}\")<\/a> is opgeslagen.",
|
||||
"custom_period": "Aangepaste periode",
|
||||
"reset_to_current": "Reset naar huidige periode",
|
||||
"select_period": "Selecteer een periode",
|
||||
"location": "Plaats",
|
||||
"other_budgets": "Aangepaste budgetten",
|
||||
"journal_links": "Transactiekoppelingen",
|
||||
"go_to_withdrawals": "Ga naar je uitgaven",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "Przejd\u017a do wydatk\u00f3w",
|
||||
"go_to_bills": "Przejd\u017a do swoich rachunk\u00f3w",
|
||||
"bills": "Rachunki",
|
||||
"last_thirty_days": "Ostanie 30 dni",
|
||||
"last_seven_days": "Ostatnie 7 dni",
|
||||
"go_to_piggies": "Przejd\u017a do swoich skarbonek",
|
||||
"saved": "Zapisano",
|
||||
"piggy_banks": "Skarbonki",
|
||||
@@ -56,6 +58,7 @@
|
||||
"account_role_defaultAsset": "Domy\u015blne konto aktyw\u00f3w",
|
||||
"account_role_savingAsset": "Konto oszcz\u0119dno\u015bciowe",
|
||||
"account_role_sharedAsset": "Wsp\u00f3\u0142dzielone konto aktyw\u00f3w",
|
||||
"clear_location": "Wyczy\u015b\u0107 lokalizacj\u0119",
|
||||
"account_role_ccAsset": "Karta kredytowa",
|
||||
"account_role_cashWalletAsset": "Portfel got\u00f3wkowy",
|
||||
"daily_budgets": "Bud\u017cety dzienne",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Bud\u017cety kwartalne",
|
||||
"half_year_budgets": "Bud\u017cety p\u00f3\u0142roczne",
|
||||
"yearly_budgets": "Bud\u017cety roczne",
|
||||
"split_transaction_title": "Opis podzielonej transakcji",
|
||||
"errors_submission": "Co\u015b posz\u0142o nie tak w czasie zapisu. Prosz\u0119 sprawd\u017a b\u0142\u0119dy.",
|
||||
"flash_error": "B\u0142\u0105d!",
|
||||
"store_transaction": "Zapisz transakcj\u0119",
|
||||
"flash_success": "Sukces!",
|
||||
"create_another": "Po zapisaniu wr\u00f3\u0107 tutaj, aby utworzy\u0107 kolejny.",
|
||||
"update_transaction": "Zaktualizuj transakcj\u0119",
|
||||
"after_update_create_another": "Po aktualizacji wr\u00f3\u0107 tutaj, aby kontynuowa\u0107 edycj\u0119.",
|
||||
"reset_after": "Wyczy\u015b\u0107 formularz po zapisaniu",
|
||||
"bill_paid_on": "Zap\u0142acone {date}",
|
||||
"first_split_decides": "Pierwszy podzia\u0142 okre\u015bla warto\u015b\u0107 tego pola",
|
||||
"first_split_overrules_source": "Pierwszy podzia\u0142 mo\u017ce nadpisa\u0107 konto \u017ar\u00f3d\u0142owe",
|
||||
"first_split_overrules_destination": "Pierwszy podzia\u0142 mo\u017ce nadpisa\u0107 konto docelowe",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transakcja #{ID} (\"{title}\")<\/a> zosta\u0142a zapisana.",
|
||||
"custom_period": "Okres niestandardowy",
|
||||
"reset_to_current": "Przywr\u00f3\u0107 do bie\u017c\u0105cego okresu",
|
||||
"select_period": "Wybierz okres",
|
||||
"location": "Lokalizacja",
|
||||
"other_budgets": "Bud\u017cety niestandardowe",
|
||||
"journal_links": "Powi\u0105zane transakcje",
|
||||
"go_to_withdrawals": "Przejd\u017a do swoich wydatk\u00f3w",
|
||||
|
@@ -16,12 +16,12 @@
|
||||
"transaction_journal_extra": "Informa\u00e7\u00e3o extra",
|
||||
"transaction_journal_meta": "Meta-informa\u00e7\u00e3o",
|
||||
"basic_journal_information": "Informa\u00e7\u00f5es b\u00e1sicas de transa\u00e7\u00e3o",
|
||||
"bills_to_pay": "Faturas a pagar",
|
||||
"bills_to_pay": "Contas a pagar",
|
||||
"left_to_spend": "Restante para gastar",
|
||||
"attachments": "Anexos",
|
||||
"net_worth": "Valor L\u00edquido",
|
||||
"bill": "Fatura",
|
||||
"no_bill": "(sem fatura)",
|
||||
"no_bill": "(sem conta)",
|
||||
"tags": "Tags",
|
||||
"internal_reference": "Refer\u00eancia interna",
|
||||
"external_url": "URL externa",
|
||||
@@ -41,12 +41,14 @@
|
||||
"categories": "Categorias",
|
||||
"go_to_budgets": "V\u00e1 para seus or\u00e7amentos",
|
||||
"income": "Receita \/ Renda",
|
||||
"go_to_deposits": "Ir para os dep\u00f3sitos",
|
||||
"go_to_deposits": "Ir para as entradas",
|
||||
"go_to_categories": "V\u00e1 para suas categorias",
|
||||
"expense_accounts": "Contas de despesas",
|
||||
"go_to_expenses": "Ir para despesas",
|
||||
"go_to_bills": "V\u00e1 para suas faturas",
|
||||
"go_to_bills": "V\u00e1 para suas contas",
|
||||
"bills": "Faturas",
|
||||
"last_thirty_days": "\u00daltimos 30 dias",
|
||||
"last_seven_days": "\u00daltimos sete dias",
|
||||
"go_to_piggies": "V\u00e1 para sua poupan\u00e7a",
|
||||
"saved": "Salvo",
|
||||
"piggy_banks": "Cofrinhos",
|
||||
@@ -56,6 +58,7 @@
|
||||
"account_role_defaultAsset": "Conta padr\u00e3o",
|
||||
"account_role_savingAsset": "Conta poupan\u00e7a",
|
||||
"account_role_sharedAsset": "Contas de ativos compartilhadas",
|
||||
"clear_location": "Limpar localiza\u00e7\u00e3o",
|
||||
"account_role_ccAsset": "Cart\u00e3o de cr\u00e9dito",
|
||||
"account_role_cashWalletAsset": "Carteira de dinheiro",
|
||||
"daily_budgets": "Or\u00e7amentos di\u00e1rios",
|
||||
@@ -64,7 +67,25 @@
|
||||
"quarterly_budgets": "Or\u00e7amentos trimestrais",
|
||||
"half_year_budgets": "Or\u00e7amentos semestrais",
|
||||
"yearly_budgets": "Or\u00e7amentos anuais",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"split_transaction_title": "Descri\u00e7\u00e3o da transa\u00e7\u00e3o dividida",
|
||||
"errors_submission": "H\u00e1 algo de errado com o seu envio. Por favor, verifique os erros abaixo.",
|
||||
"flash_error": "Erro!",
|
||||
"store_transaction": "Salvar transa\u00e7\u00e3o",
|
||||
"flash_success": "Sucesso!",
|
||||
"create_another": "Depois de armazenar, retorne aqui para criar outro.",
|
||||
"update_transaction": "Atualizar transa\u00e7\u00e3o",
|
||||
"after_update_create_another": "Depois de atualizar, retorne aqui para continuar editando.",
|
||||
"reset_after": "Resetar o formul\u00e1rio ap\u00f3s o envio",
|
||||
"bill_paid_on": "Pago em {date}",
|
||||
"first_split_decides": "A primeira divis\u00e3o determina o valor deste campo",
|
||||
"first_split_overrules_source": "A primeira divis\u00e3o pode anular a conta de origem",
|
||||
"first_split_overrules_destination": "A primeira divis\u00e3o pode anular a conta de destino",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transa\u00e7\u00e3o #{ID} (\"{title}\")<\/a> foi salva.",
|
||||
"custom_period": "Per\u00edodo personalizado",
|
||||
"reset_to_current": "Redefinir para o per\u00edodo atual",
|
||||
"select_period": "Selecione um per\u00edodo",
|
||||
"location": "Localiza\u00e7\u00e3o",
|
||||
"other_budgets": "Or\u00e7amentos de per\u00edodos personalizados",
|
||||
"journal_links": "Transa\u00e7\u00f5es ligadas",
|
||||
"go_to_withdrawals": "V\u00e1 para seus saques",
|
||||
"revenue_accounts": "Contas de receitas",
|
||||
|
118
frontend/src/locales/pt.json
Normal file
118
frontend/src/locales/pt.json
Normal file
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"firefly": {
|
||||
"Transfer": "Transferencia",
|
||||
"Withdrawal": "Levantamento",
|
||||
"Deposit": "Deposito",
|
||||
"date_and_time": "Data e hora",
|
||||
"no_currency": "(sem moeda)",
|
||||
"date": "Data",
|
||||
"time": "Hora",
|
||||
"no_budget": "(sem orcamento)",
|
||||
"destination_account": "Conta de destino",
|
||||
"source_account": "Conta de origem",
|
||||
"single_split": "Dividir",
|
||||
"create_new_transaction": "Criar uma nova transa\u00e7\u00e3o",
|
||||
"balance": "Saldo",
|
||||
"transaction_journal_extra": "Informa\u00e7\u00f5es extra",
|
||||
"transaction_journal_meta": "Informacao",
|
||||
"basic_journal_information": "Informa\u00e7\u00f5es b\u00e1sicas de transa\u00e7\u00e3o",
|
||||
"bills_to_pay": "Contas por pagar",
|
||||
"left_to_spend": "Restante para gastar",
|
||||
"attachments": "Anexos",
|
||||
"net_worth": "Patrimonio liquido",
|
||||
"bill": "Conta",
|
||||
"no_bill": "(sem contas)",
|
||||
"tags": "Tags",
|
||||
"internal_reference": "Refer\u00eancia interna",
|
||||
"external_url": "URL Externo",
|
||||
"no_piggy_bank": "(nenhum mealheiro)",
|
||||
"paid": "Pago",
|
||||
"notes": "Notas",
|
||||
"yourAccounts": "As suas contas",
|
||||
"go_to_asset_accounts": "Ver as contas de activos",
|
||||
"transaction_table_description": "Uma tabela com as suas transac\u00e7\u00f5es",
|
||||
"account": "Conta",
|
||||
"description": "Descricao",
|
||||
"amount": "Montante",
|
||||
"budget": "Orcamento",
|
||||
"category": "Categoria",
|
||||
"opposing_account": "Conta oposta",
|
||||
"budgets": "Orcamentos",
|
||||
"categories": "Categorias",
|
||||
"go_to_budgets": "Ir para orcamentos",
|
||||
"income": "Receita \/ renda",
|
||||
"go_to_deposits": "Ir para dep\u00f3sitos",
|
||||
"go_to_categories": "Ir para categorias",
|
||||
"expense_accounts": "Conta de despesas",
|
||||
"go_to_expenses": "Ir para despesas",
|
||||
"go_to_bills": "Ir para contas",
|
||||
"bills": "Contas",
|
||||
"last_thirty_days": "\u00daltimos trinta dias",
|
||||
"last_seven_days": "\u00daltimos sete dias",
|
||||
"go_to_piggies": "Ir para mealheiros",
|
||||
"saved": "Guardado",
|
||||
"piggy_banks": "Mealheiros",
|
||||
"piggy_bank": "Mealheiro",
|
||||
"amounts": "Montantes",
|
||||
"Default asset account": "Conta de activos padr\u00e3o",
|
||||
"account_role_defaultAsset": "Conta de activos padr\u00e3o",
|
||||
"account_role_savingAsset": "Contas poupan\u00e7a",
|
||||
"account_role_sharedAsset": "Conta de activos partilhados",
|
||||
"clear_location": "Limpar localiza\u00e7\u00e3o",
|
||||
"account_role_ccAsset": "Cart\u00e3o de credito",
|
||||
"account_role_cashWalletAsset": "Carteira de dinheiro",
|
||||
"daily_budgets": "Or\u00e7amento di\u00e1rio",
|
||||
"weekly_budgets": "Or\u00e7amento semanal",
|
||||
"monthly_budgets": "Or\u00e7amento mensal",
|
||||
"quarterly_budgets": "Or\u00e7amento trimestral",
|
||||
"half_year_budgets": "Or\u00e7amento semestral",
|
||||
"yearly_budgets": "Or\u00e7amento anual",
|
||||
"split_transaction_title": "Descri\u00e7\u00e3o da transac\u00e7\u00e3o dividida",
|
||||
"errors_submission": "Aconteceu algo errado com a sua submiss\u00e3o. Por favor, verifique os erros.",
|
||||
"flash_error": "Erro!",
|
||||
"store_transaction": "Guardar transa\u00e7\u00e3o",
|
||||
"flash_success": "Sucesso!",
|
||||
"create_another": "Depois de guardar, voltar aqui para criar outra.",
|
||||
"update_transaction": "Actualizar transac\u00e7\u00e3o",
|
||||
"after_update_create_another": "Ap\u00f3s a atualiza\u00e7\u00e3o, regresse aqui para continuar a editar.",
|
||||
"reset_after": "Repor o formul\u00e1rio ap\u00f3s o envio",
|
||||
"bill_paid_on": "Pago a {date}",
|
||||
"first_split_decides": "A primeira divis\u00e3o determina o valor deste campo",
|
||||
"first_split_overrules_source": "A primeira divis\u00e3o pode anular a conta de origem",
|
||||
"first_split_overrules_destination": "A primeira divis\u00e3o pode anular a conta de destino",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transa\u00e7\u00e3o #{ID} (\"{title}\")<\/a> foi guardada.",
|
||||
"custom_period": "Per\u00edodo personalizado",
|
||||
"reset_to_current": "Reiniciar o per\u00edodo personalizado",
|
||||
"select_period": "Selecionar um per\u00edodo",
|
||||
"location": "Localiza\u00e7\u00e3o",
|
||||
"other_budgets": "Or\u00e7amentos de tempo personalizado",
|
||||
"journal_links": "Liga\u00e7\u00f5es de transac\u00e7\u00e3o",
|
||||
"go_to_withdrawals": "Ir para os seus levantamentos",
|
||||
"revenue_accounts": "Conta de receitas",
|
||||
"add_another_split": "Adicionar outra divis\u00e3o"
|
||||
},
|
||||
"list": {
|
||||
"piggy_bank": "Mealheiro",
|
||||
"percentage": "%.",
|
||||
"amount": "Montante",
|
||||
"name": "Nome",
|
||||
"role": "Regra",
|
||||
"iban": "IBAN",
|
||||
"lastActivity": "Ultima actividade",
|
||||
"currentBalance": "Saldo actual",
|
||||
"balanceDiff": "Diferenca de saldo",
|
||||
"next_expected_match": "Proxima correspondencia esperada"
|
||||
},
|
||||
"config": {
|
||||
"html_language": "pt"
|
||||
},
|
||||
"form": {
|
||||
"foreign_amount": "Montante estrangeiro",
|
||||
"interest_date": "Data de juros",
|
||||
"book_date": "Data de registo",
|
||||
"process_date": "Data de processamento",
|
||||
"due_date": "Data de vencimento",
|
||||
"payment_date": "Data de pagamento",
|
||||
"invoice_date": "Data da factura"
|
||||
}
|
||||
}
|
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "Go to expenses",
|
||||
"go_to_bills": "Mergi la facturi",
|
||||
"bills": "Facturi",
|
||||
"last_thirty_days": "Ultimele 30 de zile",
|
||||
"last_seven_days": "Ultimele 7 zile",
|
||||
"go_to_piggies": "Mergi la pu\u0219culi\u021b\u0103",
|
||||
"saved": "Salvat",
|
||||
"piggy_banks": "Pu\u0219culi\u021b\u0103",
|
||||
@@ -56,6 +58,7 @@
|
||||
"account_role_defaultAsset": "Contul implicit activ",
|
||||
"account_role_savingAsset": "Cont de economii",
|
||||
"account_role_sharedAsset": "Contul de active partajat",
|
||||
"clear_location": "\u0218terge\u021bi loca\u021bia",
|
||||
"account_role_ccAsset": "Card de credit",
|
||||
"account_role_cashWalletAsset": "Cash - Numerar",
|
||||
"daily_budgets": "Daily budgets",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Quarterly budgets",
|
||||
"half_year_budgets": "Half-yearly budgets",
|
||||
"yearly_budgets": "Yearly budgets",
|
||||
"split_transaction_title": "Descrierea tranzac\u021biei divizate",
|
||||
"errors_submission": "There was something wrong with your submission. Please check out the errors.",
|
||||
"flash_error": "Eroare!",
|
||||
"store_transaction": "Store transaction",
|
||||
"flash_success": "Succes!",
|
||||
"create_another": "Dup\u0103 stocare, reveni\u021bi aici pentru a crea alta.",
|
||||
"update_transaction": "Actualiza\u021bi tranzac\u021bia",
|
||||
"after_update_create_another": "Dup\u0103 actualizare, reveni\u021bi aici pentru a continua editarea.",
|
||||
"reset_after": "Reseta\u021bi formularul dup\u0103 trimitere",
|
||||
"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}\">Tranzac\u021bia #{ID} (\"{title}\")<\/a> a fost stocat\u0103.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"location": "Loca\u021bie",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Link-uri de tranzac\u021bii",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@@ -23,8 +23,8 @@
|
||||
"bill": "\u0421\u0447\u0451\u0442 \u043a \u043e\u043f\u043b\u0430\u0442\u0435",
|
||||
"no_bill": "(\u043d\u0435\u0442 \u0441\u0447\u0451\u0442\u0430 \u043d\u0430 \u043e\u043f\u043b\u0430\u0442\u0443)",
|
||||
"tags": "\u041c\u0435\u0442\u043a\u0438",
|
||||
"internal_reference": "Internal reference",
|
||||
"external_url": "External URL",
|
||||
"internal_reference": "\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044f\u044f \u0441\u0441\u044b\u043b\u043a\u0430",
|
||||
"external_url": "\u0412\u043d\u0435\u0448\u043d\u0438\u0439 URL-\u0430\u0434\u0440\u0435\u0441",
|
||||
"no_piggy_bank": "(\u043d\u0435\u0442 \u043a\u043e\u043f\u0438\u043b\u043a\u0438)",
|
||||
"paid": "\u041e\u043f\u043b\u0430\u0447\u0435\u043d\u043e",
|
||||
"notes": "\u0417\u0430\u043c\u0435\u0442\u043a\u0438",
|
||||
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "\u041f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u0440\u0430\u0441\u0445\u043e\u0434\u0430\u043c",
|
||||
"go_to_bills": "\u041f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u0432\u0430\u0448\u0438\u043c \u0441\u0447\u0435\u0442\u0430\u043c \u043d\u0430 \u043e\u043f\u043b\u0430\u0442\u0443",
|
||||
"bills": "\u0421\u0447\u0435\u0442\u0430 \u043a \u043e\u043f\u043b\u0430\u0442\u0435",
|
||||
"last_thirty_days": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 30 \u0434\u043d\u0435\u0439",
|
||||
"last_seven_days": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 7 \u0434\u043d\u0435\u0439",
|
||||
"go_to_piggies": "\u041f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u0432\u0430\u0448\u0438\u043c \u043a\u043e\u043f\u0438\u043b\u043a\u0430\u043c",
|
||||
"saved": "\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e",
|
||||
"piggy_banks": "\u041a\u043e\u043f\u0438\u043b\u043a\u0438",
|
||||
@@ -56,19 +58,38 @@
|
||||
"account_role_defaultAsset": "\u0421\u0447\u0451\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e",
|
||||
"account_role_savingAsset": "\u0421\u0431\u0435\u0440\u0435\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0447\u0435\u0442",
|
||||
"account_role_sharedAsset": "\u041e\u0431\u0449\u0438\u0439 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u0447\u0451\u0442",
|
||||
"clear_location": "\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435",
|
||||
"account_role_ccAsset": "\u041a\u0440\u0435\u0434\u0438\u0442\u043d\u0430\u044f \u043a\u0430\u0440\u0442\u0430",
|
||||
"account_role_cashWalletAsset": "\u041a\u043e\u0448\u0435\u043b\u0451\u043a \u0441 \u043d\u0430\u043b\u0438\u0447\u043d\u044b\u043c\u0438",
|
||||
"account_role_cashWalletAsset": "\u041d\u0430\u043b\u0438\u0447\u043d\u044b\u0435",
|
||||
"daily_budgets": "\u0411\u044e\u0434\u0436\u0435\u0442\u044b \u043d\u0430 \u0434\u0435\u043d\u044c",
|
||||
"weekly_budgets": "\u0411\u044e\u0434\u0436\u0435\u0442\u044b \u043d\u0430 \u043d\u0435\u0434\u0435\u043b\u044e",
|
||||
"monthly_budgets": "\u0411\u044e\u0434\u0436\u0435\u0442\u044b \u043d\u0430 \u043c\u0435\u0441\u044f\u0446",
|
||||
"quarterly_budgets": "\u0411\u044e\u0434\u0436\u0435\u0442\u044b \u043d\u0430 \u043a\u0432\u0430\u0440\u0442\u0430\u043b",
|
||||
"half_year_budgets": "\u0411\u044e\u0434\u0436\u0435\u0442\u044b \u043d\u0430 \u043f\u043e\u043b\u0433\u043e\u0434\u0430",
|
||||
"yearly_budgets": "\u0413\u043e\u0434\u043e\u0432\u044b\u0435 \u0431\u044e\u0434\u0436\u0435\u0442\u044b",
|
||||
"split_transaction_title": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u0439 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438",
|
||||
"errors_submission": "\u041f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0448\u043b\u043e \u043d\u0435 \u0442\u0430\u043a. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u043d\u0438\u0436\u0435.",
|
||||
"flash_error": "\u041e\u0448\u0438\u0431\u043a\u0430!",
|
||||
"store_transaction": "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e",
|
||||
"flash_success": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e!",
|
||||
"create_another": "\u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u0441\u044e\u0434\u0430 \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0435\u0449\u0451 \u043e\u0434\u043d\u0443 \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c.",
|
||||
"update_transaction": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e",
|
||||
"after_update_create_another": "\u041f\u043e\u0441\u043b\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u0435\u0440\u043d\u0438\u0442\u0435\u0441\u044c \u0441\u044e\u0434\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.",
|
||||
"reset_after": "\u0421\u0431\u0440\u043e\u0441\u0438\u0442\u044c \u0444\u043e\u0440\u043c\u0443 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438",
|
||||
"bill_paid_on": "\u041e\u043f\u043b\u0430\u0447\u0435\u043d\u043e {date}",
|
||||
"first_split_decides": "\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043f\u043e\u043b\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0439 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438",
|
||||
"first_split_overrules_source": "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 \u043c\u043e\u0436\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0447\u0435\u0442 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430",
|
||||
"first_split_overrules_destination": "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 \u043c\u043e\u0436\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0447\u0435\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">\u0422\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f #{ID} (\"{title}\")<\/a> \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0430.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"location": "\u0420\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u0435",
|
||||
"other_budgets": "\u0411\u044e\u0434\u0436\u0435\u0442\u044b \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u043e\u0442\u0440\u0435\u0437\u043e\u043a \u0432\u0440\u0435\u043c\u0435\u043d\u0438",
|
||||
"journal_links": "\u0421\u0432\u044f\u0437\u0438 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438",
|
||||
"go_to_withdrawals": "\u041f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u0432\u0430\u0448\u0438\u043c \u0440\u0430\u0441\u0445\u043e\u0434\u0430\u043c",
|
||||
"revenue_accounts": "\u0421\u0447\u0435\u0442\u0430 \u0434\u043e\u0445\u043e\u0434\u043e\u0432",
|
||||
"add_another_split": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u0447\u0430\u0441\u0442\u044c"
|
||||
"add_another_split": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0449\u0435 \u043e\u0434\u043d\u0443 \u0447\u0430\u0441\u0442\u044c"
|
||||
},
|
||||
"list": {
|
||||
"piggy_bank": "\u041a\u043e\u043f\u0438\u043b\u043a\u0430",
|
||||
@@ -87,10 +108,10 @@
|
||||
},
|
||||
"form": {
|
||||
"foreign_amount": "\u0421\u0443\u043c\u043c\u0430 \u0432 \u0438\u043d\u043e\u0441\u0442\u0440\u0430\u043d\u043d\u043e\u0439 \u0432\u0430\u043b\u044e\u0442\u0435",
|
||||
"interest_date": "\u0414\u0430\u0442\u0430 \u0432\u044b\u043f\u043b\u0430\u0442\u044b",
|
||||
"interest_date": "\u0414\u0430\u0442\u0430 \u043d\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u043e\u0432",
|
||||
"book_date": "\u0414\u0430\u0442\u0430 \u0431\u0440\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f",
|
||||
"process_date": "\u0414\u0430\u0442\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438",
|
||||
"due_date": "\u0421\u0440\u043e\u043a",
|
||||
"due_date": "\u0421\u0440\u043e\u043a \u043e\u043f\u043b\u0430\u0442\u044b",
|
||||
"payment_date": "\u0414\u0430\u0442\u0430 \u043f\u043b\u0430\u0442\u0435\u0436\u0430",
|
||||
"invoice_date": "\u0414\u0430\u0442\u0430 \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0447\u0451\u0442\u0430"
|
||||
}
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "Zobrazi\u0165 v\u00fddavky",
|
||||
"go_to_bills": "Zobrazi\u0165 \u00fa\u010dty",
|
||||
"bills": "\u00da\u010dty",
|
||||
"last_thirty_days": "Uplynul\u00fdch 30 dn\u00ed",
|
||||
"last_seven_days": "Uplynul\u00fdch 7 dn\u00ed",
|
||||
"go_to_piggies": "Zobrazi\u0165 pokladni\u010dky",
|
||||
"saved": "Ulo\u017een\u00e9",
|
||||
"piggy_banks": "Pokladni\u010dky",
|
||||
@@ -56,6 +58,7 @@
|
||||
"account_role_defaultAsset": "Predvolen\u00fd \u00fa\u010det akt\u00edv",
|
||||
"account_role_savingAsset": "\u0160etriaci \u00fa\u010det",
|
||||
"account_role_sharedAsset": "Zdie\u013ean\u00fd \u00fa\u010det akt\u00edv",
|
||||
"clear_location": "Odstr\u00e1ni\u0165 poz\u00edciu",
|
||||
"account_role_ccAsset": "Kreditn\u00e1 karta",
|
||||
"account_role_cashWalletAsset": "Pe\u0148a\u017eenka",
|
||||
"daily_budgets": "Denn\u00e9 rozpo\u010dty",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "\u0160tvr\u0165ro\u010dn\u00e9 rozpo\u010dty",
|
||||
"half_year_budgets": "Polro\u010dn\u00e9 rozpo\u010dty",
|
||||
"yearly_budgets": "Ro\u010dn\u00e9 rozpo\u010dty",
|
||||
"split_transaction_title": "Popis roz\u00fa\u010dtovania",
|
||||
"errors_submission": "Pri odosielan\u00ed sa nie\u010do nepodarilo. Skontrolujte pros\u00edm chyby.",
|
||||
"flash_error": "Chyba!",
|
||||
"store_transaction": "Ulo\u017ei\u0165 transakciu",
|
||||
"flash_success": "Hotovo!",
|
||||
"create_another": "Po ulo\u017een\u00ed sa vr\u00e1ti\u0165 sp\u00e4\u0165 sem a vytvori\u0165 \u010fal\u0161\u00ed.",
|
||||
"update_transaction": "Upravi\u0165 transakciu",
|
||||
"after_update_create_another": "Po aktualiz\u00e1cii sa vr\u00e1ti\u0165 sp\u00e4\u0165 a pokra\u010dova\u0165 v \u00faprav\u00e1ch.",
|
||||
"reset_after": "Po odoslan\u00ed vynulova\u0165 formul\u00e1r",
|
||||
"bill_paid_on": "Uhraden\u00e9 {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}\">Transakcia #{ID} (\"{title}\")<\/a> bola ulo\u017een\u00e1.",
|
||||
"custom_period": "Vlastn\u00e9 obdobie",
|
||||
"reset_to_current": "Obnovi\u0165 na aktu\u00e1lne obdobie",
|
||||
"select_period": "Vyberte obdobie",
|
||||
"location": "Poloha",
|
||||
"other_budgets": "\u0160pecifick\u00e9 \u010dasovan\u00e9 rozpo\u010dty",
|
||||
"journal_links": "Prepojenia transakcie",
|
||||
"go_to_withdrawals": "Zobrazi\u0165 v\u00fdbery",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "G\u00e5 till utgifter",
|
||||
"go_to_bills": "G\u00e5 till dina notor",
|
||||
"bills": "Notor",
|
||||
"last_thirty_days": "Senaste 30 dagarna",
|
||||
"last_seven_days": "Senaste 7 dagarna",
|
||||
"go_to_piggies": "G\u00e5 till dina sparb\u00f6ssor",
|
||||
"saved": "Sparad",
|
||||
"piggy_banks": "Spargrisar",
|
||||
@@ -56,6 +58,7 @@
|
||||
"account_role_defaultAsset": "F\u00f6rvalt tillg\u00e5ngskonto",
|
||||
"account_role_savingAsset": "Sparkonto",
|
||||
"account_role_sharedAsset": "Delat tillg\u00e5ngskonto",
|
||||
"clear_location": "Rena plats",
|
||||
"account_role_ccAsset": "Kreditkort",
|
||||
"account_role_cashWalletAsset": "Pl\u00e5nbok",
|
||||
"daily_budgets": "Dagliga budgetar",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Kvartalsbudgetar",
|
||||
"half_year_budgets": "Halv\u00e5rsbudgetar",
|
||||
"yearly_budgets": "\u00c5rliga budgetar",
|
||||
"split_transaction_title": "Beskrivning av delad transaktion",
|
||||
"errors_submission": "N\u00e5got fel uppstod med inskickningen. V\u00e4nligen kontrollera felen nedan.",
|
||||
"flash_error": "Fel!",
|
||||
"store_transaction": "Store transaction",
|
||||
"flash_success": "Slutf\u00f6rd!",
|
||||
"create_another": "Efter sparat, \u00e5terkom hit f\u00f6r att skapa ytterligare en.",
|
||||
"update_transaction": "Uppdatera transaktion",
|
||||
"after_update_create_another": "Efter uppdaterat, \u00e5terkom hit f\u00f6r att forts\u00e4tta redigera.",
|
||||
"reset_after": "\u00c5terst\u00e4ll formul\u00e4r efter inskickat",
|
||||
"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}\">Transaktion #{ID} (\"{title}\")<\/a> sparades.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"location": "Plats",
|
||||
"other_budgets": "Anpassade tidsinst\u00e4llda budgetar",
|
||||
"journal_links": "Transaktionsl\u00e4nkar",
|
||||
"go_to_withdrawals": "G\u00e5 till dina uttag",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "Go to expenses",
|
||||
"go_to_bills": "\u0110i \u0111\u1ebfn h\u00f3a \u0111\u01a1n c\u1ee7a b\u1ea1n",
|
||||
"bills": "H\u00f3a \u0111\u01a1n",
|
||||
"last_thirty_days": "Ba m\u01b0\u01a1i ng\u00e0y g\u1ea7n \u0111\u00e2y",
|
||||
"last_seven_days": "B\u1ea3y ng\u00e0y g\u1ea7n \u0111\u00e2y",
|
||||
"go_to_piggies": "T\u1edbi heo \u0111\u1ea5t c\u1ee7a b\u1ea1n",
|
||||
"saved": "Saved",
|
||||
"piggy_banks": "Heo \u0111\u1ea5t",
|
||||
@@ -56,6 +58,7 @@
|
||||
"account_role_defaultAsset": "t\u00e0i kho\u1ea3n m\u1eb7c \u0111\u1ecbnh",
|
||||
"account_role_savingAsset": "T\u00e0i kho\u1ea3n ti\u1ebft ki\u1ec7m",
|
||||
"account_role_sharedAsset": "t\u00e0i kho\u1ea3n d\u00f9ng chung",
|
||||
"clear_location": "X\u00f3a v\u1ecb tr\u00ed",
|
||||
"account_role_ccAsset": "Th\u1ebb t\u00edn d\u1ee5ng",
|
||||
"account_role_cashWalletAsset": "V\u00ed ti\u1ec1n m\u1eb7t",
|
||||
"daily_budgets": "Daily budgets",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Quarterly budgets",
|
||||
"half_year_budgets": "Half-yearly budgets",
|
||||
"yearly_budgets": "Yearly budgets",
|
||||
"split_transaction_title": "M\u00f4 t\u1ea3 giao d\u1ecbch t\u00e1ch",
|
||||
"errors_submission": "There was something wrong with your submission. Please check out the errors.",
|
||||
"flash_error": "L\u1ed7i!",
|
||||
"store_transaction": "Store transaction",
|
||||
"flash_success": "Th\u00e0nh c\u00f4ng!",
|
||||
"create_another": "Sau khi l\u01b0u tr\u1eef, quay tr\u1edf l\u1ea1i \u0111\u00e2y \u0111\u1ec3 t\u1ea1o m\u1ed9t c\u00e1i kh\u00e1c.",
|
||||
"update_transaction": "C\u1eadp nh\u1eadt giao d\u1ecbch",
|
||||
"after_update_create_another": "Sau khi c\u1eadp nh\u1eadt, quay l\u1ea1i \u0111\u00e2y \u0111\u1ec3 ti\u1ebfp t\u1ee5c ch\u1ec9nh s\u1eeda.",
|
||||
"reset_after": "\u0110\u1eb7t l\u1ea1i m\u1eabu sau khi g\u1eedi",
|
||||
"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}\">Giao d\u1ecbch #{ID} (\"{title}\")<\/a> \u0111\u00e3 \u0111\u01b0\u1ee3c l\u01b0u tr\u1eef.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"location": "V\u1ecb tr\u00ed",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Li\u00ean k\u1ebft giao d\u1ecbch",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@@ -1,74 +1,95 @@
|
||||
{
|
||||
"firefly": {
|
||||
"Transfer": "\u8f6c\u5e10",
|
||||
"Transfer": "\u8f6c\u8d26",
|
||||
"Withdrawal": "\u63d0\u6b3e",
|
||||
"Deposit": "\u5b58\u6b3e",
|
||||
"date_and_time": "Date and time",
|
||||
"Deposit": "\u6536\u5165",
|
||||
"date_and_time": "\u65e5\u671f\u548c\u65f6\u95f4",
|
||||
"no_currency": "(\u6ca1\u6709\u8d27\u5e01)",
|
||||
"date": "\u65e5\u671f",
|
||||
"time": "Time",
|
||||
"time": "\u65f6\u95f4",
|
||||
"no_budget": "(\u65e0\u9884\u7b97)",
|
||||
"destination_account": "\u76ee\u6807\u5e10\u6237",
|
||||
"source_account": "\u6765\u6e90\u5e10\u6237",
|
||||
"single_split": "Split",
|
||||
"create_new_transaction": "\u65b0\u5efa\u4ea4\u6613",
|
||||
"destination_account": "\u76ee\u6807\u8d26\u6237",
|
||||
"source_account": "\u6765\u6e90\u8d26\u6237",
|
||||
"single_split": "\u62c6\u5206",
|
||||
"create_new_transaction": "\u521b\u5efa\u65b0\u4ea4\u6613",
|
||||
"balance": "\u4f59\u989d",
|
||||
"transaction_journal_extra": "Extra information",
|
||||
"transaction_journal_meta": "\u540e\u8bbe\u8d44\u8baf",
|
||||
"basic_journal_information": "Basic transaction information",
|
||||
"bills_to_pay": "\u5f85\u4ed8\u5e10\u5355",
|
||||
"left_to_spend": "\u5269\u4f59\u53ef\u82b1\u8d39",
|
||||
"attachments": "\u9644\u52a0\u6863\u6848",
|
||||
"net_worth": "\u51c0\u503c",
|
||||
"bill": "\u5e10\u5355",
|
||||
"no_bill": "(no bill)",
|
||||
"transaction_journal_extra": "\u989d\u5916\u4fe1\u606f",
|
||||
"transaction_journal_meta": "\u5143\u4fe1\u606f",
|
||||
"basic_journal_information": "\u57fa\u7840\u4ea4\u6613\u4fe1\u606f",
|
||||
"bills_to_pay": "\u5f85\u4ed8\u8d26\u5355",
|
||||
"left_to_spend": "\u5269\u4f59\u652f\u51fa",
|
||||
"attachments": "\u9644\u4ef6",
|
||||
"net_worth": "\u51c0\u8d44\u4ea7",
|
||||
"bill": "\u8d26\u5355",
|
||||
"no_bill": "(\u65e0\u8d26\u5355)",
|
||||
"tags": "\u6807\u7b7e",
|
||||
"internal_reference": "\u5185\u90e8\u53c2\u8003",
|
||||
"internal_reference": "\u5185\u90e8\u5f15\u7528",
|
||||
"external_url": "\u5916\u90e8\u94fe\u63a5",
|
||||
"no_piggy_bank": "\uff08\u65e0\u5b58\u94b1\u7f50\uff09",
|
||||
"no_piggy_bank": "(\u65e0\u5b58\u94b1\u7f50)",
|
||||
"paid": "\u5df2\u4ed8\u6b3e",
|
||||
"notes": "\u6ce8\u91ca",
|
||||
"yourAccounts": "\u60a8\u7684\u5e10\u6237",
|
||||
"go_to_asset_accounts": "\u68c0\u89c6\u60a8\u7684\u8d44\u4ea7\u5e10\u6237",
|
||||
"transaction_table_description": "A table containing your transactions",
|
||||
"account": "\u5e10\u6237",
|
||||
"notes": "\u5907\u6ce8",
|
||||
"yourAccounts": "\u60a8\u7684\u8d26\u6237",
|
||||
"go_to_asset_accounts": "\u67e5\u770b\u60a8\u7684\u8d44\u4ea7\u8d26\u6237",
|
||||
"transaction_table_description": "\u5305\u542b\u60a8\u4ea4\u6613\u7684\u8868\u683c",
|
||||
"account": "\u8d26\u6237",
|
||||
"description": "\u63cf\u8ff0",
|
||||
"amount": "\u91d1\u989d",
|
||||
"budget": "\u9884\u7b97",
|
||||
"category": "\u5206\u7c7b",
|
||||
"opposing_account": "Opposing account",
|
||||
"opposing_account": "\u5bf9\u65b9\u8d26\u6237",
|
||||
"budgets": "\u9884\u7b97",
|
||||
"categories": "\u5206\u7c7b",
|
||||
"go_to_budgets": "\u524d\u5f80\u60a8\u7684\u9884\u7b97",
|
||||
"income": "\u6536\u5165 \/ \u6240\u5f97",
|
||||
"go_to_deposits": "Go to deposits",
|
||||
"income": "\u6536\u5165",
|
||||
"go_to_deposits": "\u524d\u5f80\u6536\u5165",
|
||||
"go_to_categories": "\u524d\u5f80\u60a8\u7684\u5206\u7c7b",
|
||||
"expense_accounts": "\u652f\u51fa\u5e10\u6237",
|
||||
"go_to_expenses": "Go to expenses",
|
||||
"go_to_bills": "\u524d\u5f80\u60a8\u7684\u5e10\u5355",
|
||||
"bills": "\u5e10\u5355",
|
||||
"expense_accounts": "\u652f\u51fa\u8d26\u6237",
|
||||
"go_to_expenses": "\u524d\u5f80\u652f\u51fa",
|
||||
"go_to_bills": "\u524d\u5f80\u8d26\u5355",
|
||||
"bills": "\u8d26\u5355",
|
||||
"last_thirty_days": "\u6700\u8fd1 30 \u5929",
|
||||
"last_seven_days": "\u6700\u8fd1 7 \u5929",
|
||||
"go_to_piggies": "\u524d\u5f80\u60a8\u7684\u5b58\u94b1\u7f50",
|
||||
"saved": "\u5df2\u4fdd\u5b58",
|
||||
"piggy_banks": "\u5b58\u94b1\u7f50",
|
||||
"piggy_bank": "\u5b58\u94b1\u7f50",
|
||||
"amounts": "Amounts",
|
||||
"Default asset account": "\u9884\u8bbe\u8d44\u4ea7\u5e10\u6237",
|
||||
"account_role_defaultAsset": "\u9884\u8bbe\u8d44\u4ea7\u5e10\u6237",
|
||||
"account_role_savingAsset": "\u50a8\u84c4\u5e10\u6237",
|
||||
"account_role_sharedAsset": "\u5171\u7528\u8d44\u4ea7\u5e10\u6237",
|
||||
"amounts": "\u91d1\u989d",
|
||||
"Default asset account": "\u9ed8\u8ba4\u8d44\u4ea7\u8d26\u6237",
|
||||
"account_role_defaultAsset": "\u9ed8\u8ba4\u8d44\u4ea7\u8d26\u6237",
|
||||
"account_role_savingAsset": "\u50a8\u84c4\u8d26\u6237",
|
||||
"account_role_sharedAsset": "\u5171\u7528\u8d44\u4ea7\u8d26\u6237",
|
||||
"clear_location": "\u6e05\u9664\u4f4d\u7f6e",
|
||||
"account_role_ccAsset": "\u4fe1\u7528\u5361",
|
||||
"account_role_cashWalletAsset": "\u73b0\u91d1\u94b1\u5305",
|
||||
"daily_budgets": "Daily budgets",
|
||||
"weekly_budgets": "Weekly budgets",
|
||||
"monthly_budgets": "Monthly budgets",
|
||||
"quarterly_budgets": "Quarterly budgets",
|
||||
"half_year_budgets": "Half-yearly budgets",
|
||||
"yearly_budgets": "Yearly budgets",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "\u4ea4\u6613\u8fde\u7ed3",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
"revenue_accounts": "\u6536\u5165\u5e10\u6237",
|
||||
"add_another_split": "\u589e\u52a0\u62c6\u5206"
|
||||
"daily_budgets": "\u6bcf\u65e5\u9884\u7b97",
|
||||
"weekly_budgets": "\u6bcf\u5468\u9884\u7b97",
|
||||
"monthly_budgets": "\u6bcf\u6708\u9884\u7b97",
|
||||
"quarterly_budgets": "\u6bcf\u5b63\u5ea6\u9884\u7b97",
|
||||
"half_year_budgets": "\u6bcf\u534a\u5e74\u9884\u7b97",
|
||||
"yearly_budgets": "\u6bcf\u5e74\u9884\u7b97",
|
||||
"split_transaction_title": "\u62c6\u5206\u4ea4\u6613\u7684\u63cf\u8ff0",
|
||||
"errors_submission": "\u60a8\u63d0\u4ea4\u7684\u5185\u5bb9\u6709\u8bef\uff0c\u8bf7\u68c0\u67e5\u9519\u8bef\u4fe1\u606f\u3002",
|
||||
"flash_error": "\u9519\u8bef\uff01",
|
||||
"store_transaction": "\u4fdd\u5b58\u4ea4\u6613",
|
||||
"flash_success": "\u6210\u529f\uff01",
|
||||
"create_another": "\u4fdd\u5b58\u540e\uff0c\u8fd4\u56de\u6b64\u9875\u9762\u4ee5\u521b\u5efa\u65b0\u8bb0\u5f55",
|
||||
"update_transaction": "\u66f4\u65b0\u4ea4\u6613",
|
||||
"after_update_create_another": "\u66f4\u65b0\u540e\uff0c\u8fd4\u56de\u6b64\u9875\u9762\u7ee7\u7eed\u7f16\u8f91\u3002",
|
||||
"reset_after": "\u63d0\u4ea4\u540e\u91cd\u7f6e\u8868\u5355",
|
||||
"bill_paid_on": "\u652f\u4ed8\u4e8e {date}",
|
||||
"first_split_decides": "\u9996\u7b14\u62c6\u5206\u51b3\u5b9a\u6b64\u5b57\u6bb5\u7684\u503c",
|
||||
"first_split_overrules_source": "\u9996\u7b14\u62c6\u5206\u53ef\u80fd\u8986\u76d6\u6765\u6e90\u8d26\u6237",
|
||||
"first_split_overrules_destination": "\u9996\u7b14\u62c6\u5206\u53ef\u80fd\u8986\u76d6\u76ee\u6807\u8d26\u6237",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">\u4ea4\u6613 #{ID} (\u201c{title}\u201d)<\/a> \u5df2\u4fdd\u5b58\u3002",
|
||||
"custom_period": "\u81ea\u5b9a\u4e49\u5468\u671f",
|
||||
"reset_to_current": "\u91cd\u7f6e\u4e3a\u5f53\u524d\u5468\u671f",
|
||||
"select_period": "\u9009\u62e9\u5468\u671f",
|
||||
"location": "\u4f4d\u7f6e",
|
||||
"other_budgets": "\u81ea\u5b9a\u4e49\u533a\u95f4\u9884\u7b97",
|
||||
"journal_links": "\u4ea4\u6613\u5173\u8054",
|
||||
"go_to_withdrawals": "\u524d\u5f80\u652f\u51fa",
|
||||
"revenue_accounts": "\u6536\u5165\u8d26\u6237",
|
||||
"add_another_split": "\u589e\u52a0\u53e6\u4e00\u7b14\u62c6\u5206"
|
||||
},
|
||||
"list": {
|
||||
"piggy_bank": "\u5b58\u94b1\u7f50",
|
||||
@@ -76,18 +97,18 @@
|
||||
"amount": "\u91d1\u989d",
|
||||
"name": "\u540d\u79f0",
|
||||
"role": "\u89d2\u8272",
|
||||
"iban": "\u56fd\u9645\u94f6\u884c\u5e10\u6237\u53f7\u7801 (IBAN)",
|
||||
"iban": "\u56fd\u9645\u94f6\u884c\u8d26\u6237\u53f7\u7801\uff08IBAN\uff09",
|
||||
"lastActivity": "\u4e0a\u6b21\u6d3b\u52a8",
|
||||
"currentBalance": "\u76ee\u524d\u9980\u989d",
|
||||
"balanceDiff": "\u9980\u989d\u5dee",
|
||||
"next_expected_match": "\u4e0b\u4e00\u4e2a\u9884\u671f\u7684\u914d\u5bf9"
|
||||
"currentBalance": "\u76ee\u524d\u4f59\u989d",
|
||||
"balanceDiff": "\u4f59\u989d\u5dee",
|
||||
"next_expected_match": "\u9884\u671f\u4e0b\u6b21\u652f\u4ed8"
|
||||
},
|
||||
"config": {
|
||||
"html_language": "zh-cn"
|
||||
},
|
||||
"form": {
|
||||
"foreign_amount": "\u5916\u5e01\u91d1\u989d",
|
||||
"interest_date": "\u5229\u7387\u65e5\u671f",
|
||||
"interest_date": "\u5229\u606f\u65e5\u671f",
|
||||
"book_date": "\u767b\u8bb0\u65e5\u671f",
|
||||
"process_date": "\u5904\u7406\u65e5\u671f",
|
||||
"due_date": "\u5230\u671f\u65e5",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
"go_to_expenses": "Go to expenses",
|
||||
"go_to_bills": "\u524d\u5f80\u60a8\u7684\u5e33\u55ae",
|
||||
"bills": "\u5e33\u55ae",
|
||||
"last_thirty_days": "\u6700\u8fd130\u5929",
|
||||
"last_seven_days": "\u6700\u8fd17\u5929",
|
||||
"go_to_piggies": "\u524d\u5f80\u60a8\u7684\u5c0f\u8c6c\u64b2\u6eff",
|
||||
"saved": "Saved",
|
||||
"piggy_banks": "\u5c0f\u8c6c\u64b2\u6eff",
|
||||
@@ -56,6 +58,7 @@
|
||||
"account_role_defaultAsset": "\u9810\u8a2d\u8cc7\u7522\u5e33\u6236",
|
||||
"account_role_savingAsset": "\u5132\u84c4\u5e33\u6236",
|
||||
"account_role_sharedAsset": "\u5171\u7528\u8cc7\u7522\u5e33\u6236",
|
||||
"clear_location": "\u6e05\u9664\u4f4d\u7f6e",
|
||||
"account_role_ccAsset": "\u4fe1\u7528\u5361",
|
||||
"account_role_cashWalletAsset": "\u73fe\u91d1\u9322\u5305",
|
||||
"daily_budgets": "Daily budgets",
|
||||
@@ -64,6 +67,24 @@
|
||||
"quarterly_budgets": "Quarterly budgets",
|
||||
"half_year_budgets": "Half-yearly budgets",
|
||||
"yearly_budgets": "Yearly budgets",
|
||||
"split_transaction_title": "\u62c6\u5206\u4ea4\u6613\u7684\u63cf\u8ff0",
|
||||
"errors_submission": "There was something wrong with your submission. Please check out the errors.",
|
||||
"flash_error": "\u932f\u8aa4\uff01",
|
||||
"store_transaction": "Store transaction",
|
||||
"flash_success": "\u6210\u529f\uff01",
|
||||
"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.",
|
||||
"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": "\u4f4d\u7f6e",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "\u4ea4\u6613\u9023\u7d50",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
2
frontend/src/pages/dashboard.js
vendored
2
frontend/src/pages/dashboard.js
vendored
@@ -78,9 +78,9 @@ new Vue({
|
||||
beforeCreate() {
|
||||
this.$store.commit('initialiseStore');
|
||||
this.$store.dispatch('updateCurrencyPreference');
|
||||
this.$store.dispatch('dashboard/index/initialiseStore');
|
||||
},
|
||||
});
|
||||
|
||||
new Vue({
|
||||
i18n,
|
||||
store,
|
||||
|
42
frontend/src/pages/transactions/edit.js
vendored
Normal file
42
frontend/src/pages/transactions/edit.js
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* edit.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 store from "../../components/store";
|
||||
import Edit from "../../components/transactions/Edit";
|
||||
import Vue from "vue";
|
||||
|
||||
require('../../bootstrap');
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
// i18n
|
||||
let i18n = require('../../i18n');
|
||||
|
||||
let props = {};
|
||||
new Vue({
|
||||
i18n,
|
||||
store,
|
||||
render(createElement) {
|
||||
return createElement(Edit, {props: props});
|
||||
},
|
||||
beforeCreate() {
|
||||
this.$store.commit('initialiseStore');
|
||||
this.$store.dispatch('updateCurrencyPreference');
|
||||
},
|
||||
}).$mount('#transactions_edit');
|
19
frontend/src/scss/_variables.scss
vendored
Normal file
19
frontend/src/scss/_variables.scss
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* _variables.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/>.
|
||||
*/
|
3
frontend/webpack.mix.js
vendored
3
frontend/webpack.mix.js
vendored
@@ -22,6 +22,7 @@ const mix = require('laravel-mix');
|
||||
require('laravel-mix-bundle-analyzer');
|
||||
|
||||
|
||||
|
||||
// production
|
||||
mix.webpackConfig({
|
||||
resolve: {
|
||||
@@ -62,6 +63,7 @@ mix
|
||||
|
||||
// transactions.
|
||||
.js('src/pages/transactions/create.js', 'public/js/transactions')
|
||||
.js('src/pages/transactions/edit.js', 'public/js/transactions')
|
||||
// register page
|
||||
.js('src/pages/register.js', 'public/js')
|
||||
|
||||
@@ -73,6 +75,7 @@ mix
|
||||
// move to right dir
|
||||
.copy('public/js','../public/v2/js')
|
||||
.copy('fonts','../public/fonts')
|
||||
.copy('images','../public/images')
|
||||
.copy('public/css','../public/v2/css')
|
||||
;
|
||||
|
||||
|
Reference in New Issue
Block a user