Error reporting in new form.

This commit is contained in:
James Cole
2019-05-24 05:29:04 +02:00
parent 695244c928
commit 8f1928c933
18 changed files with 2151 additions and 814 deletions

2433
public/v1/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -16,7 +16,9 @@ Vue.component('custom-date', require('./components/transactions/CustomDate.vue')
Vue.component('custom-string', require('./components/transactions/CustomString.vue')); Vue.component('custom-string', require('./components/transactions/CustomString.vue'));
Vue.component('custom-attachments', require('./components/transactions/CustomAttachments.vue')); Vue.component('custom-attachments', require('./components/transactions/CustomAttachments.vue'));
Vue.component('custom-textarea', require('./components/transactions/CustomTextArea.vue')); Vue.component('custom-textarea', require('./components/transactions/CustomTextArea.vue'));
Vue.component('standard-date', require('./components/transactions/StandardDate.vue'));
Vue.component('group-description', require('./components/transactions/GroupDescription'));
Vue.component('transaction-description', require('./components/transactions/TransactionDescription'));
Vue.component('custom-transaction-fields', require('./components/transactions/CustomTransactionFields.vue')); Vue.component('custom-transaction-fields', require('./components/transactions/CustomTransactionFields.vue'));
Vue.component('piggy-bank', require('./components/transactions/PiggyBank.vue')); Vue.component('piggy-bank', require('./components/transactions/PiggyBank.vue'));

View File

@@ -18,7 +18,7 @@
- along with Firefly III. If not, see <http://www.gnu.org/licenses/>. - along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
--> -->
<template> <template>
<div class="form-group"> <div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="input-group"> <div class="input-group">
<input <input
@@ -50,6 +50,9 @@
:target="target" :target="target"
item-key="name" item-key="name"
></typeahead> ></typeahead>
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div> </div>
</div> </div>
@@ -61,6 +64,7 @@
title: String, title: String,
index: Number, index: Number,
transactionType: String, transactionType: String,
error: Array,
accountName: { accountName: {
type: String, type: String,
default: '' default: ''
@@ -106,6 +110,9 @@
}, },
methods: methods:
{ {
hasError: function () {
return this.error.length > 0;
},
triggerTransactionType: function () { triggerTransactionType: function () {
if (null === this.transactionType) { if (null === this.transactionType) {
return; return;

View File

@@ -19,12 +19,16 @@
--> -->
<template> <template>
<div class="form-group"> <div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<label class="col-sm-4 control-label" ref="cur"></label> <label class="col-sm-4 control-label" ref="cur"></label>
<div class="col-sm-8"> <div class="col-sm-8">
<input type="number" ref="amount" :value="value" @input="handleInput" step="any" class="form-control" <input type="number" ref="amount" :value="value" @input="handleInput" step="any"
class="form-control"
name="amount[]" name="amount[]"
title="amount" autocomplete="off" placeholder="Amount"> title="amount" autocomplete="off" placeholder="Amount">
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div> </div>
</div> </div>
</template> </template>
@@ -32,7 +36,7 @@
<script> <script>
export default { export default {
name: "Amount", name: "Amount",
props: ['source', 'destination', 'transactionType','value'], props: ['source', 'destination', 'transactionType', 'value', 'error'],
data() { data() {
return { return {
sourceAccount: this.source, sourceAccount: this.source,
@@ -44,6 +48,9 @@
handleInput(e) { handleInput(e) {
this.$emit('input', this.$refs.amount.value); this.$emit('input', this.$refs.amount.value);
}, },
hasError: function () {
return this.error.length > 0;
},
changeData: function () { changeData: function () {
if ('' === this.transactionType) { if ('' === this.transactionType) {
$(this.$refs.cur).text(this.sourceAccount.currency_name); $(this.$refs.cur).text(this.sourceAccount.currency_name);

View File

@@ -19,11 +19,17 @@
--> -->
<template> <template>
<div class="form-group" v-if="typeof this.transactionType !== 'undefined' && this.transactionType === 'Withdrawal'"> <div class="form-group"
v-bind:class="{ 'has-error': hasError()}"
v-if="typeof this.transactionType !== 'undefined' && this.transactionType === 'Withdrawal'">
<div class="col-sm-12"> <div class="col-sm-12">
<select name="budget[]" ref="budget" @input="handleInput" class="form-control" v-if="this.budgets.length > 0"> <select name="budget[]" ref="budget" @input="handleInput" class="form-control"
v-if="this.budgets.length > 0">
<option v-for="budget in this.budgets" :label="budget.name" :value="budget.id">{{budget.name}}</option> <option v-for="budget in this.budgets" :label="budget.name" :value="budget.id">{{budget.name}}</option>
</select> </select>
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div> </div>
</div> </div>
</template> </template>
@@ -31,7 +37,7 @@
<script> <script>
export default { export default {
name: "Budget", name: "Budget",
props: ['transactionType','value'], props: ['transactionType', 'value', 'error'],
mounted() { mounted() {
this.loadBudgets(); this.loadBudgets();
}, },
@@ -44,6 +50,9 @@
handleInput(e) { handleInput(e) {
this.$emit('input', this.$refs.budget.value); this.$emit('input', this.$refs.budget.value);
}, },
hasError: function () {
return this.error.length > 0;
},
loadBudgets: function () { loadBudgets: function () {
let URI = document.getElementsByTagName('base')[0].href + "json/budgets"; let URI = document.getElementsByTagName('base')[0].href + "json/budgets";
axios.get(URI, {}).then((res) => { axios.get(URI, {}).then((res) => {

View File

@@ -19,7 +19,7 @@
--> -->
<template> <template>
<div class="form-group"> <div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="input-group"> <div class="input-group">
<input <input
@@ -51,6 +51,9 @@
:target="target" :target="target"
item-key="name" item-key="name"
></typeahead> ></typeahead>
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div> </div>
</div> </div>
</template> </template>
@@ -61,6 +64,7 @@
props: { props: {
value: String, value: String,
inputName: String, inputName: String,
error: Array,
accountName: { accountName: {
type: String, type: String,
default: '' default: ''
@@ -81,6 +85,9 @@
this.categoryAutoCompleteURI = document.getElementsByTagName('base')[0].href + "json/categories?query="; this.categoryAutoCompleteURI = document.getElementsByTagName('base')[0].href + "json/categories?query=";
}, },
methods: { methods: {
hasError: function () {
return this.error.length > 0;
},
handleInput(e) { handleInput(e) {
if (typeof this.$refs.input.value === 'string') { if (typeof this.$refs.input.value === 'string') {
this.$emit('input', this.$refs.input.value); this.$emit('input', this.$refs.input.value);

View File

@@ -23,6 +23,16 @@
enctype="multipart/form-data"> enctype="multipart/form-data">
<input name="_token" type="hidden" value="xxx"> <input name="_token" type="hidden" value="xxx">
<div class="row" v-if="invalid_submission !== ''">
<div class="col-lg-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<strong>Error!</strong> {{ invalid_submission }}
</div>
</div>
</div>
<div class="row" v-if="transactions.length > 1"> <div class="row" v-if="transactions.length > 1">
<div class="col-lg-6"> <div class="col-lg-6">
<div class="box"> <div class="box">
@@ -32,18 +42,10 @@
</h3> </h3>
</div> </div>
<div class="box-body"> <div class="box-body">
<div class="form-group"> <group-description
<div class="col-sm-12"> :error="group_title_errors"
<input type="text" class="form-control" name="group_title" v-model="group_title"
v-model="group_title" ></group-description>
title="Description of the split transaction" autocomplete="off"
placeholder="Description of the split transaction">
<p class="help-block">
If you create a split transaction, there must be a global description for all splits
of the transaction.
</p>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -75,6 +77,7 @@
:index="index" :index="index"
v-on:clear:value="clearSource(index)" v-on:clear:value="clearSource(index)"
v-on:select:account="selectedSourceAccount(index, $event)" v-on:select:account="selectedSourceAccount(index, $event)"
:error="transaction.errors.source_account"
></account-select> ></account-select>
<account-select <account-select
inputName="destination[]" inputName="destination[]"
@@ -85,23 +88,20 @@
:index="index" :index="index"
v-on:clear:value="clearDestination(index)" v-on:clear:value="clearDestination(index)"
v-on:select:account="selectedDestinationAccount(index, $event)" v-on:select:account="selectedDestinationAccount(index, $event)"
:error="transaction.errors.destination_account"
></account-select> ></account-select>
<div class="form-group"> <transaction-description
<div class="col-sm-12"> v-model="transaction.description"
<input type="text" class="form-control" name="description[]" :index="index"
v-model="transaction.description" :error="transaction.errors.description"
title="Description" autocomplete="off" placeholder="Description"> >
</div> </transaction-description>
</div> <standard-date
<div class="form-group"> v-model="transaction.date"
<div class="col-sm-12"> :index="index"
<input type="date" class="form-control" name="date[]" :error="transaction.errors.date"
title="Date" value="" autocomplete="off" >
v-model="transaction.date" </standard-date>
:disabled="index > 0"
placeholder="Date">
</div>
</div>
<div v-if="index===0"> <div v-if="index===0">
<transaction-type <transaction-type
:source="transaction.source_account.type" :source="transaction.source_account.type"
@@ -117,6 +117,7 @@
:source="transaction.source_account" :source="transaction.source_account"
:destination="transaction.destination_account" :destination="transaction.destination_account"
v-model="transaction.amount" v-model="transaction.amount"
:error="transaction.errors.amount"
:transactionType="transactionType" :transactionType="transactionType"
></amount> ></amount>
<foreign-amount <foreign-amount
@@ -124,26 +125,33 @@
:destination="transaction.destination_account" :destination="transaction.destination_account"
v-model="transaction.foreign_amount" v-model="transaction.foreign_amount"
:transactionType="transactionType" :transactionType="transactionType"
:error="transaction.errors.foreign_amount"
></foreign-amount> ></foreign-amount>
</div> </div>
<div class="col-lg-4"> <div class="col-lg-4">
<budget <budget
:transactionType="transactionType" :transactionType="transactionType"
v-model="transaction.budget" v-model="transaction.budget"
:error="transaction.errors.budget_id"
></budget> ></budget>
<category <category
:transactionType="transactionType" :transactionType="transactionType"
v-model="transaction.category" v-model="transaction.category"
:error="transaction.errors.category"
></category> ></category>
<piggy-bank <piggy-bank
:transactionType="transactionType" :transactionType="transactionType"
v-model="transaction.piggy_bank" v-model="transaction.piggy_bank"
:error="transaction.errors.piggy_bank"
></piggy-bank> ></piggy-bank>
<tags <tags
v-model="transaction.tags" v-model="transaction.tags"
:error="transaction.errors.tags"
></tags> ></tags>
<custom-transaction-fields <custom-transaction-fields
v-model="transaction.custom_fields"></custom-transaction-fields> v-model="transaction.custom_fields"
:error="transaction.errors.custom_errors"
></custom-transaction-fields>
</div> </div>
</div> </div>
</div> </div>
@@ -165,9 +173,11 @@
</template> </template>
<script> <script>
import GroupDescription from "./GroupDescription";
export default { export default {
name: "CreateTransaction", name: "CreateTransaction",
components: {}, components: {GroupDescription},
mounted() { mounted() {
this.addTransaction(); this.addTransaction();
}, },
@@ -183,6 +193,10 @@
let transactionType; let transactionType;
let firstSource; let firstSource;
let firstDestination; let firstDestination;
let foreignAmount = null;
let foreignCurrency = null;
let currentArray;
if (this.transactions.length > 1) { if (this.transactions.length > 1) {
data.group_title = this.group_title; data.group_title = this.group_title;
} }
@@ -209,7 +223,8 @@
for (let key in this.transactions) { for (let key in this.transactions) {
if (this.transactions.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) { if (this.transactions.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
tagList = []; tagList = [];
foreignAmount = null;
foreignCurrency = null;
// loop tags // loop tags
for (let tagKey in this.transactions[key].tags) { for (let tagKey in this.transactions[key].tags) {
if (this.transactions[key].tags.hasOwnProperty(tagKey) && /^0$|^[1-9]\d*$/.test(tagKey) && key <= 4294967294) { if (this.transactions[key].tags.hasOwnProperty(tagKey) && /^0$|^[1-9]\d*$/.test(tagKey) && key <= 4294967294) {
@@ -217,8 +232,13 @@
} }
} }
// set foreign currency info:
if (this.transactions[key].foreign_amount.amount !== '' && parseFloat(this.transactions[key].foreign_amount.amount) !== .00) {
foreignAmount = this.transactions[key].foreign_amount.amount;
foreignCurrency = this.transactions[key].foreign_amount.currency_id;
}
data.transactions.push( currentArray =
{ {
type: transactionType, type: transactionType,
date: this.transactions[key].date, date: this.transactions[key].date,
@@ -226,9 +246,6 @@
amount: this.transactions[key].amount, amount: this.transactions[key].amount,
currency_id: this.transactions[key].currency_id, currency_id: this.transactions[key].currency_id,
foreign_amount: this.transactions[key].foreign_amount.amount,
foreign_currency_id: this.transactions[key].foreign_amount.currency_id,
description: this.transactions[key].description, description: this.transactions[key].description,
source_id: this.transactions[key].source_account.id, source_id: this.transactions[key].source_account.id,
@@ -237,10 +254,11 @@
destination_id: this.transactions[key].destination_account.id, destination_id: this.transactions[key].destination_account.id,
destination_name: this.transactions[key].destination_account.name, destination_name: this.transactions[key].destination_account.name,
budget_id: this.transactions[key].budget,
category_name: this.transactions[key].category, category_name: this.transactions[key].category,
piggy_bank_id: this.transactions[key].piggy_bank, //budget_id: this.transactions[key].budget,
tags: tagList, //piggy_bank_id: this.transactions[key].piggy_bank,
interest_date: this.transactions[key].custom_fields.interest_date, interest_date: this.transactions[key].custom_fields.interest_date,
book_date: this.transactions[key].custom_fields.book_date, book_date: this.transactions[key].custom_fields.book_date,
@@ -250,8 +268,24 @@
invoice_date: this.transactions[key].custom_fields.invoice_date, invoice_date: this.transactions[key].custom_fields.invoice_date,
internal_reference: this.transactions[key].custom_fields.internal_reference, internal_reference: this.transactions[key].custom_fields.internal_reference,
notes: this.transactions[key].custom_fields.notes notes: this.transactions[key].custom_fields.notes
} };
);
if (tagList.length > 0) {
currentArray.tags = tagList;
}
if (null !== foreignAmount) {
currentArray.foreign_amount = foreignAmount;
currentArray.foreign_currency_id = foreignCurrency;
}
// set budget id and piggy ID.
if(parseInt(this.transactions[key].budget) > 0) {
currentArray.budget_id = parseInt(this.transactions[key].budget);
}
if(parseInt(this.transactions[key].piggy_bank) > 0) {
currentArray.piggy_bank_id = parseInt(this.transactions[key].piggy_bank);
}
data.transactions.push(currentArray);
} }
} }
//console.log(data); //console.log(data);
@@ -264,21 +298,96 @@
axios.post(uri, data) axios.post(uri, data)
.then(response => { .then(response => {
console.log('OK!'); window.location.href = 'transactions/show/'+ response.data.data.id + '?message=OK';
//console.log(response);
}).catch(error => { }).catch(error => {
// give user errors things back. // give user errors things back.
console.log('error!'); // something something render errors.
// something something render errors. console.log(error.response.data);
this.parseErrors(error.response.data);
console.log(error.response.data); // something.
// something.
}); });
if (e) { if (e) {
e.preventDefault(); e.preventDefault();
} }
}, },
setDefaultErrors: function () {
for (const key in this.transactions) {
if (this.transactions.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
this.transactions[key].errors = {
source_account: [],
destination_account: [],
description: [],
amount: [],
date: [],
budget_id: [],
foreign_amount: [],
category: [],
piggy_bank: [],
tags: [],
// custom fields:
custom_errors: {
interest_date: [],
book_date: [],
process_date: [],
due_date: [],
payment_date: [],
invoice_date: [],
internal_reference: [],
notes: [],
attachments: [],
},
};
}
}
},
parseErrors: function (errors) {
this.setDefaultErrors();
this.invalid_submission = "";
if (errors.message.length > 0) {
this.invalid_submission = "There was something wrong with your submission. Please check out the errors below.";
}
let transactionIndex;
let fieldName;
for (const key in errors.errors) {
if (errors.errors.hasOwnProperty(key)) {
if (key === 'group_title') {
this.group_title_errors = errors.errors[key];
}
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.
switch (fieldName) {
case 'amount':
case 'date':
case 'budget_id':
case 'description':
case 'tags':
this.transactions[transactionIndex].errors[fieldName] = errors.errors[key];
break;
case 'source_name':
case 'source_id':
this.transactions[transactionIndex].errors.source_account =
this.transactions[transactionIndex].errors.source_account.concat(errors.errors[key]);
break;
case 'destination_name':
case 'destination_id':
this.transactions[transactionIndex].errors.destination_account =
this.transactions[transactionIndex].errors.destination_account.concat(errors.errors[key]);
break;
case 'foreign_amount':
case 'foreign_currency_id':
this.transactions[transactionIndex].errors.foreign_amount =
this.transactions[transactionIndex].errors.foreign_amount.concat(errors.errors[key]);
break;
}
}
}
}
},
addTransaction: function (e) { addTransaction: function (e) {
this.transactions.push({ this.transactions.push({
description: "", description: "",
@@ -286,6 +395,30 @@
amount: "", amount: "",
category: "", category: "",
piggy_bank: 0, piggy_bank: 0,
errors: {
source_account: [],
destination_account: [],
description: [],
amount: [],
date: [],
budget_id: [],
foreign_amount: [],
category: [],
piggy_bank: [],
tags: [],
// custom fields:
custom_errors: {
interest_date: [],
book_date: [],
process_date: [],
due_date: [],
payment_date: [],
invoice_date: [],
internal_reference: [],
notes: [],
attachments: [],
},
},
budget: 0, budget: 0,
tags: [], tags: [],
custom_fields: { custom_fields: {
@@ -349,7 +482,6 @@
} }
this.transactions.splice(index, 1); this.transactions.splice(index, 1);
console.log('Going to remove index ' + index);
for (const key in this.transactions) { for (const key in this.transactions) {
if ( if (
@@ -445,7 +577,9 @@
return { return {
transactionType: null, transactionType: null,
group_title: "", group_title: "",
transactions: [] transactions: [],
group_title_errors: [],
invalid_submission: ""
}; };
}, },
} }

View File

@@ -1,5 +1,7 @@
<template> <template>
<div class="form-group"> <div class="form-group"
v-bind:class="{ 'has-error': hasError()}"
>
<div class="col-sm-12 text-sm"> <div class="col-sm-12 text-sm">
{{ title }} {{ title }}
</div> </div>
@@ -9,6 +11,9 @@
:placeholder="title" :placeholder="title"
:title="title" :title="title"
:name="name" type="file" class="form-control"> :name="name" type="file" class="form-control">
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div> </div>
</div> </div>
</template> </template>
@@ -18,8 +23,14 @@
name: "CustomAttachments", name: "CustomAttachments",
props: { props: {
title: String, title: String,
name: String name: String,
error: Array
}, },
methods: {
hasError: function () {
return this.error.length > 0;
},
}
} }
</script> </script>

View File

@@ -1,5 +1,7 @@
<template> <template>
<div class="form-group"> <div class="form-group"
v-bind:class="{ 'has-error': hasError()}"
>
<div class="col-sm-12 text-sm"> <div class="col-sm-12 text-sm">
{{ title }} {{ title }}
</div> </div>
@@ -9,6 +11,9 @@
ref="date" ref="date"
:value="value" @input="handleInput" :value="value" @input="handleInput"
:placeholder="title"> :placeholder="title">
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div> </div>
</div> </div>
</template> </template>
@@ -19,12 +24,16 @@
props: { props: {
value: String, value: String,
title: String, title: String,
name: String name: String,
error: Array,
}, },
methods: { methods: {
handleInput(e) { handleInput(e) {
this.$emit('input', this.$refs.date.value); this.$emit('input', this.$refs.date.value);
} },
hasError: function () {
return this.error.length > 0;
},
} }
} }
</script> </script>

View File

@@ -1,5 +1,7 @@
<template> <template>
<div class="form-group"> <div class="form-group"
v-bind:class="{ 'has-error': hasError()}"
>
<div class="col-sm-12 text-sm"> <div class="col-sm-12 text-sm">
{{ title }} {{ title }}
</div> </div>
@@ -9,6 +11,9 @@
ref="str" ref="str"
:value="value" @input="handleInput" :value="value" @input="handleInput"
:placeholder="title"> :placeholder="title">
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div> </div>
</div> </div>
</template> </template>
@@ -19,11 +24,15 @@
props: { props: {
title: String, title: String,
name: String, name: String,
value: String value: String,
error: Array
}, },
methods: { methods: {
handleInput(e) { handleInput(e) {
this.$emit('input', this.$refs.str.value); this.$emit('input', this.$refs.str.value);
},
hasError: function () {
return this.error.length > 0;
} }
} }
} }

View File

@@ -1,14 +1,19 @@
<template> <template>
<div class="form-group"> <div class="form-group"
v-bind:class="{ 'has-error': hasError()}"
>
<div class="col-sm-12 text-sm"> <div class="col-sm-12 text-sm">
{{ title }} {{ title }}
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<textarea class="form-control" :name="name" <textarea class="form-control" :name="name"
:title="title" autocomplete="off" :title="title" autocomplete="off"
ref="str" ref="str"
:value="value" @input="handleInput" :value="value" @input="handleInput"
:placeholder="title" ></textarea> :placeholder="title"></textarea>
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div> </div>
</div> </div>
</template> </template>
@@ -19,11 +24,15 @@
props: { props: {
title: String, title: String,
name: String, name: String,
value: String value: String,
error: Array
}, },
methods: { methods: {
handleInput(e) { handleInput(e) {
this.$emit('input', this.$refs.str.value); this.$emit('input', this.$refs.str.value);
},
hasError: function () {
return this.error.length > 0;
} }
} }
} }

View File

@@ -20,18 +20,34 @@
<template> <template>
<div> <div>
<component v-model="value.interest_date" v-if="this.fields.interest_date" name="interest_date[]" title="Interest date" v-bind:is="dateComponent"></component> <component
<component v-model="value.book_date" v-if="this.fields.book_date" name="book_date[]" title="Book date" v-bind:is="dateComponent"></component> :error="error.interest_date"
<component v-model="value.process_date" v-if="this.fields.process_date" name="process_date[]" title="Process date" v-bind:is="dateComponent"></component> v-model="value.interest_date" v-if="this.fields.interest_date" name="interest_date[]" title="Interest date" v-bind:is="dateComponent"></component>
<component v-model="value.due_date" v-if="this.fields.due_date" name="due_date[]" title="Due date" v-bind:is="dateComponent"></component> <component
<component v-model="value.payment_date" v-if="this.fields.payment_date" name="payment_date[]" title="Payment date" v-bind:is="dateComponent"></component> :error="error.book_date"
v-model="value.book_date" v-if="this.fields.book_date" name="book_date[]" title="Book date" v-bind:is="dateComponent"></component>
<component
:error="error.process_date"
v-model="value.process_date" v-if="this.fields.process_date" name="process_date[]" title="Process date" v-bind:is="dateComponent"></component>
<component
:error="error.due_date"
v-model="value.due_date" v-if="this.fields.due_date" name="due_date[]" title="Due date" v-bind:is="dateComponent"></component>
<component
:error="error.payment_date"
v-model="value.payment_date" v-if="this.fields.payment_date" name="payment_date[]" title="Payment date" v-bind:is="dateComponent"></component>
<component v-model="value.invoice_date" v-if="this.fields.invoice_date" name="invoice_date[]" title="Invoice date" v-bind:is="dateComponent"></component> <component v-model="value.invoice_date" v-if="this.fields.invoice_date" name="invoice_date[]" title="Invoice date" v-bind:is="dateComponent"></component>
<component v-model="value.internal_reference" v-if="this.fields.internal_reference" name="internal_reference[]" title="Internal reference" v-bind:is="stringComponent"></component> <component
:error="error.internal_reference"
v-model="value.internal_reference" v-if="this.fields.internal_reference" name="internal_reference[]" title="Internal reference" v-bind:is="stringComponent"></component>
<component v-model="value.attachments" v-if="this.fields.attachments" name="attachments[]" title="Attachments" v-bind:is="attachmentComponent"></component> <component
:error="error.attachments"
v-model="value.attachments" v-if="this.fields.attachments" name="attachments[]" title="Attachments" v-bind:is="attachmentComponent"></component>
<component v-model="value.notes" v-if="this.fields.notes" name="notes[]" title="Notes" v-bind:is="textareaComponent"></component> <component
:error="error.notes"
v-model="value.notes" v-if="this.fields.notes" name="notes[]" title="Notes" v-bind:is="textareaComponent"></component>
</div> </div>
@@ -40,7 +56,7 @@
<script> <script>
export default { export default {
name: "CustomTransactionFields", name: "CustomTransactionFields",
props: ['value'], props: ['value','error'],
mounted() { mounted() {
this.getPreference(); this.getPreference();
}, },

View File

@@ -19,7 +19,7 @@
--> -->
<template> <template>
<div class="form-group"> <div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<div class="col-sm-4"> <div class="col-sm-4">
<select class="form-control" ref="currency_select" name="foreign_currency[]" <select class="form-control" ref="currency_select" name="foreign_currency[]"
v-if="this.enabledCurrencies.length > 0" @input="handleInput"> v-if="this.enabledCurrencies.length > 0" @input="handleInput">
@@ -38,6 +38,10 @@
<input type="number" @input="handleInput" ref="amount" :value="value.amount" step="any" class="form-control" <input type="number" @input="handleInput" ref="amount" :value="value.amount" step="any" class="form-control"
name="foreign_amount[]" v-if="this.enabledCurrencies.length > 0" name="foreign_amount[]" v-if="this.enabledCurrencies.length > 0"
title="Foreign amount" autocomplete="off" placeholder="Foreign amount"> title="Foreign amount" autocomplete="off" placeholder="Foreign amount">
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div> </div>
</div> </div>
</template> </template>
@@ -45,7 +49,7 @@
<script> <script>
export default { export default {
name: "ForeignAmountSelect", name: "ForeignAmountSelect",
props: ['source', 'destination', 'transactionType', 'value'], props: ['source', 'destination', 'transactionType', 'value','error'],
mounted() { mounted() {
this.loadCurrencies(); this.loadCurrencies();
}, },
@@ -68,6 +72,9 @@
} }
}, },
methods: { methods: {
hasError: function () {
return this.error.length > 0;
},
handleInput(e) { handleInput(e) {
this.$emit('input', { this.$emit('input', {
amount: +this.$refs.amount.value, amount: +this.$refs.amount.value,

View File

@@ -0,0 +1,42 @@
<template>
<div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<div class="col-sm-12">
<input
type="text"
class="form-control"
name="group_title"
title="Description of the split transaction"
ref="descr"
autocomplete="off"
placeholder="Description of the split transaction"
:value="value" @input="handleInput"
>
<p class="help-block" v-if="error.length === 0">
If you create a split transaction, there must be a global description for all splits
of the transaction.
</p>
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div>
</div>
</template>
<script>
export default {
props: ['error', 'value', 'index'],
name: "GroupDescription",
methods: {
hasError: function () {
return this.error.length > 0;
},
handleInput(e) {
this.$emit('input', this.$refs.descr.value);
}
}
}
</script>
<style scoped>
</style>

View File

@@ -19,11 +19,16 @@
--> -->
<template> <template>
<div class="form-group" v-if="typeof this.transactionType !== 'undefined' && this.transactionType === 'Transfer'"> <div class="form-group"
v-bind:class="{ 'has-error': hasError()}"
v-if="typeof this.transactionType !== 'undefined' && this.transactionType === 'Transfer'">
<div class="col-sm-12"> <div class="col-sm-12">
<select name="piggy_bank[]" ref="piggy" @input="handleInput" class="form-control" v-if="this.piggies.length > 0"> <select name="piggy_bank[]" ref="piggy" @input="handleInput" class="form-control" v-if="this.piggies.length > 0">
<option v-for="piggy in this.piggies" :label="piggy.name" :value="piggy.id">{{piggy.name}}</option> <option v-for="piggy in this.piggies" :label="piggy.name" :value="piggy.id">{{piggy.name}}</option>
</select> </select>
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div> </div>
</div> </div>
</template> </template>
@@ -31,7 +36,7 @@
<script> <script>
export default { export default {
name: "PiggyBank", name: "PiggyBank",
props: ['value','transactionType'], props: ['value','transactionType','error'],
mounted() { mounted() {
this.loadPiggies(); this.loadPiggies();
}, },
@@ -44,6 +49,9 @@
handleInput(e) { handleInput(e) {
this.$emit('input', this.$refs.piggy.value); this.$emit('input', this.$refs.piggy.value);
}, },
hasError: function () {
return this.error.length > 0;
},
loadPiggies: function () { loadPiggies: function () {
let URI = document.getElementsByTagName('base')[0].href + "json/piggy-banks"; let URI = document.getElementsByTagName('base')[0].href + "json/piggy-banks";
axios.get(URI, {}).then((res) => { axios.get(URI, {}).then((res) => {

View File

@@ -0,0 +1,39 @@
<template>
<div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<div class="col-sm-12">
<input
type="date"
class="form-control"
name="date[]"
title="Date"
ref="date"
autocomplete="off"
:disabled="index > 0"
placeholder="Date"
:value="value" @input="handleInput"
>
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div>
</div>
</template>
<script>
export default {
props: ['error', 'value', 'index'],
name: "StandardDate",
methods: {
hasError: function () {
return this.error.length > 0;
},
handleInput(e) {
this.$emit('input', this.$refs.date.value);
}
}
}
</script>
<style scoped>
</style>

View File

@@ -19,7 +19,9 @@
--> -->
<template> <template>
<div class="form-group"> <div class="form-group"
v-bind:class="{ 'has-error': hasError()}"
>
<div class="col-sm-12"> <div class="col-sm-12">
<vue-tags-input <vue-tags-input
v-model="tag" v-model="tag"
@@ -30,6 +32,9 @@
@tags-changed="update" @tags-changed="update"
placeholder="Tags" placeholder="Tags"
/> />
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div> </div>
</div> </div>
</template> </template>
@@ -43,7 +48,7 @@
components: { components: {
VueTagsInput VueTagsInput
}, },
props: ['value'], props: ['value','error'],
data() { data() {
return { return {
tag: '', tag: '',
@@ -61,6 +66,9 @@
this.tags = newTags; this.tags = newTags;
this.$emit('input', this.tags); this.$emit('input', this.tags);
}, },
hasError: function () {
return this.error.length > 0;
},
initItems() { initItems() {
if (this.tag.length < 2) { if (this.tag.length < 2) {
return; return;

View File

@@ -0,0 +1,38 @@
<template>
<div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<div class="col-sm-12">
<input
type="text"
class="form-control"
name="description[]"
title="Description"
ref="descr"
autocomplete="off"
placeholder="Description"
:value="value" @input="handleInput"
>
<ul class="list-unstyled" v-for="error in this.error">
<li class="text-danger">{{ error }}</li>
</ul>
</div>
</div>
</template>
<script>
export default {
props: ['error', 'value', 'index'],
name: "TransactionDescription",
methods: {
hasError: function () {
return this.error.length > 0;
},
handleInput(e) {
this.$emit('input', this.$refs.descr.value);
}
}
}
</script>
<style scoped>
</style>