Split components for future recycling.

This commit is contained in:
James Cole
2021-02-14 19:13:42 +01:00
parent bfd925fbfe
commit e51f593a2d
37 changed files with 687 additions and 430 deletions

View File

@@ -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"
}

View 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 :class="'alert alert-' + type + ' alert-dismissible'" v-if="message.length > 0">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</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>

View File

@@ -21,6 +21,7 @@
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)
@@ -32,7 +33,8 @@ export default new Vuex.Store(
transactions: {
namespaced: true,
modules: {
create: transactions_create
create: transactions_create,
edit: transactions_edit
}
},
dashboard: {

View File

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

View File

@@ -20,230 +20,21 @@
<template>
<div>
<div class="alert alert-danger alert-dismissible" v-if="errorMessage.length > 0">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h5><i class="icon fas fa-ban"></i> {{ $t("firefly.flash_error") }}</h5>
{{ errorMessage }}
</div>
<div class="alert alert-success alert-dismissible" v-if="successMessage.length > 0">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h5><i class="icon fas fa-thumbs-up"></i> {{ $t("firefly.flash_success") }}</h5>
<span v-html="successMessage"></span>
</div>
<div class="row" v-if="transactions.length > 1">
<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>
<alert :message="errorMessage" type="danger"/>
<alert :message="successMessage" type="success"/>
<SplitPills :transactions="transactions" />
<div class="tab-content">
<div v-for="(transaction, index) in this.transactions" :class="'tab-pane' + (0===index ? ' active' : '')" :id="'split_' + index">
<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="transactions.length > 1">({{ index + 1 }} / {{ transactions.length }}) </span>
</h3>
</div>
<div class="card-body">
<!-- start of body -->
<div class="row">
<div class="col">
<TransactionDescription
v-model="transaction.description"
:index="index"
:errors="transaction.errors.description"
></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="transaction.source_account"
direction="source"
:index="index"
:errors="transaction.errors.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"
:index="index"
/>
</div>
<!-- destination -->
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12">
<!-- DESTINATION -->
<TransactionAccount
v-model="transaction.destination_account"
direction="destination"
:index="index"
:errors="transaction.errors.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 :index="index" :errors="transaction.errors.amount"/>
<!--
-->
</div>
<div class="col-xl-2 col-lg-2 col-md-2 col-sm-12 text-center d-none d-sm-block">
<TransactionForeignCurrency :index="index"/>
</div>
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12">
<TransactionForeignAmount :index="index" :errors="transaction.errors.foreign_amount"/>
</div>
</div>
<!-- dates -->
<div class="row">
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12">
<TransactionDate
:index="index"
:errors="transaction.errors.date"
/>
</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 :index="index" :enabled-dates="customDateFields" :errors="transaction.errors.custom_dates"/>
</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="transactions.length > 1">({{ index + 1 }} / {{ transactions.length }}) </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-model="transaction.budget_id"
:index="index"
:errors="transaction.errors.budget"
v-if="!('Transfer' === transactionType || 'Deposit' === transactionType)"
/>
<TransactionCategory
v-model="transaction.category"
:index="index"
:errors="transaction.errors.category"
/>
</div>
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<TransactionBill
v-model="transaction.bill_id"
:index="index"
:errors="transaction.errors.bill"
v-if="!('Transfer' === transactionType || 'Deposit' === transactionType)"
/>
<TransactionTags
:index="index"
v-model="transaction.tags"
:errors="transaction.errors.tags"
/>
<TransactionPiggyBank
:index="index"
v-model="transaction.piggy_bank_id"
:errors="transaction.errors.piggy_bank"
v-if="!('Withdrawal' === transactionType || 'Deposit' === transactionType)"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- end card for meta -->
<!-- card for extra -->
<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="transactions.length > 1">({{ index + 1 }} / {{ transactions.length }}) </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
:index="index"
v-model="transaction.internal_reference"
:errors="transaction.errors.internal_reference"
/>
<TransactionExternalUrl
:index="index"
v-model="transaction.external_url"
:errors="transaction.errors.external_url"
/>
<TransactionNotes
:index="index"
v-model="transaction.notes"
:errors="transaction.errors.notes"
/>
</div>
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<TransactionAttachments
:index="index"
ref="attachments"
:transaction_journal_id="transaction.transaction_journal_id"
:submitted_transaction="submittedTransaction"
v-model="transaction.attachments"
v-on:uploaded-attachments="uploadedAttachment($event)"
/>
<TransactionLinks :index="index"
v-model="transaction.links"
/>
</div>
</div>
<!-- end of body -->
</div>
</div>
</div>
</div>
<!-- end card for extra -->
<!-- end of card -->
</div>
<SplitForm
v-for="(transaction, index) in this.transactions"
v-bind:key="index"
:transaction="transaction"
:index="index"
:count="transactions.length"
:submitted-transaction="submittedTransaction"
v-on:uploaded-attachments="uploadedAttachment($event)"
/>
</div>
<div class="row">
<!-- group title -->
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
@@ -258,6 +49,7 @@
</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">
@@ -306,45 +98,20 @@
<script>
import {createNamespacedHelpers} from 'vuex'
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 Alert from '../partials/Alert';
import SplitPills from "./SplitPills";
import TransactionGroupTitle from "./TransactionGroupTitle";
import SplitForm from "./SplitForm";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
export default {
name: "Create",
components: {
TransactionAttachments,
TransactionNotes,
TransactionExternalUrl,
SplitForm,
Alert,
SplitPills,
TransactionGroupTitle,
TransactionInternalReference,
TransactionPiggyBank,
TransactionTags,
TransactionLinks,
TransactionBill,
TransactionCategory,
TransactionCustomDates,
TransactionForeignCurrency,
TransactionForeignAmount, TransactionAmount, SwitchAccount, TransactionAccount, TransactionBudget, TransactionDescription, TransactionDate
},
created() {
this.storeAllowedOpposingTypes();
@@ -386,8 +153,8 @@ export default {
},
computed: {
...mapGetters([
'transactionType', // -> this.someGetter
'transactions', // -> this.someOtherGetter
'transactionType',
'transactions',
'customDateFields',
'date',
'groupTitle'
@@ -465,12 +232,24 @@ export default {
* forwarded.
*/
finalizeSubmit() {
// console.log('finalizeSubmit (' + this.submittedTransaction + ', ' + this.submittedAttachments + ', ' + this.submittedLinks + ')');
console.log('finalizeSubmit (' + this.submittedTransaction + ', ' + this.submittedAttachments + ', ' + this.submittedLinks + ')');
if (this.submittedTransaction && this.submittedAttachments && this.submittedLinks) {
console.log('all true');
console.log('createAnother = ' + this.createAnother);
console.log('inError = ' + this.inError);
if (false === this.createAnother && false === this.inError) {
console.log('redirect');
window.location.href = (window.previousURL ?? '/') + '?transaction_group_id=' + this.returnedGroupId + '&message=created';
return;
}
if (false === this.inError) {
// show message:
this.errorMessage = '';
this.successMessage = this.$t('firefly.transaction_stored_link', {ID: this.returnedGroupId, title: this.returnedGroupTitle});
}
// enable flags:
this.enableSubmit = true;
this.submittedTransaction = false;
@@ -478,9 +257,6 @@ export default {
this.submittedAttachments = false;
this.inError = false;
// show message:
this.errorMessage = '';
this.successMessage = this.$t('firefly.transaction_stored_link', {ID: this.returnedGroupId, title: this.returnedGroupTitle});
// reset attachments (always do this)
for (let i in this.transactions) {
@@ -509,9 +285,9 @@ export default {
* need to happen in the right order.
*/
submitTransaction: function () {
console.log('submitTransaction()');
// disable the submit button:
this.enableSubmit = false;
// console.log('enable submit = false');
// convert the data so its ready to be submitted:
const url = './api/v1/transactions';
@@ -520,6 +296,7 @@ export default {
// POST the transaction.
axios.post(url, data)
.then(response => {
console.log('Response is OK!');
// report the transaction is submitted.
this.submittedTransaction = true;
@@ -557,7 +334,7 @@ export default {
* The ID is set via the store.
*/
submitAttachments: function (data, response) {
// console.log('submitAttachments');
console.log('submitAttachments()');
let result = response.data.data.attributes.transactions
for (let i in data.transactions) {
if (data.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
@@ -575,7 +352,7 @@ export default {
* Once the number of components matches the number of splits we know all attachments have been uploaded.
*/
uploadedAttachment: function (journalId) {
// console.log('Triggered uploadedAttachment(' + journalId + ')');
console.log('Triggered uploadedAttachment(' + journalId + ')');
let key = 'str' + journalId;
this.submittedAttCount[key] = 1;
let count = Object.keys(this.submittedAttCount).length;
@@ -586,6 +363,7 @@ export default {
},
submitTransactionLinks(data, response) {
console.log('submitTransactionLinks()');
let promises = [];
let result = response.data.data.attributes.transactions;
let total = 0;

View File

@@ -0,0 +1,43 @@
<!--
- 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"/>
</div>
</template>
<script>
export default {
name: "Edit",
data() {
return {
successMessage: '',
errorMessage: '',
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,282 @@
<!--
- 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 :class="'tab-pane' + (0===index ? ' active' : '')" :id="'split_' + index">
<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>
<div class="card-body">
<!-- start of body -->
<div class="row">
<div class="col">
<TransactionDescription
v-model="transaction.description"
:index="index"
:errors="transaction.errors.description"
></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="transaction.source_account"
direction="source"
:index="index"
:errors="transaction.errors.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"
:index="index"
/>
</div>
<!-- destination -->
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12">
<!-- DESTINATION -->
<TransactionAccount
v-model="transaction.destination_account"
direction="destination"
:index="index"
:errors="transaction.errors.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 :index="index" :errors="transaction.errors.amount"/>
<!--
-->
</div>
<div class="col-xl-2 col-lg-2 col-md-2 col-sm-12 text-center d-none d-sm-block">
<TransactionForeignCurrency :index="index"/>
</div>
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12">
<TransactionForeignAmount :index="index" :errors="transaction.errors.foreign_amount"/>
</div>
</div>
<!-- dates -->
<div class="row">
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12">
<TransactionDate
:index="index"
:errors="transaction.errors.date"
/>
</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
:index="index"
:errors="transaction.errors.custom_dates"
/>
</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-model="transaction.budget_id"
:index="index"
:errors="transaction.errors.budget"
v-if="!('Transfer' === transactionType || 'Deposit' === transactionType)"
/>
<TransactionCategory
v-model="transaction.category"
:index="index"
:errors="transaction.errors.category"
/>
</div>
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<TransactionBill
v-model="transaction.bill_id"
:index="index"
:errors="transaction.errors.bill"
v-if="!('Transfer' === transactionType || 'Deposit' === transactionType)"
/>
<TransactionTags
:index="index"
v-model="transaction.tags"
:errors="transaction.errors.tags"
/>
<TransactionPiggyBank
:index="index"
v-model="transaction.piggy_bank_id"
:errors="transaction.errors.piggy_bank"
v-if="!('Withdrawal' === transactionType || 'Deposit' === transactionType)"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- end card for meta -->
<!-- card for extra -->
<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 -->
<div class="row">
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<TransactionInternalReference
:index="index"
v-model="transaction.internal_reference"
:errors="transaction.errors.internal_reference"
/>
<TransactionExternalUrl
:index="index"
v-model="transaction.external_url"
:errors="transaction.errors.external_url"
/>
<TransactionNotes
:index="index"
v-model="transaction.notes"
:errors="transaction.errors.notes"
/>
</div>
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<TransactionAttachments
:index="index"
ref="attachments"
v-on="$listeners"
:transaction_journal_id="transaction.transaction_journal_id"
:submitted_transaction="submittedTransaction"
v-model="transaction.attachments"
/>
<TransactionLinks :index="index" v-model="transaction.links" />
</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 {createNamespacedHelpers} from "vuex";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
export default {
name: "SplitForm",
props: [
'transaction',
'split',
'count',
'index',
'submittedTransaction' // need to know if transaction is submitted.
],
computed: {
...mapGetters(['transactionType',])
},
components: {
SplitPills,
TransactionAttachments,
TransactionNotes,
TransactionExternalUrl,
TransactionInternalReference,
TransactionPiggyBank,
TransactionTags,
TransactionLinks,
TransactionBill,
TransactionCategory,
TransactionCustomDates,
TransactionForeignCurrency,
TransactionForeignAmount,
TransactionAmount,
SwitchAccount,
TransactionAccount,
TransactionBudget,
TransactionDescription,
TransactionDate
},
}
</script>

View 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 class="row" v-if="transactions.length > 1">
<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>

View File

@@ -29,7 +29,7 @@
</div>
<vue-typeahead-bootstrap
v-if="visible"
v-model="value.name"
v-model="accountName"
:data="accounts"
:showOnFocus=true
:inputClass="errors.length > 0 ? 'is-invalid' : ''"
@@ -73,7 +73,9 @@ export default {
accounts: [],
accountTypes: [],
initialSet: [],
selectedAccount: {}
selectedAccount: {},
account: this.value,
accountName: ''
}
},
created() {
@@ -97,7 +99,8 @@ export default {
},
clearAccount: function () {
this.accounts = this.initialSet;
this.value = {name: ''};
this.account = {name: ''};
this.accountName = '';
},
lookupAccount: debounce(function () {
if (0 === this.accountTypes.length) {
@@ -106,11 +109,12 @@ export default {
}
// update autocomplete URL:
axios.get(this.getACURL(this.accountTypes, this.value.name))
axios.get(this.getACURL(this.accountTypes, this.account.name))
.then(response => {
this.accounts = response.data;
})
}, 300),
createInitialSet: function () {
let types = this.sourceAllowedTypes;
if ('destination' === this.direction) {
@@ -119,7 +123,6 @@ export default {
axios.get(this.getACURL(types, ''))
.then(response => {
// console.log('initial set of accounts. ' + this.direction);
this.accounts = response.data;
this.initialSet = response.data;
});
@@ -127,10 +130,10 @@ export default {
},
watch: {
selectedAccount: function (value) {
this.value = value;
this.value.name = this.value.name_with_balance;
this.accountName = this.account.name_with_balance;
this.account = value;
},
value: function (value) {
account: function (value) {
this.updateField({field: this.accountKey, index: this.index, value: value});
// set the opposing account allowed set.
let opposingAccounts = [];
@@ -150,60 +153,10 @@ export default {
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();
// }
// }
},
computed: {
...mapGetters([
'transactionType',
'transactions',
'defaultTransaction',
'sourceAllowedTypes',
'destinationAllowedTypes',
'allowedOpposingTypes'
@@ -228,20 +181,6 @@ export default {
return false;
}
}
// 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});
// }
// }
}
}
</script>
<style scoped>
</style>

View File

@@ -41,7 +41,7 @@ export default {
props: ['transaction_journal_id'],
watch: {
transaction_journal_id: function (value) {
// console.log('transaction_journal_id changed to ' + value);
console.log('transaction_journal_id changed to ' + value);
// do upload!
if (0 !== value) {
this.doUpload();
@@ -50,7 +50,7 @@ export default {
},
methods: {
doUpload: function () {
// console.log('Now in doUpload() for ' + this.$refs.att.files.length + ' files.');
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];
@@ -73,7 +73,7 @@ export default {
.post(uploadUri, new Blob([evt.target.result]))
.then(attachmentResponse => {
// TODO feedback etc.
// console.log('Uploaded a file.');
console.log('Uploaded a file. Emit event!');
// console.log(attachmentResponse);
theParent.$emit('uploaded-attachments', this.transaction_journal_id);
});
@@ -84,7 +84,7 @@ export default {
}
}
if (0 === this.$refs.att.files.length) {
// console.log('No files to upload.');
console.log('No files to upload. Emit event!');
this.$emit('uploaded-attachments', this.transaction_journal_id);
}
}

View File

@@ -27,7 +27,7 @@
<select
ref="bill"
:title="$t('firefly.bill')"
v-model="value"
v-model="bill"
autocomplete="off"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
name="bill_id[]"
@@ -54,7 +54,8 @@ export default {
name: "TransactionBill",
data() {
return {
billList: []
billList: [],
bill: this.value
}
},
created() {
@@ -97,7 +98,7 @@ export default {
},
},
watch: {
value: function (value) {
bill: function (value) {
this.updateField({field: 'bill_id', index: this.index, value: value});
}
},

View File

@@ -27,7 +27,7 @@
<select
ref="budget"
:title="$t('firefly.budget')"
v-model="value"
v-model="budget"
autocomplete="off"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
name="budget_id[]"
@@ -53,7 +53,8 @@ export default {
name: "TransactionBudget",
data() {
return {
budgetList: []
budgetList: [],
budget: this.value
}
},
created() {
@@ -96,7 +97,7 @@ export default {
},
},
watch: {
value: function (value) {
budget: function (value) {
this.updateField({field: 'budget_id', index: this.index, value: value});
}
},

View File

@@ -26,7 +26,7 @@
<vue-typeahead-bootstrap
inputName="category[]"
v-model="value"
v-model="category"
:data="categories"
:placeholder="$t('firefly.category')"
:showOnFocus=true
@@ -63,7 +63,8 @@ export default {
data() {
return {
categories: [],
initialSet: []
initialSet: [],
category: this.value
}
},
@@ -84,7 +85,7 @@ export default {
],
),
clearCategory: function () {
this.value = null;
this.category = null;
},
getACURL: function (query) {
// update autocomplete URL:
@@ -99,7 +100,7 @@ export default {
}, 300)
},
watch: {
value: function (value) {
category: function (value) {
this.updateField({field: 'category', index: this.index, value: value});
}
},
@@ -115,7 +116,7 @@ export default {
return this.categories[this.index].name;
},
set(value) {
this.value = value.name;
this.category = value.name;
}
}
}

View File

@@ -49,28 +49,47 @@ import {createNamespacedHelpers} from "vuex";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
export default {
name: "TransactionCustomDates",
props: ['enabledDates', 'index', 'errors'],
props: ['index', 'errors'],
data() {
return {
enabledDates: {},
}
},
created() {
this.getCustomDates();
},
methods: {
...mapGetters(
[
'transactions'
]
),
...mapMutations(
[
'updateField',
],
...mapGetters(['transactions']),
...mapMutations(['updateField',],
),
getFieldValue(field) {
return this.transactions()[parseInt(this.index)][field] ?? '';
},
setFieldValue(event, field) {
this.updateField({index: this.index, field: field, value: event.target.value});
}
},
getCustomDates: function () {
axios.get('./api/v1/preferences/transaction_journal_optional_fields').then(response => {
let fields = response.data.data.attributes.data;
let allDateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date'];
let selectedDateFields = {
interest_date: false,
book_date: false,
process_date: false,
due_date: false,
payment_date: false,
invoice_date: false,
};
for (let key in fields) {
if (fields.hasOwnProperty(key)) {
if (-1 !== allDateFields.indexOf(key)) {
selectedDateFields[key] = fields[key];
}
}
}
this.enabledDates = selectedDateFields;
});
},
}
}
</script>
<style scoped>
</style>

View File

@@ -22,7 +22,7 @@
<div class="form-group">
<vue-typeahead-bootstrap
inputName="description[]"
v-model="value"
v-model="description"
:data="descriptions"
:placeholder="$t('firefly.description')"
:showOnFocus=true
@@ -59,7 +59,8 @@ export default {
data() {
return {
descriptions: [],
initialSet: []
initialSet: [],
description: this.value
}
},
created() {
@@ -77,7 +78,7 @@ export default {
],
),
clearDescription: function () {
this.value = '';
this.description = '';
},
getACURL: function (query) {
// update autocomplete URL:
@@ -92,7 +93,7 @@ export default {
}, 300)
},
watch: {
value: function (value) {
description: function (value) {
this.updateField({field: 'description', index: this.index, value: value});
}
},

View File

@@ -28,7 +28,7 @@
type="url"
name="external_url[]"
:placeholder="$t('firefly.external_url')"
v-model="value"
v-model="url"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
/>
<div class="input-group-append">
@@ -46,6 +46,11 @@ const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers
export default {
props: ['index', 'value', 'errors'],
name: "TransactionExternalUrl",
data() {
return {
url: this.value
}
},
methods: {
...mapMutations(
[
@@ -54,7 +59,7 @@ export default {
),
},
watch: {
value: function (value) {
url: function (value) {
this.updateField({field: 'external_url', index: this.index, value: value});
}
}

View File

@@ -25,7 +25,7 @@
</div>
<vue-typeahead-bootstrap
inputName="group_title"
v-model="value"
v-model="title"
:data="descriptions"
:placeholder="$t('firefly.split_transaction_title')"
:showOnFocus=true
@@ -61,7 +61,8 @@ export default {
data() {
return {
descriptions: [],
initialSet: []
initialSet: [],
title: this.value
}
},
@@ -73,7 +74,7 @@ export default {
});
},
watch: {
value: function (value) {
title: function (value) {
//console.log('set');
this.setGroupTitle({groupTitle: value});
}
@@ -91,7 +92,7 @@ export default {
),
clearDescription: function () {
this.setGroupTitle({groupTitle: ''});
this.value = '';
this.title = '';
},
getACURL: function (query) {
// update autocomplete URL:
@@ -99,7 +100,7 @@ export default {
},
lookupDescription: debounce(function () {
// update autocomplete URL:
axios.get(this.getACURL(this.value))
axios.get(this.getACURL(this.title))
.then(response => {
this.descriptions = response.data;
})

View File

@@ -27,7 +27,7 @@
<input
type="text"
name="internal_reference[]"
v-model="value"
v-model="reference"
:placeholder="$t('firefly.internal_reference')"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
/>
@@ -46,6 +46,11 @@ const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers
export default {
props: ['index', 'value', 'errors'],
name: "TransactionInternalReference",
data() {
return {
reference: this.value
}
},
methods: {
...mapMutations(
[
@@ -54,13 +59,9 @@ export default {
),
},
watch: {
value: function (value) {
reference: function (value) {
this.updateField({field: 'internal_reference', index: this.index, value: value});
}
}
}
</script>
<style scoped>
</style>

View File

@@ -26,11 +26,11 @@
</div>
<div class="row">
<div class="col">
<p v-if="value.length === 0">
<p v-if="links.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>
<ul class="list-group" v-if="value.length > 0">
<li class="list-group-item" v-for="transaction in value">
<ul class="list-group" v-if="links.length > 0">
<li class="list-group-item" v-for="transaction in links">
<em>{{ getTextForLinkType(transaction.link_type_id) }}</em>
<a :href='"./transaction/show/" + transaction.transaction_group_id'>{{ transaction.description }}</a>
@@ -64,7 +64,7 @@
</div>
</li>
</ul>
<div class="form-text" v-if="value.length > 0">
<div class="form-text" v-if="links.length > 0">
<button data-toggle="modal" data-target="#linkModal" class="btn btn-default"><i class="fas fa-plus"></i></button>
</div>
</div>
@@ -184,6 +184,11 @@
</template>
<script>
import {createNamespacedHelpers} from 'vuex'
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
const lodashClonedeep = require('lodash.clonedeep');
// TODO error handling
export default {
props: ['index', 'value', 'errors'],
@@ -195,27 +200,31 @@ export default {
locale: 'en-US',
linkTypes: [],
query: '',
searching: false
searching: false,
links: [],
}
},
created() {
this.locale = localStorage.locale ?? 'en-US';
this.links = lodashClonedeep(this.value);
this.getLinkTypes();
},
watch: {
value: function (value) {
//console.log('Selected transactions is now:');
//console.log(value);
links: function (value) {
this.updateField({index: this.index, field: 'links', value: lodashClonedeep(value)});
}
},
methods: {
...mapMutations(
[
'updateField',
],
),
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;
}
@@ -246,27 +255,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);
}
}
}
@@ -330,9 +339,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;
}
@@ -341,9 +350,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;
}

View File

@@ -26,7 +26,7 @@
</div>
<div class="input-group">
<textarea
v-model="value"
v-model="notes"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
:placeholder="$t('firefly.notes')"
></textarea>
@@ -43,6 +43,11 @@ const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers
export default {
props: ['index', 'value', 'errors'],
name: "TransactionNotes",
data() {
return {
notes: this.value
}
},
methods: {
...mapMutations(
[
@@ -51,7 +56,7 @@ export default {
),
},
watch: {
value: function (value) {
notes: function (value) {
this.updateField({field: 'notes', index: this.index, value: value});
}
}

View File

@@ -27,7 +27,7 @@
<select
ref="piggy_bank_id"
:title="$t('firefly.piggy_bank')"
v-model="value"
v-model="piggy_bank_id"
autocomplete="off"
:class="errors.length > 0 ? 'form-control is-invalid' : 'form-control'"
name="piggy_bank_id[]"
@@ -54,7 +54,8 @@ export default {
name: "TransactionPiggyBank",
data() {
return {
piggyList: []
piggyList: [],
piggy_bank_id: this.value
}
},
created() {
@@ -97,7 +98,7 @@ export default {
},
},
watch: {
value: function (value) {
piggy_bank_id: function (value) {
this.updateField({field: 'piggy_bank_id', index: this.index, value: value});
}
},

View File

@@ -59,14 +59,13 @@ export default {
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
};
},
watch: {
'currentTag': 'initItems',
value: function (value) {
//console.log('watch: value');
//console.log(value);
tagList: function (value) {
this.updateField({field: 'tags', index: this.index, value: value});
this.updateTags = false;
this.tags = value;
@@ -81,7 +80,7 @@ export default {
shortList.push({text: value[key].text});
}
}
this.value = shortList;
this.tagList = shortList;
}
this.updateTags = true;
}

42
frontend/src/pages/transactions/edit.js vendored Normal file
View 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');

View File

@@ -62,6 +62,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')