James Cole 2025-01-10 06:26:28 +01:00
parent d44cd50768
commit 115e3435af
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
11 changed files with 134 additions and 39 deletions

View File

@ -146,6 +146,7 @@ return [
'select_source_account', 'select_source_account',
'split_transaction_title', 'split_transaction_title',
'errors_submission', 'errors_submission',
'is_reconciled',
'split', 'split',
'single_split', 'single_split',
'transaction_stored_link', 'transaction_stored_link',

View File

@ -203,16 +203,16 @@ export default {
return ('' === this.rates[index].rate && '' === this.rates[index].inverse) || this.updating; return ('' === this.rates[index].rate && '' === this.rates[index].inverse) || this.updating;
}, },
updateRate: function (index) { updateRate: function (index) {
console.log('Update!'); // console.log('Update!');
console.log(this.rates[index].key); // console.log(this.rates[index].key);
let parts = this.spliceKey(this.rates[index].key); let parts = this.spliceKey(this.rates[index].key);
if (0 === parts.length) { if (0 === parts.length) {
return; return;
} }
if ('' !== this.rates[index].rate) { if ('' !== this.rates[index].rate) {
// update rate // update rate
console.log('Rate is ' + this.rates[index].rate); // console.log('Rate is ' + this.rates[index].rate);
console.log('ID is ' + this.rates[index].rate_id); // console.log('ID is ' + this.rates[index].rate_id);
this.updating = true; this.updating = true;
axios.put("./api/v2/exchange-rates/" + this.rates[index].rate_id, {rate: this.rates[index].rate}) axios.put("./api/v2/exchange-rates/" + this.rates[index].rate_id, {rate: this.rates[index].rate})
.then(() => { .then(() => {
@ -221,8 +221,8 @@ export default {
} }
if ('' !== this.rates[index].inverse) { if ('' !== this.rates[index].inverse) {
// update inverse // update inverse
console.log('Inverse is ' + this.rates[index].inverse); // console.log('Inverse is ' + this.rates[index].inverse);
console.log('Inverse ID is ' + this.rates[index].inverse_id); // console.log('Inverse ID is ' + this.rates[index].inverse_id);
this.updating = true; this.updating = true;
axios.put("./api/v2/exchange-rates/" + this.rates[index].inverse_id, {rate: this.rates[index].inverse}) axios.put("./api/v2/exchange-rates/" + this.rates[index].inverse_id, {rate: this.rates[index].inverse})
.then(() => { .then(() => {
@ -231,12 +231,12 @@ export default {
} }
}, },
deleteRate: function (index) { deleteRate: function (index) {
console.log(this.rates[index].key); // console.log(this.rates[index].key);
let parts = this.spliceKey(this.rates[index].key); let parts = this.spliceKey(this.rates[index].key);
if (0 === parts.length) { if (0 === parts.length) {
return; return;
} }
console.log(parts); // console.log(parts);
// delete A to B // delete A to B
axios.delete("./api/v2/exchange-rates/rates/" + parts.from + '/' + parts.to + '?date=' + format(parts.date, 'yyyy-MM-dd')); axios.delete("./api/v2/exchange-rates/rates/" + parts.from + '/' + parts.to + '?date=' + format(parts.date, 'yyyy-MM-dd'));
@ -271,7 +271,7 @@ export default {
} }
}); });
axios.get("./api/v2/currencies/" + this.to_code).then((response) => { axios.get("./api/v2/currencies/" + this.to_code).then((response) => {
console.log(response.data.data); // console.log(response.data.data);
this.to = { this.to = {
id: response.data.data.id, id: response.data.data.id,
code: response.data.data.attributes.code, code: response.data.data.attributes.code,
@ -295,16 +295,16 @@ export default {
let rate_id = current.id; let rate_id = current.id;
let inverse_id = '0'; let inverse_id = '0';
let key = from_code + '_' + to_code + '_' + format(date, 'yyyy-MM-dd'); let key = from_code + '_' + to_code + '_' + format(date, 'yyyy-MM-dd');
console.log('Key is now "' + key + '"'); // console.log('Key is now "' + key + '"');
// perhaps the returned rate is actually the inverse rate. // perhaps the returned rate is actually the inverse rate.
if (from_code === this.to_code && to_code === this.from_code) { if (from_code === this.to_code && to_code === this.from_code) {
console.log('Inverse rate found!'); // console.log('Inverse rate found!');
key = to_code + '_' + from_code + '_' + format(date, 'yyyy-MM-dd'); key = to_code + '_' + from_code + '_' + format(date, 'yyyy-MM-dd');
rate = ''; rate = '';
inverse = current.attributes.rate; inverse = current.attributes.rate;
inverse_id = current.id; inverse_id = current.id;
console.log('Key updated to "' + key + '"'); // console.log('Key updated to "' + key + '"');
} }
if (!this.tempRates.hasOwnProperty(key)) { if (!this.tempRates.hasOwnProperty(key)) {

View File

@ -59,7 +59,7 @@ export default {
}, },
methods: { methods: {
handleInput() { handleInput() {
console.log(this.active); // console.log(this.active);
this.$emit('input', this.active); this.$emit('input', this.active);
}, },
}, },

View File

@ -887,7 +887,7 @@ export default {
deleteTransaction: function (index, event) { deleteTransaction: function (index, event) {
event.preventDefault(); event.preventDefault();
console.log('Remove transaction.'); // console.log('Remove transaction.');
this.transactions.splice(index, 1); this.transactions.splice(index, 1);
}, },
limitSourceType: function (type) { limitSourceType: function (type) {

View File

@ -154,6 +154,10 @@
:transactionType="transactionType" :transactionType="transactionType"
v-bind:title="$t('form.foreign_amount')" v-bind:title="$t('form.foreign_amount')"
></foreign-amount> ></foreign-amount>
<reconciled v-show="isReconciled"
v-model="transaction.reconciled"
:error="transaction.errors.reconciled"
></reconciled>
</div> </div>
<div class="col-lg-4"> <div class="col-lg-4">
<budget <budget
@ -455,6 +459,7 @@ export default {
transaction_journal_id: transaction.transaction_journal_id, transaction_journal_id: transaction.transaction_journal_id,
description: transaction.description, description: transaction.description,
date: transaction.date.substring(0, 16), date: transaction.date.substring(0, 16),
reconciled: transaction.reconciled,
amount: this.roundNumber(this.positiveAmount(transaction.amount), transaction.currency_decimal_places), amount: this.roundNumber(this.positiveAmount(transaction.amount), transaction.currency_decimal_places),
category: transaction.category_name, category: transaction.category_name,
errors: { errors: {
@ -464,6 +469,7 @@ export default {
amount: [], amount: [],
date: [], date: [],
budget_id: [], budget_id: [],
reconciled: [],
bill_id: [], bill_id: [],
foreign_amount: [], foreign_amount: [],
category: [], category: [],
@ -540,7 +546,7 @@ export default {
// } // }
}, },
convertData: function () { convertData: function () {
console.log('start of convertData'); // console.log('start of convertData');
let data = { let data = {
'apply_rules': this.applyRules, 'apply_rules': this.applyRules,
'fire_webhooks': this.fireWebhooks, 'fire_webhooks': this.fireWebhooks,
@ -578,7 +584,7 @@ export default {
if ('deposit' === transactionType) { if ('deposit' === transactionType) {
currencyId = this.transactions[0].destination_account.currency_id; currencyId = this.transactions[0].destination_account.currency_id;
} }
console.log('Overruled currency ID to ' + currencyId); // console.log('Overruled currency ID to ' + currencyId);
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) {
@ -586,7 +592,7 @@ export default {
} }
} }
//console.log(data); //console.log(data);
console.log('end of convertData'); // console.log('end of convertData');
return data; return data;
}, },
convertDataRow(row, index, transactionType, currencyId) { convertDataRow(row, index, transactionType, currencyId) {
@ -615,7 +621,7 @@ export default {
// } // }
row.currency_id = currencyId; row.currency_id = currencyId;
console.log('Final currency ID = ' + currencyId); // console.log('Final currency ID = ' + currencyId);
date = row.date; date = row.date;
if (index > 0) { if (index > 0) {
@ -676,6 +682,8 @@ export default {
row.amount = String(row.amount).replace(',', '.'); row.amount = String(row.amount).replace(',', '.');
} }
// console.log('Reconciled is ' + row.reconciled);
currentArray = currentArray =
{ {
transaction_journal_id: row.transaction_journal_id, transaction_journal_id: row.transaction_journal_id,
@ -688,6 +696,8 @@ export default {
source_id: sourceId, source_id: sourceId,
source_name: sourceName, source_name: sourceName,
reconciled: row.reconciled,
destination_id: destId, destination_id: destId,
destination_name: destName, destination_name: destName,
@ -726,7 +736,7 @@ export default {
if (parseInt(row.piggy_bank) > 0) { if (parseInt(row.piggy_bank) > 0) {
currentArray.piggy_bank_id = parseInt(row.piggy_bank); currentArray.piggy_bank_id = parseInt(row.piggy_bank);
} }
if(this.isReconciled && !this.storeAsNew) { if(this.isReconciled && !this.storeAsNew && true === row.reconciled) {
// drop content from array: // drop content from array:
delete currentArray.source_id; delete currentArray.source_id;
delete currentArray.source_name; delete currentArray.source_name;
@ -738,11 +748,14 @@ export default {
delete currentArray.currency_id; delete currentArray.currency_id;
currentArray.reconciled = true; currentArray.reconciled = true;
} }
if(true === row.isReconciled) {
this.isReconciled = false;
}
return currentArray; return currentArray;
}, },
submit: function (e) { submit: function (e) {
console.log('Submit!'); // console.log('Submit!');
let button = $(e.currentTarget); let button = $(e.currentTarget);
button.prop("disabled", true); button.prop("disabled", true);
@ -751,26 +764,26 @@ export default {
let uri = './api/v1/transactions/' + groupId + '?_token=' + document.head.querySelector('meta[name="csrf-token"]').content; let uri = './api/v1/transactions/' + groupId + '?_token=' + document.head.querySelector('meta[name="csrf-token"]').content;
let method = 'PUT'; let method = 'PUT';
if (this.storeAsNew) { if (this.storeAsNew) {
console.log('storeAsNew'); // console.log('storeAsNew');
// other links. // other links.
uri = './api/v1/transactions?_token=' + document.head.querySelector('meta[name="csrf-token"]').content; uri = './api/v1/transactions?_token=' + document.head.querySelector('meta[name="csrf-token"]').content;
method = 'POST'; method = 'POST';
} }
const data = this.convertData(); const data = this.convertData();
console.log('POST!'); // console.log('POST!');
axios({ axios({
method: method, method: method,
url: uri, url: uri,
data: data, data: data,
}).then(response => { }).then(response => {
console.log('Response!'); // console.log('Response!');
if (0 === this.collectAttachmentData(response)) { if (0 === this.collectAttachmentData(response)) {
const title = response.data.data.attributes.group_title ?? response.data.data.attributes.transactions[0].description; const title = response.data.data.attributes.group_title ?? response.data.data.attributes.transactions[0].description;
this.redirectUser(response.data.data.id, title); this.redirectUser(response.data.data.id, title);
} }
button.removeAttr('disabled'); button.removeAttr('disabled');
}).catch(error => { }).catch(error => {
console.log('Error :('); // console.log('Error :(');
// give user errors things back. // give user errors things back.
// something something render errors. // something something render errors.
this.parseErrors(error.response.data); this.parseErrors(error.response.data);
@ -779,11 +792,11 @@ export default {
if (e) { if (e) {
e.preventDefault(); e.preventDefault();
} }
console.log('DONE with method.'); // console.log('DONE with method.');
}, },
redirectUser(groupId, title) { redirectUser(groupId, title) {
console.log('Now in redirectUser'); // console.log('Now in redirectUser');
if (this.returnAfter) { if (this.returnAfter) {
this.setDefaultErrors(); this.setDefaultErrors();
// do message if update or new: // do message if update or new:
@ -801,11 +814,11 @@ export default {
window.location.href = window.previousUrl + '?transaction_group_id=' + groupId + '&message=updated'; window.location.href = window.previousUrl + '?transaction_group_id=' + groupId + '&message=updated';
} }
} }
console.log('End of redirectUser'); // console.log('End of redirectUser');
}, },
collectAttachmentData(response) { collectAttachmentData(response) {
console.log('Now incollectAttachmentData()'); // console.log('Now incollectAttachmentData()');
let groupId = response.data.data.id; let groupId = response.data.data.id;
// array of all files to be uploaded: // array of all files to be uploaded:
@ -837,7 +850,7 @@ export default {
} }
} }
let count = toBeUploaded.length; let count = toBeUploaded.length;
console.log('Found ' + toBeUploaded.length + ' attachments.'); // console.log('Found ' + toBeUploaded.length + ' attachments.');
// loop all uploads. // loop all uploads.
for (const key in toBeUploaded) { for (const key in toBeUploaded) {
@ -863,7 +876,7 @@ export default {
})(toBeUploaded[key], key, this); })(toBeUploaded[key], key, this);
} }
} }
console.log('Done with collectAttachmentData()'); // console.log('Done with collectAttachmentData()');
return count; return count;
}, },
@ -963,6 +976,7 @@ export default {
foreign_amount: [], foreign_amount: [],
category: [], category: [],
piggy_bank: [], piggy_bank: [],
reconciled: [],
tags: [], tags: [],
// custom fields: // custom fields:
custom_errors: { custom_errors: {
@ -1060,6 +1074,7 @@ export default {
case 'budget_id': case 'budget_id':
case 'bill_id': case 'bill_id':
case 'description': case 'description':
case 'reconciled':
case 'tags': case 'tags':
this.transactions[transactionIndex].errors[fieldName] = errors.errors[key]; this.transactions[transactionIndex].errors[fieldName] = errors.errors[key];
break; break;
@ -1106,6 +1121,7 @@ export default {
bill_id: [], bill_id: [],
foreign_amount: [], foreign_amount: [],
category: [], category: [],
reconciled: [],
piggy_bank: [], piggy_bank: [],
tags: [], tags: [],
// custom fields: // custom fields:

View File

@ -0,0 +1,75 @@
<!--
- Reconciled.vue
- Copyright (c) 2025 james@firefly-iii.org.
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see https://www.gnu.org/licenses/.
-->
<template>
<div class="form-group" v-bind:class="{ 'has-error': hasError()}">
<div class="col-sm-8 col-sm-offset-4 text-sm">
</div>
<label ref="cur" class="col-sm-4 control-label">
{{ $t('firefly.is_reconciled') }}
</label>
<div class="col-sm-8">
<div class="checkbox">
<label>
<input name="create_another" @input="handleInput" type="checkbox" v-model="reconciled">
</label>
</div>
</div>
<ul v-for="error in this.error" class="list-unstyled">
<li class="text-danger">{{ error }}</li>
</ul>
</div>
</template>
<script>
export default {
name: "Reconciled",
props: {
value: Boolean,
error: Array
},
data() {
return {
reconciled: this.value
}
},
watch: {
reconciled: function () {
// console.log('reconciled is ' + this.reconciled);
this.$emit('input', this.reconciled);
},
},
methods: {
handleInput(e) {
},
hasError: function () {
return this.error.length > 0;
},
}
}
</script>
<style scoped>
</style>

View File

@ -74,13 +74,13 @@ export default {
}, },
methods: { methods: {
update(newTags) { update(newTags) {
console.log('update', newTags); // console.log('update', newTags);
this.autocompleteItems = []; this.autocompleteItems = [];
this.tags = newTags; this.tags = newTags;
this.$emit('input', this.tags); this.$emit('input', this.tags);
}, },
clearTags() { clearTags() {
console.log('clearTags'); // console.log('clearTags');
this.tags = []; this.tags = [];
this.$emit('input', this.tags); this.$emit('input', this.tags);
@ -89,7 +89,7 @@ export default {
return this.error.length > 0; return this.error.length > 0;
}, },
initItems() { initItems() {
console.log('Now in initItems'); // console.log('Now in initItems');
if (this.tag.length < 2) { if (this.tag.length < 2) {
return; return;
} }

View File

@ -124,7 +124,7 @@ export default {
}, },
downloadWebhook: function (id) { downloadWebhook: function (id) {
axios.get('./api/v1/webhooks/' + id).then(response => { axios.get('./api/v1/webhooks/' + id).then(response => {
console.log(response.data.data.attributes); // console.log(response.data.data.attributes);
this.title = response.data.data.attributes.title; this.title = response.data.data.attributes.title;
this.id = parseInt(response.data.data.id); this.id = parseInt(response.data.data.id);

View File

@ -290,7 +290,7 @@ export default {
} }
let journalId = parseInt(prompt('Enter a transaction ID')); let journalId = parseInt(prompt('Enter a transaction ID'));
if (journalId !== null && journalId > 0 && journalId <= 16777216) { if (journalId !== null && journalId > 0 && journalId <= 16777216) {
console.log('OK 1'); // console.log('OK 1');
this.disabledTrigger = true; this.disabledTrigger = true;
// disable button. Add informative message. // disable button. Add informative message.
//let button = $('#triggerButton'); //let button = $('#triggerButton');
@ -300,7 +300,7 @@ export default {
// TODO actually trigger the webhook. // TODO actually trigger the webhook.
axios.post('./api/v1/webhooks/' + this.id + '/trigger-transaction/' + journalId, {}); axios.post('./api/v1/webhooks/' + this.id + '/trigger-transaction/' + journalId, {});
//button.prop('disabled', false).removeClass('disabled'); //button.prop('disabled', false).removeClass('disabled');
console.log('OK 2'); // console.log('OK 2');
// set a time-outs. // set a time-outs.
this.loading = true; this.loading = true;
@ -308,7 +308,7 @@ export default {
this.getWebhook(); this.getWebhook();
this.disabledTrigger = false; this.disabledTrigger = false;
}, 2000); }, 2000);
console.log('OK 3'); // console.log('OK 3');
} }
@ -363,7 +363,7 @@ export default {
}, },
downloadWebhook: function () { downloadWebhook: function () {
axios.get('./api/v1/webhooks/' + this.id).then(response => { axios.get('./api/v1/webhooks/' + this.id).then(response => {
console.log(response.data.data.attributes); // console.log(response.data.data.attributes);
this.edit_url = './webhooks/edit/' + this.id; this.edit_url = './webhooks/edit/' + this.id;
this.delete_url = './webhooks/delete/' + this.id; this.delete_url = './webhooks/delete/' + this.id;
this.title = response.data.data.attributes.title; this.title = response.data.data.attributes.title;

View File

@ -31,6 +31,7 @@ import PiggyBank from "./components/transactions/PiggyBank";
import Tags from "./components/transactions/Tags"; import Tags from "./components/transactions/Tags";
import Category from "./components/transactions/Category"; import Category from "./components/transactions/Category";
import Amount from "./components/transactions/Amount"; import Amount from "./components/transactions/Amount";
import Reconciled from "./components/transactions/Reconciled";
import ForeignAmountSelect from "./components/transactions/ForeignAmountSelect"; import ForeignAmountSelect from "./components/transactions/ForeignAmountSelect";
import TransactionType from "./components/transactions/TransactionType"; import TransactionType from "./components/transactions/TransactionType";
import AccountSelect from "./components/transactions/AccountSelect"; import AccountSelect from "./components/transactions/AccountSelect";
@ -63,6 +64,7 @@ Vue.component('tags', Tags);
Vue.component('category', Category); Vue.component('category', Category);
Vue.component('amount', Amount); Vue.component('amount', Amount);
Vue.component('foreign-amount', ForeignAmountSelect); Vue.component('foreign-amount', ForeignAmountSelect);
Vue.component('reconciled', Reconciled);
Vue.component('transaction-type', TransactionType); Vue.component('transaction-type', TransactionType);
Vue.component('account-select', AccountSelect); Vue.component('account-select', AccountSelect);

View File

@ -1677,7 +1677,8 @@ return [
'list_all_attachments' => 'List of all attachments', 'list_all_attachments' => 'List of all attachments',
// transaction index // transaction index
'is_reconciled_fields_dropped' => 'Because this transaction is reconciled, you will not be able to update the accounts, nor the amount(s).', 'is_reconciled_fields_dropped' => 'Because this transaction is reconciled, you will not be able to update the accounts, nor the amount(s) unless you remove the reconciliation flag.',
'is_reconciled' => 'Is reconciled',
'title_expenses' => 'Expenses', 'title_expenses' => 'Expenses',
'title_withdrawal' => 'Expenses', 'title_withdrawal' => 'Expenses',
'title_revenue' => 'Revenue / income', 'title_revenue' => 'Revenue / income',