diff --git a/app/Api/V1/Controllers/Models/Transaction/StoreController.php b/app/Api/V1/Controllers/Models/Transaction/StoreController.php index d95d92f441..a1990926a7 100644 --- a/app/Api/V1/Controllers/Models/Transaction/StoreController.php +++ b/app/Api/V1/Controllers/Models/Transaction/StoreController.php @@ -37,6 +37,7 @@ use Illuminate\Http\JsonResponse; use Illuminate\Validation\ValidationException; use League\Fractal\Resource\Item; use Log; +use Validator; /** * Class StoreController diff --git a/frontend/src/components/shared/transactions.js b/frontend/src/components/shared/transactions.js new file mode 100644 index 0000000000..d96cd66093 --- /dev/null +++ b/frontend/src/components/shared/transactions.js @@ -0,0 +1,166 @@ +/* + * transactions.js + * Copyright (c) 2021 james@firefly-iii.org + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +export function getDefaultErrors() { + return { + description: [], + amount: [], + source: [], + destination: [], + currency: [], + foreign_currency: [], + foreign_amount: [], + date: [], + custom_dates: [], + budget: [], + category: [], + bill: [], + tags: [], + piggy_bank: [], + internal_reference: [], + external_url: [], + notes: [], + location: [] + }; +} + +export function getDefaultTransaction() { + return { + // basic + description: '', + transaction_journal_id: 0, + // accounts: + source_account_id: null, + source_account_name: null, + source_account_type: null, + + source_account_currency_id: null, + source_account_currency_code: null, + source_account_currency_symbol: null, + + destination_account_id: null, + destination_account_name: null, + destination_account_type: null, + + destination_account_currency_id: null, + destination_account_currency_code: null, + destination_account_currency_symbol: null, + + source_account: { + id: 0, + name: "", + name_with_balance: "", + type: "", + currency_id: 0, + currency_name: '', + currency_code: '', + currency_decimal_places: 2 + }, + destination_account: { + id: 0, + name: "", + type: "", + currency_id: 0, + currency_name: '', + currency_code: '', + currency_decimal_places: 2 + }, + + // amount: + amount: '', + currency_id: 0, + foreign_amount: '', + foreign_currency_id: 0, + + // meta data + category: null, + budget_id: 0, + bill_id: 0, + piggy_bank_id: 0, + tags: [], + + // optional date fields (6x): + interest_date: null, + book_date: null, + process_date: null, + due_date: null, + payment_date: null, + invoice_date: null, + + // optional other fields: + internal_reference: null, + external_url: null, + external_id: null, + notes: null, + + // transaction links: + links: [], + attachments: [], + // location: + zoom_level: null, + longitude: null, + latitude: null, + + // error handling + errors: {}, + } +} + +export function toW3CString(date) { + // https://gist.github.com/tristanlins/6585391 + let year = date.getFullYear(); + let month = date.getMonth(); + month++; + if (month < 10) { + month = '0' + month; + } + let day = date.getDate(); + if (day < 10) { + day = '0' + day; + } + let hours = date.getHours(); + if (hours < 10) { + hours = '0' + hours; + } + let minutes = date.getMinutes(); + if (minutes < 10) { + minutes = '0' + minutes; + } + let seconds = date.getSeconds(); + if (seconds < 10) { + seconds = '0' + seconds; + } + let offset = -date.getTimezoneOffset(); + let offsetHours = Math.abs(Math.floor(offset / 60)); + let offsetMinutes = Math.abs(offset) - offsetHours * 60; + if (offsetHours < 10) { + offsetHours = '0' + offsetHours; + } + if (offsetMinutes < 10) { + offsetMinutes = '0' + offsetMinutes; + } + let offsetSign = '+'; + if (offset < 0) { + offsetSign = '-'; + } + return year + '-' + month + '-' + day + + 'T' + hours + ':' + minutes + ':' + seconds + + offsetSign + offsetHours + ':' + offsetMinutes; +} \ No newline at end of file diff --git a/frontend/src/components/store/modules/transactions/create.js b/frontend/src/components/store/modules/transactions/create.js index f791839477..4d691b044b 100644 --- a/frontend/src/components/store/modules/transactions/create.js +++ b/frontend/src/components/store/modules/transactions/create.js @@ -20,6 +20,8 @@ const lodashClonedeep = require('lodash.clonedeep'); +import {getDefaultTransaction, getDefaultErrors} from '../../../shared/transactions'; + // initial state const state = () => ({ transactionType: 'any', @@ -33,105 +35,8 @@ const state = () => ({ payment_date: false, invoice_date: false, }, - defaultErrors: { - description: [], - amount: [], - source: [], - destination: [], - currency: [], - foreign_currency: [], - foreign_amount: [], - date: [], - custom_dates: [], - budget: [], - category: [], - bill: [], - tags: [], - piggy_bank: [], - internal_reference: [], - external_url: [], - notes: [], - location: [] - }, - defaultTransaction: { - // basic - description: '', - transaction_journal_id: 0, - // accounts: - source_account_id: null, - source_account_name: null, - source_account_type: null, - - source_account_currency_id: null, - source_account_currency_code: null, - source_account_currency_symbol: null, - - destination_account_id: null, - destination_account_name: null, - destination_account_type: null, - - destination_account_currency_id: null, - destination_account_currency_code: null, - destination_account_currency_symbol: null, - - source_account: { - id: 0, - name: "", - name_with_balance: "", - type: "", - currency_id: 0, - currency_name: '', - currency_code: '', - currency_decimal_places: 2 - }, - destination_account: { - id: 0, - name: "", - type: "", - currency_id: 0, - currency_name: '', - currency_code: '', - currency_decimal_places: 2 - }, - - // amount: - amount: '', - currency_id: 0, - foreign_amount: '', - foreign_currency_id: 0, - - // meta data - category: null, - budget_id: 0, - bill_id: 0, - piggy_bank_id: 0, - tags: [], - - // optional date fields (6x): - interest_date: null, - book_date: null, - process_date: null, - due_date: null, - payment_date: null, - invoice_date: null, - - // optional other fields: - internal_reference: null, - external_url: null, - external_id: null, - notes: null, - - // transaction links: - links: [], - attachments: [], - // location: - zoom_level: null, - longitude: null, - latitude: null, - - // error handling - errors: {}, - }, + defaultTransaction: getDefaultTransaction(), + defaultErrors: getDefaultErrors() } ) diff --git a/frontend/src/components/transactions/Create.vue b/frontend/src/components/transactions/Create.vue index 4ccb56fb33..ba6d5c7e6c 100644 --- a/frontend/src/components/transactions/Create.vue +++ b/frontend/src/components/transactions/Create.vue @@ -119,6 +119,7 @@ import Alert from '../partials/Alert'; import SplitPills from "./SplitPills"; import TransactionGroupTitle from "./TransactionGroupTitle"; import SplitForm from "./SplitForm"; +import {toW3CString} from '../shared/transactions'; const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create') @@ -130,10 +131,13 @@ export default { SplitPills, TransactionGroupTitle, }, + /** + * Grab some stuff from the API, add the first transaction. + */ created() { - this.storeAllowedOpposingTypes(); - this.storeAccountToTransaction(); - this.storeCustomFields(); + this.getAllowedOpposingTypes(); + this.getAccountToTransaction(); + this.getCustomFields(); this.addTransaction(); }, data() { @@ -142,8 +146,8 @@ export default { errorMessage: '', successMessage: '', - // custom fields, useful for components: - customFields: [], + // custom fields to show, useful for components: + customFields: {}, // states for the form (makes sense right) enableSubmit: true, @@ -184,6 +188,9 @@ export default { } }, computed: { + /** + * Grabbed from the store. + */ ...mapGetters([ 'transactionType', 'transactions', @@ -191,9 +198,6 @@ export default { ]) }, watch: { - transactions: function () { - // console.log('updated transactions'); - }, submittedTransaction: function () { // see finalizeSubmit() this.finalizeSubmit(); @@ -230,18 +234,6 @@ export default { // console.log('Triggered to remove transaction ' + payload.index); this.$store.commit('transactions/create/deleteTransaction', payload); }, - /** - * This method grabs the users preferred custom transaction fields. It's used when configuring the - * custom date selects that will be available. It could be something the component does by itself, - * thereby separating concerns. This is on my list. If it changes to a per-component thing, then - * it should be done via the create.js Vue store because multiple components are interested in the - * user's custom transaction fields. - */ - storeCustomFields: function () { - axios.get('./api/v1/preferences/transaction_journal_optional_fields').then(response => { - this.customFields = response.data.data.attributes.data; - }); - }, /** * Submitting a transaction consists of 3 steps: submitting the transaction, uploading attachments * and creating links. Only once all three steps are executed may the message be shown or the user be @@ -714,7 +706,7 @@ export default { theDate.setHours(this.time.getHours()); theDate.setMinutes(this.time.getMinutes()); theDate.setSeconds(this.time.getSeconds()); - dateStr = this.toW3CString(theDate); + dateStr = toW3CString(theDate); } // console.log('dateStr = ' + dateStr); @@ -829,60 +821,37 @@ export default { // return it. return currentSplit; }, - toW3CString: function (date) { - // https://gist.github.com/tristanlins/6585391 - let year = date.getFullYear(); - let month = date.getMonth(); - month++; - if (month < 10) { - month = '0' + month; - } - let day = date.getDate(); - if (day < 10) { - day = '0' + day; - } - let hours = date.getHours(); - if (hours < 10) { - hours = '0' + hours; - } - let minutes = date.getMinutes(); - if (minutes < 10) { - minutes = '0' + minutes; - } - let seconds = date.getSeconds(); - if (seconds < 10) { - seconds = '0' + seconds; - } - let offset = -date.getTimezoneOffset(); - let offsetHours = Math.abs(Math.floor(offset / 60)); - let offsetMinutes = Math.abs(offset) - offsetHours * 60; - if (offsetHours < 10) { - offsetHours = '0' + offsetHours; - } - if (offsetMinutes < 10) { - offsetMinutes = '0' + offsetMinutes; - } - let offsetSign = '+'; - if (offset < 0) { - offsetSign = '-'; - } - return year + '-' + month + '-' + day + - 'T' + hours + ':' + minutes + ':' + seconds + - offsetSign + offsetHours + ':' + offsetMinutes; - }, - storeAllowedOpposingTypes: function () { + /** + * Get API value. + */ + getAllowedOpposingTypes: function () { axios.get('./api/v1/configuration/static/firefly.allowed_opposing_types') .then(response => { this.allowedOpposingTypes = response.data['firefly.allowed_opposing_types']; // console.log('Set allowedOpposingTypes'); }); }, - storeAccountToTransaction: function () { + /** + * Get API value. + */ + getAccountToTransaction: function () { axios.get('./api/v1/configuration/static/firefly.account_to_transaction') .then(response => { this.accountToTransaction = response.data['firefly.account_to_transaction']; }); }, + /** + * This method grabs the users preferred custom transaction fields. It's used when configuring the + * custom date selects that will be available. It could be something the component does by itself, + * thereby separating concerns. This is on my list. If it changes to a per-component thing, then + * it should be done via the create.js Vue store because multiple components are interested in the + * user's custom transaction fields. + */ + getCustomFields: function () { + axios.get('./api/v1/preferences/transaction_journal_optional_fields').then(response => { + this.customFields = response.data.data.attributes.data; + }); + }, setDestinationAllowedTypes: function (value) { // console.log('Create::setDestinationAllowedTypes'); // console.log(value); diff --git a/frontend/src/components/transactions/Edit.vue b/frontend/src/components/transactions/Edit.vue index 45239a9785..4b467bf6e7 100644 --- a/frontend/src/components/transactions/Edit.vue +++ b/frontend/src/components/transactions/Edit.vue @@ -20,19 +20,545 @@