2019-06-01 13:38:18 -05:00
|
|
|
<!--
|
|
|
|
- EditTransaction.vue
|
|
|
|
- Copyright (c) 2019 thegrumpydictator@gmail.com
|
|
|
|
-
|
|
|
|
- This file is part of Firefly III.
|
|
|
|
-
|
|
|
|
- Firefly III is free software: you can redistribute it and/or modify
|
|
|
|
- it under the terms of the GNU General Public License as published by
|
|
|
|
- the Free Software Foundation, either version 3 of the License, or
|
|
|
|
- (at your option) any later version.
|
|
|
|
-
|
|
|
|
- Firefly III 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 General Public License for more details.
|
|
|
|
-
|
|
|
|
- You should have received a copy of the GNU General Public License
|
|
|
|
- along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
-->
|
|
|
|
|
|
|
|
<template>
|
2019-08-26 23:45:35 -05:00
|
|
|
<form method="POST" action="#" accept-charset="UTF-8" class="form-horizontal" id="store"
|
2019-06-01 13:38:18 -05:00
|
|
|
enctype="multipart/form-data">
|
|
|
|
<input name="_token" type="hidden" value="xxx">
|
|
|
|
<div class="row" v-if="error_message !== ''">
|
|
|
|
<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">×</span></button>
|
|
|
|
<strong>Error!</strong> {{ error_message }}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="row" v-if="success_message !== ''">
|
|
|
|
<div class="col-lg-12">
|
|
|
|
<div class="alert alert-success alert-dismissible" role="alert">
|
|
|
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
|
|
|
|
aria-hidden="true">×</span></button>
|
|
|
|
<strong>Success!</strong> <span v-html="success_message"></span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="row" v-if="transactions.length > 1">
|
|
|
|
<div class="col-lg-6">
|
|
|
|
<div class="box">
|
|
|
|
<div class="box-header with-border">
|
|
|
|
<h3 class="box-title">
|
|
|
|
Description of the split transaction
|
|
|
|
</h3>
|
|
|
|
</div>
|
|
|
|
<div class="box-body">
|
|
|
|
<group-description
|
|
|
|
:error="group_title_errors"
|
|
|
|
v-model="group_title"
|
|
|
|
></group-description>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
<div class="row" v-for="(transaction, index) in transactions">
|
|
|
|
<div class="col-lg-12">
|
|
|
|
<div class="box">
|
|
|
|
<div class="box-header with-border">
|
|
|
|
<h3 class="box-title splitTitle">
|
|
|
|
<span v-if="transactions.length > 1">Split {{ index+1 }} / {{ transactions.length }}</span>
|
|
|
|
<span v-if="transactions.length === 1">Transaction information</span>
|
|
|
|
</h3>
|
|
|
|
<div class="box-tools pull-right" v-if="transactions.length > 1" x>
|
2019-08-27 00:20:46 -05:00
|
|
|
<button type="button" v-on:click="deleteTransaction(index, $event)" class="btn btn-xs btn-danger"><i
|
2019-06-01 13:38:18 -05:00
|
|
|
class="fa fa-trash"></i></button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="box-body">
|
|
|
|
<div class="row">
|
|
|
|
<div class="col-lg-4">
|
2019-08-07 11:52:42 -05:00
|
|
|
<transaction-description
|
|
|
|
v-model="transaction.description"
|
|
|
|
:index="index"
|
|
|
|
:error="transaction.errors.description"
|
|
|
|
>
|
|
|
|
</transaction-description>
|
2019-06-01 13:38:18 -05:00
|
|
|
<account-select
|
|
|
|
inputName="source[]"
|
|
|
|
title="Source account"
|
|
|
|
:accountName="transaction.source_account.name"
|
|
|
|
:accountTypeFilters="transaction.source_account.allowed_types"
|
|
|
|
:transactionType="transactionType"
|
|
|
|
:index="index"
|
|
|
|
v-on:clear:value="clearSource(index)"
|
|
|
|
v-on:select:account="selectedSourceAccount(index, $event)"
|
|
|
|
:error="transaction.errors.source_account"
|
|
|
|
></account-select>
|
|
|
|
<account-select
|
|
|
|
inputName="destination[]"
|
|
|
|
title="Destination account"
|
|
|
|
:accountName="transaction.destination_account.name"
|
|
|
|
:accountTypeFilters="transaction.destination_account.allowed_types"
|
|
|
|
:transactionType="transactionType"
|
|
|
|
:index="index"
|
|
|
|
v-on:clear:value="clearDestination(index)"
|
|
|
|
v-on:select:account="selectedDestinationAccount(index, $event)"
|
|
|
|
:error="transaction.errors.destination_account"
|
|
|
|
></account-select>
|
|
|
|
<standard-date
|
|
|
|
v-model="transaction.date"
|
|
|
|
:index="index"
|
|
|
|
:error="transaction.errors.date"
|
|
|
|
>
|
|
|
|
</standard-date>
|
|
|
|
<div v-if="index===0">
|
|
|
|
<transaction-type
|
|
|
|
:source="transaction.source_account.type"
|
|
|
|
:destination="transaction.destination_account.type"
|
|
|
|
v-on:set:transactionType="setTransactionType($event)"
|
|
|
|
v-on:act:limitSourceType="limitSourceType($event)"
|
|
|
|
v-on:act:limitDestinationType="limitDestinationType($event)"
|
|
|
|
></transaction-type>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="col-lg-4">
|
|
|
|
<amount
|
|
|
|
:source="transaction.source_account"
|
|
|
|
:destination="transaction.destination_account"
|
|
|
|
v-model="transaction.amount"
|
|
|
|
:error="transaction.errors.amount"
|
|
|
|
:transactionType="transactionType"
|
|
|
|
></amount>
|
|
|
|
<foreign-amount
|
|
|
|
:source="transaction.source_account"
|
|
|
|
:destination="transaction.destination_account"
|
|
|
|
v-model="transaction.foreign_amount"
|
|
|
|
:transactionType="transactionType"
|
|
|
|
:error="transaction.errors.foreign_amount"
|
|
|
|
></foreign-amount>
|
|
|
|
</div>
|
|
|
|
<div class="col-lg-4">
|
|
|
|
<budget
|
|
|
|
:transactionType="transactionType"
|
|
|
|
v-model="transaction.budget"
|
|
|
|
:error="transaction.errors.budget_id"
|
|
|
|
></budget>
|
|
|
|
<category
|
|
|
|
:transactionType="transactionType"
|
|
|
|
v-model="transaction.category"
|
|
|
|
:error="transaction.errors.category"
|
|
|
|
></category>
|
|
|
|
<tags
|
2019-08-11 00:29:05 -05:00
|
|
|
:tags="transaction.tags"
|
2019-06-01 13:38:18 -05:00
|
|
|
v-model="transaction.tags"
|
|
|
|
:error="transaction.errors.tags"
|
|
|
|
></tags>
|
|
|
|
<custom-transaction-fields
|
|
|
|
v-model="transaction.custom_fields"
|
|
|
|
:error="transaction.errors.custom_errors"
|
|
|
|
></custom-transaction-fields>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="box-footer" v-if="transactions.length-1 === index">
|
2019-08-23 06:53:05 -05:00
|
|
|
<button class="btn btn-primary" type="button" @click="addTransaction">Add another split</button>
|
2019-06-01 13:38:18 -05:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="row">
|
|
|
|
<div class="col-lg-3 col-md-4 col-sm-6 col-xs-12">
|
|
|
|
<div class="box">
|
|
|
|
<div class="box-header with-border">
|
|
|
|
<h3 class="box-title">
|
|
|
|
Submission
|
|
|
|
</h3>
|
|
|
|
</div>
|
|
|
|
<div class="box-body">
|
|
|
|
<div class="checkbox">
|
|
|
|
<label>
|
|
|
|
<input v-model="returnAfter" name="return_after" type="checkbox">
|
2019-07-19 09:08:42 -05:00
|
|
|
After updating, return here to continue editing.
|
2019-06-01 13:38:18 -05:00
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="box-footer">
|
|
|
|
<div class="btn-group">
|
|
|
|
<button class="btn btn-success" @click="submit">Update</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</form>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
export default {
|
|
|
|
name: "EditTransaction",
|
|
|
|
props: {
|
|
|
|
groupId: Number
|
|
|
|
},
|
|
|
|
mounted() {
|
|
|
|
this.getGroup();
|
|
|
|
},
|
|
|
|
ready() {
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('Ready Group ID: ' + this.groupId);
|
2019-06-01 13:38:18 -05:00
|
|
|
},
|
|
|
|
methods: {
|
2019-07-19 09:08:42 -05:00
|
|
|
positiveAmount(amount) {
|
2019-06-01 13:38:18 -05:00
|
|
|
if (amount < 0) {
|
|
|
|
return amount * -1;
|
|
|
|
}
|
|
|
|
return amount;
|
|
|
|
},
|
2019-07-19 09:08:42 -05:00
|
|
|
selectedSourceAccount(index, model) {
|
2019-06-01 13:38:18 -05:00
|
|
|
if (typeof model === 'string') {
|
|
|
|
// cant change types, only name.
|
2019-07-19 09:08:42 -05:00
|
|
|
// also clear ID
|
|
|
|
this.transactions[index].source_account.id = null;
|
2019-06-01 13:38:18 -05:00
|
|
|
this.transactions[index].source_account.name = model;
|
2019-07-19 09:08:42 -05:00
|
|
|
return;
|
2019-06-01 13:38:18 -05:00
|
|
|
}
|
2019-07-19 09:08:42 -05:00
|
|
|
this.transactions[index].source_account = {
|
|
|
|
id: model.id,
|
|
|
|
name: model.name,
|
|
|
|
type: model.type,
|
|
|
|
currency_id: model.currency_id,
|
|
|
|
currency_name: model.currency_name,
|
|
|
|
currency_code: model.currency_code,
|
|
|
|
currency_decimal_places: model.currency_decimal_places,
|
|
|
|
allowed_types: this.transactions[index].source_account.allowed_types
|
|
|
|
};
|
2019-06-01 13:38:18 -05:00
|
|
|
},
|
2019-07-19 09:08:42 -05:00
|
|
|
selectedDestinationAccount(index, model) {
|
2019-06-01 13:38:18 -05:00
|
|
|
if (typeof model === 'string') {
|
|
|
|
// cant change types, only name.
|
2019-07-19 09:08:42 -05:00
|
|
|
// also clear ID
|
|
|
|
this.transactions[index].destination_account.id = null;
|
2019-06-01 13:38:18 -05:00
|
|
|
this.transactions[index].destination_account.name = model;
|
2019-07-19 09:08:42 -05:00
|
|
|
return;
|
2019-06-01 13:38:18 -05:00
|
|
|
}
|
2019-07-19 09:08:42 -05:00
|
|
|
this.transactions[index].destination_account = {
|
|
|
|
id: model.id,
|
|
|
|
name: model.name,
|
|
|
|
type: model.type,
|
|
|
|
currency_id: model.currency_id,
|
|
|
|
currency_name: model.currency_name,
|
|
|
|
currency_code: model.currency_code,
|
|
|
|
currency_decimal_places: model.currency_decimal_places,
|
|
|
|
allowed_types: this.transactions[index].destination_account.allowed_types
|
|
|
|
};
|
2019-06-01 13:38:18 -05:00
|
|
|
},
|
2019-07-19 09:08:42 -05:00
|
|
|
clearSource(index) {
|
2019-06-01 13:38:18 -05:00
|
|
|
// reset source account:
|
|
|
|
this.transactions[index].source_account = {
|
|
|
|
id: 0,
|
|
|
|
name: '',
|
|
|
|
type: '',
|
|
|
|
currency_id: 0,
|
|
|
|
currency_name: '',
|
|
|
|
currency_code: '',
|
|
|
|
currency_decimal_places: 2,
|
|
|
|
allowed_types: this.transactions[index].source_account.allowed_types
|
|
|
|
};
|
|
|
|
// if there is a destination model, reset the types of the source
|
|
|
|
// by pretending we selected it again.
|
|
|
|
if (this.transactions[index].destination_account) {
|
|
|
|
this.selectedDestinationAccount(index, this.transactions[index].destination_account);
|
|
|
|
}
|
|
|
|
},
|
2019-07-19 09:08:42 -05:00
|
|
|
setTransactionType(type) {
|
2019-06-01 13:38:18 -05:00
|
|
|
this.transactionType = type;
|
|
|
|
},
|
2019-07-19 09:08:42 -05:00
|
|
|
deleteTransaction(index, event) {
|
2019-06-01 13:38:18 -05:00
|
|
|
event.preventDefault();
|
|
|
|
for (const key in this.transactions) {
|
|
|
|
if (
|
|
|
|
this.transactions.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
2019-07-19 09:08:42 -05:00
|
|
|
// TODO empty iff?
|
2019-06-01 13:38:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.transactions.splice(index, 1);
|
|
|
|
|
|
|
|
for (const key in this.transactions) {
|
|
|
|
if (
|
|
|
|
this.transactions.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
2019-07-19 09:08:42 -05:00
|
|
|
// TODO empty iff?
|
2019-06-01 13:38:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2019-07-19 09:08:42 -05:00
|
|
|
clearDestination(index) {
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('clearDestination(' + index + ')');
|
2019-06-01 13:38:18 -05:00
|
|
|
// reset destination account:
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('Destination allowed types first:');
|
|
|
|
// console.log(this.transactions[index].destination_account.allowed_types);
|
2019-06-01 13:38:18 -05:00
|
|
|
this.transactions[index].destination_account = {
|
|
|
|
id: 0,
|
|
|
|
name: '',
|
|
|
|
type: '',
|
|
|
|
currency_id: 0,
|
|
|
|
currency_name: '',
|
|
|
|
currency_code: '',
|
|
|
|
currency_decimal_places: 2,
|
|
|
|
allowed_types: this.transactions[index].destination_account.allowed_types
|
|
|
|
};
|
|
|
|
// reset destination allowed account types.
|
|
|
|
//this.transactions[index].source_account.allowed_types = [];
|
|
|
|
|
|
|
|
// if there is a source model, reset the types of the destination
|
|
|
|
// by pretending we selected it again.
|
|
|
|
if (this.transactions[index].source_account) {
|
|
|
|
this.selectedSourceAccount(index, this.transactions[index].source_account);
|
|
|
|
}
|
|
|
|
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('Destination allowed types after:');
|
|
|
|
// console.log(this.transactions[index].destination_account.allowed_types);
|
2019-06-01 13:38:18 -05:00
|
|
|
},
|
2019-07-19 09:08:42 -05:00
|
|
|
getGroup() {
|
2019-06-01 13:38:18 -05:00
|
|
|
|
|
|
|
const page = window.location.href.split('/');
|
|
|
|
const groupId = page[page.length - 1];
|
|
|
|
|
|
|
|
|
|
|
|
const uri = './api/v1/transactions/' + groupId + '?_token=' + document.head.querySelector('meta[name="csrf-token"]').content;
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log(uri);
|
2019-06-01 13:38:18 -05:00
|
|
|
|
|
|
|
// fill in transactions array.
|
|
|
|
axios.get(uri)
|
|
|
|
.then(response => {
|
2019-07-19 09:08:42 -05:00
|
|
|
this.processIncomingGroup(response.data.data);
|
2019-06-01 13:38:18 -05:00
|
|
|
})
|
|
|
|
.catch(error => {
|
2019-09-06 09:29:52 -05:00
|
|
|
//console.error('Some error.');
|
2019-06-01 13:38:18 -05:00
|
|
|
});
|
|
|
|
},
|
2019-07-19 09:08:42 -05:00
|
|
|
processIncomingGroup(data) {
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log(data);
|
2019-07-19 09:08:42 -05:00
|
|
|
this.group_title = data.attributes.group_title;
|
|
|
|
let transactions = data.attributes.transactions.reverse();
|
|
|
|
for (let key in transactions) {
|
|
|
|
if (transactions.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
|
|
|
let transaction = transactions[key];
|
|
|
|
this.processIncomingGroupRow(transaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
},
|
|
|
|
processIncomingGroupRow(transaction) {
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log(transaction);
|
2019-08-02 23:27:56 -05:00
|
|
|
this.setTransactionType(transaction.type);
|
2019-08-11 00:29:05 -05:00
|
|
|
|
|
|
|
let newTags = [];
|
|
|
|
for(let key in transaction.tags) {
|
|
|
|
if (transaction.tags.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
|
|
|
newTags.push({text: transaction.tags[key], tiClasses: []});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
this.transactions.push({
|
2019-08-11 12:14:13 -05:00
|
|
|
transaction_journal_id: transaction.transaction_journal_id,
|
2019-07-19 09:08:42 -05:00
|
|
|
description: transaction.description,
|
|
|
|
date: transaction.date.substr(0, 10),
|
|
|
|
amount: this.positiveAmount(transaction.amount),
|
|
|
|
category: transaction.category_name,
|
|
|
|
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: transaction.budget_id,
|
2019-08-11 00:29:05 -05:00
|
|
|
tags: newTags,
|
2019-07-19 09:08:42 -05:00
|
|
|
custom_fields: {
|
|
|
|
interest_date: transaction.interest_date,
|
|
|
|
book_date: transaction.book_date,
|
|
|
|
process_date: transaction.process_date,
|
|
|
|
due_date: transaction.due_date,
|
|
|
|
payment_date: transaction.payment_date,
|
|
|
|
invoice_date: transaction.invoice_date,
|
|
|
|
internal_reference: transaction.internal_reference,
|
|
|
|
notes: transaction.notes
|
|
|
|
},
|
|
|
|
foreign_amount: {
|
|
|
|
amount: this.positiveAmount(transaction.foreign_amount),
|
|
|
|
currency_id: transaction.foreign_currency_id
|
|
|
|
},
|
|
|
|
source_account: {
|
|
|
|
id: transaction.source_id,
|
|
|
|
name: transaction.source_name,
|
|
|
|
type: transaction.source_type,
|
|
|
|
currency_id: transaction.currency_id,
|
|
|
|
currency_name: transaction.currency_name,
|
|
|
|
currency_code: transaction.currency_code,
|
|
|
|
currency_decimal_places: transaction.currency_decimal_places,
|
|
|
|
allowed_types: [transaction.source_type]
|
|
|
|
},
|
|
|
|
destination_account: {
|
|
|
|
id: transaction.destination_id,
|
|
|
|
name: transaction.destination_name,
|
|
|
|
type: transaction.destination_type,
|
|
|
|
currency_id: transaction.currency_id,
|
|
|
|
currency_name: transaction.currency_name,
|
|
|
|
currency_code: transaction.currency_code,
|
|
|
|
currency_decimal_places: transaction.currency_decimal_places,
|
|
|
|
allowed_types: [transaction.destination_type]
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
2019-06-01 13:38:18 -05:00
|
|
|
convertData: function () {
|
|
|
|
let data = {
|
|
|
|
'transactions': [],
|
|
|
|
};
|
|
|
|
let transactionType;
|
|
|
|
let firstSource;
|
|
|
|
let firstDestination;
|
|
|
|
|
|
|
|
if (this.transactions.length > 1) {
|
|
|
|
data.group_title = this.group_title;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get transaction type from first transaction
|
|
|
|
transactionType = this.transactionType ? this.transactionType.toLowerCase() : 'invalid';
|
|
|
|
|
|
|
|
// if the transaction type is invalid, might just be that we can deduce it from
|
|
|
|
// the presence of a source or destination account
|
|
|
|
firstSource = this.transactions[0].source_account.type;
|
|
|
|
firstDestination = this.transactions[0].destination_account.type;
|
|
|
|
|
|
|
|
if ('invalid' === transactionType && ['Asset account', 'Loan', 'Debt', 'Mortgage'].includes(firstSource)) {
|
|
|
|
//console.log('Assumed this is a withdrawal.');
|
|
|
|
transactionType = 'withdrawal';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ('invalid' === transactionType && ['Asset account', 'Loan', 'Debt', 'Mortgage'].includes(firstDestination)) {
|
|
|
|
//console.log('Assumed this is a deposit.');
|
|
|
|
transactionType = 'deposit';
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let key in this.transactions) {
|
|
|
|
if (this.transactions.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
2019-07-19 09:08:42 -05:00
|
|
|
data.transactions.push(this.convertDataRow(this.transactions[key], key, transactionType));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//console.log(data);
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
return data;
|
|
|
|
},
|
|
|
|
convertDataRow(row, index, transactionType) {
|
|
|
|
let tagList = [];
|
|
|
|
let foreignAmount = null;
|
|
|
|
let foreignCurrency = null;
|
|
|
|
let currentArray;
|
|
|
|
let sourceId;
|
|
|
|
let sourceName;
|
|
|
|
let destId;
|
|
|
|
let destName;
|
|
|
|
let date;
|
|
|
|
sourceId = row.source_account.id;
|
|
|
|
sourceName = row.source_account.name;
|
|
|
|
destId = row.destination_account.id;
|
|
|
|
destName = row.destination_account.name;
|
|
|
|
|
|
|
|
date = row.date;
|
|
|
|
if (index > 0) {
|
|
|
|
date = this.transactions[0].date;
|
|
|
|
}
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
// if type is 'withdrawal' and destination is empty, cash withdrawal.
|
|
|
|
if (transactionType === 'withdrawal' && '' === destName) {
|
|
|
|
destId = window.cashAccountId;
|
|
|
|
}
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
// if type is 'deposit' and source is empty, cash deposit.
|
|
|
|
if (transactionType === 'deposit' && '' === sourceName) {
|
|
|
|
sourceId = window.cashAccountId;
|
|
|
|
}
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
// if index is over 0 and type is withdrawal or transfer, take source from index 0.
|
|
|
|
if (index > 0 && (transactionType.toLowerCase() === 'withdrawal' || transactionType.toLowerCase() === 'transfer')) {
|
|
|
|
sourceId = this.transactions[0].source_account.id;
|
|
|
|
sourceName = this.transactions[0].source_account.name;
|
|
|
|
}
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
// if index is over 0 and type is deposit or transfer, take destination from index 0.
|
|
|
|
if (index > 0 && (transactionType.toLowerCase() === 'deposit' || transactionType.toLowerCase() === 'transfer')) {
|
|
|
|
destId = this.transactions[0].destination_account.id;
|
|
|
|
destName = this.transactions[0].destination_account.name;
|
|
|
|
}
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
tagList = [];
|
|
|
|
foreignAmount = null;
|
|
|
|
foreignCurrency = null;
|
|
|
|
// loop tags
|
|
|
|
for (let tagKey in row.tags) {
|
|
|
|
if (row.tags.hasOwnProperty(tagKey) && /^0$|^[1-9]\d*$/.test(tagKey) && tagKey <= 4294967294) {
|
|
|
|
tagList.push(row.tags[tagKey].text);
|
|
|
|
}
|
|
|
|
}
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
// set foreign currency info:
|
|
|
|
if (row.foreign_amount.amount !== '' && parseFloat(row.foreign_amount.amount) !== .00) {
|
|
|
|
foreignAmount = row.foreign_amount.amount;
|
|
|
|
foreignCurrency = row.foreign_amount.currency_id;
|
|
|
|
}
|
|
|
|
if (foreignCurrency === row.currency_id) {
|
|
|
|
foreignAmount = null;
|
|
|
|
foreignCurrency = null;
|
|
|
|
}
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
// correct some id's
|
|
|
|
if (0 === destId) {
|
|
|
|
destId = null;
|
|
|
|
}
|
|
|
|
if (0 === sourceId) {
|
|
|
|
sourceId = null;
|
|
|
|
}
|
|
|
|
currentArray =
|
|
|
|
{
|
2019-08-11 12:14:13 -05:00
|
|
|
transaction_journal_id: row.transaction_journal_id,
|
2019-07-19 09:08:42 -05:00
|
|
|
type: transactionType,
|
|
|
|
date: date,
|
|
|
|
amount: row.amount,
|
|
|
|
currency_id: row.currency_id,
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
description: row.description,
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
source_id: sourceId,
|
|
|
|
source_name: sourceName,
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
destination_id: destId,
|
|
|
|
destination_name: destName,
|
2019-06-01 13:38:18 -05:00
|
|
|
|
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
category_name: row.category,
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
interest_date: row.custom_fields.interest_date,
|
|
|
|
book_date: row.custom_fields.book_date,
|
|
|
|
process_date: row.custom_fields.process_date,
|
|
|
|
due_date: row.custom_fields.due_date,
|
|
|
|
payment_date: row.custom_fields.payment_date,
|
|
|
|
invoice_date: row.custom_fields.invoice_date,
|
|
|
|
internal_reference: row.custom_fields.internal_reference,
|
|
|
|
notes: row.custom_fields.notes
|
|
|
|
};
|
2019-06-01 13:38:18 -05:00
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
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.
|
2019-09-01 07:49:26 -05:00
|
|
|
currentArray.budget_id = parseInt(row.budget);
|
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
if (parseInt(row.piggy_bank) > 0) {
|
|
|
|
currentArray.piggy_bank_id = parseInt(row.piggy_bank);
|
2019-06-01 13:38:18 -05:00
|
|
|
}
|
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
return currentArray;
|
2019-06-01 13:38:18 -05:00
|
|
|
},
|
|
|
|
submit: function (e) {
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('I am submit');
|
2019-06-01 13:38:18 -05:00
|
|
|
const page = window.location.href.split('/');
|
|
|
|
const groupId = page[page.length - 1];
|
|
|
|
const uri = './api/v1/transactions/' + groupId + '?_token=' + document.head.querySelector('meta[name="csrf-token"]').content;
|
|
|
|
const data = this.convertData();
|
|
|
|
|
|
|
|
let button = $(e.currentTarget);
|
|
|
|
button.prop("disabled", true);
|
|
|
|
|
|
|
|
axios.put(uri, data)
|
|
|
|
.then(response => {
|
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
if (0 === this.collectAttachmentData(response)) {
|
|
|
|
this.redirectUser(response.data.data.id, button);
|
2019-06-01 13:38:18 -05:00
|
|
|
}
|
2019-07-19 09:08:42 -05:00
|
|
|
|
|
|
|
// if (this.returnAfter) {
|
|
|
|
// this.setDefaultErrors();
|
|
|
|
// // do message:
|
|
|
|
// this.success_message = '<a href="transactions/show/' + response.data.data.id + '">The transaction</a> has been updated.';
|
|
|
|
// this.error_message = '';
|
|
|
|
// button.prop("disabled", false);
|
|
|
|
// } else {
|
|
|
|
// window.location.href = 'transactions/show/' + response.data.data.id + '?message=updated';
|
|
|
|
// }
|
2019-06-01 13:38:18 -05:00
|
|
|
}).catch(error => {
|
|
|
|
// give user errors things back.
|
|
|
|
// something something render errors.
|
|
|
|
this.parseErrors(error.response.data);
|
|
|
|
// something.
|
|
|
|
button.prop("disabled", false);
|
|
|
|
});
|
|
|
|
if (e) {
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
},
|
2019-07-19 09:08:42 -05:00
|
|
|
|
|
|
|
redirectUser(groupId, button) {
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('In redirectUser()');
|
2019-07-19 09:08:42 -05:00
|
|
|
// if count is 0, send user onwards.
|
|
|
|
|
|
|
|
if (this.returnAfter) {
|
|
|
|
this.setDefaultErrors();
|
|
|
|
// do message:
|
|
|
|
this.success_message = '<a href="transactions/show/' + groupId + '">The transaction</a> has been updated.';
|
|
|
|
this.error_message = '';
|
|
|
|
button.prop("disabled", false);
|
|
|
|
} else {
|
2019-08-05 12:45:20 -05:00
|
|
|
window.location.href = window.previousUri + '?transaction_group_id=' + groupId+ '&message=updated';
|
2019-07-19 09:08:42 -05:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
collectAttachmentData(response) {
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('Now incollectAttachmentData()');
|
2019-07-19 09:08:42 -05:00
|
|
|
let groupId = response.data.data.id;
|
|
|
|
|
|
|
|
// array of all files to be uploaded:
|
|
|
|
let toBeUploaded = [];
|
|
|
|
|
|
|
|
// array with all file data.
|
|
|
|
let fileData = [];
|
|
|
|
|
|
|
|
// all attachments
|
|
|
|
let attachments = $('input[name="attachments[]"]');
|
|
|
|
|
|
|
|
// loop over all attachments, and add references to this array:
|
|
|
|
for (const key in attachments) {
|
|
|
|
if (attachments.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
|
|
|
for (const fileKey in attachments[key].files) {
|
|
|
|
if (attachments[key].files.hasOwnProperty(fileKey) && /^0$|^[1-9]\d*$/.test(fileKey) && fileKey <= 4294967294) {
|
|
|
|
// include journal thing.
|
|
|
|
toBeUploaded.push(
|
|
|
|
{
|
|
|
|
journal: response.data.data.attributes.transactions[key].transaction_journal_id,
|
|
|
|
file: attachments[key].files[fileKey]
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let count = toBeUploaded.length;
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('Found ' + toBeUploaded.length + ' attachments.');
|
2019-07-19 09:08:42 -05:00
|
|
|
|
|
|
|
// loop all uploads.
|
|
|
|
for (const key in toBeUploaded) {
|
|
|
|
if (toBeUploaded.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
|
|
|
// create file reader thing that will read all of these uploads
|
|
|
|
(function (f, i, theParent) {
|
|
|
|
let fileReader = new FileReader();
|
|
|
|
fileReader.onloadend = function (evt) {
|
|
|
|
if (evt.target.readyState === FileReader.DONE) { // DONE == 2
|
|
|
|
fileData.push(
|
|
|
|
{
|
|
|
|
name: toBeUploaded[key].file.name,
|
|
|
|
journal: toBeUploaded[key].journal,
|
|
|
|
content: new Blob([evt.target.result])
|
|
|
|
}
|
|
|
|
);
|
|
|
|
if (fileData.length === count) {
|
|
|
|
theParent.uploadFiles(fileData, groupId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
fileReader.readAsArrayBuffer(f.file);
|
|
|
|
})(toBeUploaded[key], key, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
},
|
|
|
|
|
|
|
|
uploadFiles(fileData, groupId) {
|
|
|
|
let count = fileData.length;
|
|
|
|
let uploads = 0;
|
|
|
|
for (const key in fileData) {
|
|
|
|
if (fileData.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('Creating attachment #' + key);
|
2019-07-19 09:08:42 -05:00
|
|
|
// axios thing, + then.
|
|
|
|
const uri = './api/v1/attachments';
|
|
|
|
const data = {
|
|
|
|
filename: fileData[key].name,
|
|
|
|
model: 'TransactionJournal',
|
|
|
|
model_id: fileData[key].journal,
|
|
|
|
};
|
|
|
|
axios.post(uri, data)
|
|
|
|
.then(response => {
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('Created attachment #' + key);
|
|
|
|
// console.log('Uploading attachment #' + key);
|
2019-07-19 09:08:42 -05:00
|
|
|
const uploadUri = './api/v1/attachments/' + response.data.data.id + '/upload';
|
|
|
|
axios.post(uploadUri, fileData[key].content)
|
|
|
|
.then(response => {
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('Uploaded attachment #' + key);
|
2019-07-19 09:08:42 -05:00
|
|
|
uploads++;
|
|
|
|
if (uploads === count) {
|
|
|
|
// finally we can redirect the user onwards.
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('FINAL UPLOAD');
|
2019-07-19 09:08:42 -05:00
|
|
|
this.redirectUser(groupId);
|
|
|
|
}
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.log('Upload complete!');
|
2019-07-19 09:08:42 -05:00
|
|
|
return true;
|
|
|
|
}).catch(error => {
|
2019-09-06 09:29:52 -05:00
|
|
|
// console.error('Could not upload');
|
|
|
|
// console.error(error);
|
2019-07-19 09:08:42 -05:00
|
|
|
return false;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2019-06-01 13:38:18 -05:00
|
|
|
addTransaction: function (e) {
|
|
|
|
this.transactions.push({
|
2019-08-11 12:14:13 -05:00
|
|
|
transaction_journal_id: 0,
|
2019-06-01 13:38:18 -05:00
|
|
|
description: "",
|
|
|
|
date: "",
|
|
|
|
amount: "",
|
|
|
|
category: "",
|
|
|
|
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,
|
|
|
|
tags: [],
|
|
|
|
custom_fields: {
|
|
|
|
"interest_date": "",
|
|
|
|
"book_date": "",
|
|
|
|
"process_date": "",
|
|
|
|
"due_date": "",
|
|
|
|
"payment_date": "",
|
|
|
|
"invoice_date": "",
|
|
|
|
"internal_reference": "",
|
|
|
|
"notes": "",
|
|
|
|
"attachments": []
|
|
|
|
},
|
|
|
|
foreign_amount: {
|
|
|
|
amount: "",
|
|
|
|
currency_id: 0
|
|
|
|
},
|
|
|
|
source_account: {
|
|
|
|
id: 0,
|
|
|
|
name: "",
|
|
|
|
type: "",
|
|
|
|
currency_id: 0,
|
|
|
|
currency_name: '',
|
|
|
|
currency_code: '',
|
|
|
|
currency_decimal_places: 2,
|
|
|
|
allowed_types: []
|
|
|
|
},
|
|
|
|
destination_account: {
|
|
|
|
id: 0,
|
|
|
|
name: "",
|
|
|
|
type: "",
|
|
|
|
currency_id: 0,
|
|
|
|
currency_name: '',
|
|
|
|
currency_code: '',
|
|
|
|
currency_decimal_places: 2,
|
|
|
|
allowed_types: []
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (e) {
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
2019-07-19 09:08:42 -05:00
|
|
|
},
|
|
|
|
parseErrors: function (errors) {
|
|
|
|
this.setDefaultErrors();
|
|
|
|
this.error_message = "";
|
|
|
|
if (errors.message.length > 0) {
|
|
|
|
this.error_message = "There was something wrong with your submission. Please check out the errors below.";
|
|
|
|
} else {
|
|
|
|
this.error_message = '';
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2019-08-27 00:51:34 -05:00
|
|
|
// unique some things
|
|
|
|
this.transactions[transactionIndex].errors.source_account =
|
|
|
|
Array.from(new Set(this.transactions[transactionIndex].errors.source_account));
|
|
|
|
this.transactions[transactionIndex].errors.destination_account =
|
|
|
|
Array.from(new Set(this.transactions[transactionIndex].errors.destination_account));
|
|
|
|
|
2019-07-19 09:08:42 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
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: [],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2019-06-01 13:38:18 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
group: this.groupId,
|
|
|
|
error_message: "",
|
|
|
|
success_message: "",
|
|
|
|
transactions: [],
|
|
|
|
group_title: "",
|
|
|
|
returnAfter: false,
|
|
|
|
transactionType: null,
|
|
|
|
group_title_errors: [],
|
|
|
|
resetButtonDisabled: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
|
|
</style>
|