mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 10:47:00 +00:00 
			
		
		
		
	New code for transaction processing and frontend
This commit is contained in:
		
							
								
								
									
										28
									
								
								resources/assets/v2/api/v2/model/transaction/post.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								resources/assets/v2/api/v2/model/transaction/post.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * post.js | ||||
|  * Copyright (c) 2023 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| import {api} from "../../../../boot/axios"; | ||||
|  | ||||
| export default class Post { | ||||
|     post(submission) { | ||||
|         let url = '/api/v2/transactions'; | ||||
|         return api.post(url, submission); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										58
									
								
								resources/assets/v2/boot/bootstrap.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										58
									
								
								resources/assets/v2/boot/bootstrap.js
									
									
									
									
										vendored
									
									
								
							| @@ -29,39 +29,53 @@ import axios from 'axios'; | ||||
| import store from "store"; | ||||
| import observePlugin from 'store/plugins/observe'; | ||||
| import Alpine from "alpinejs"; | ||||
| import * as bootstrap from 'bootstrap' | ||||
| import * as bootstrap from 'bootstrap'; | ||||
| import {getFreshVariable} from "../store/get-fresh-variable.js"; | ||||
|  | ||||
| store.addPlugin(observePlugin); | ||||
| window.store = store; | ||||
|  | ||||
|  | ||||
| // import even more | ||||
| import {getVariable} from "../store/get-variable.js"; | ||||
| import {getViewRange} from "../support/get-viewrange.js"; | ||||
|  | ||||
| // wait for 3 promises, because we need those later on. | ||||
| window.bootstrapped = false; | ||||
| Promise.all([ | ||||
|     getVariable('viewRange'), | ||||
|     getVariable('darkMode'), | ||||
|     getVariable('locale'), | ||||
|     getVariable('language'), | ||||
| ]).then((values) => { | ||||
|     if (!store.get('start') || !store.get('end')) { | ||||
|         // calculate new start and end, and store them. | ||||
|         const range = getViewRange(values[0], new Date); | ||||
|         store.set('start', range.start); | ||||
|         store.set('end', range.end); | ||||
|     } | ||||
| window.store = store; | ||||
|  | ||||
|     // save local in window.__ something | ||||
|     window.__localeId__ = values[2]; | ||||
|     store.set('language', values[3]); | ||||
|     store.set('locale', values[3]); | ||||
|  | ||||
|     const event = new Event('firefly-iii-bootstrapped'); | ||||
|     document.dispatchEvent(event); | ||||
|     window.bootstrapped = true; | ||||
| // always grab the preference "marker" from Firefly III. | ||||
| getFreshVariable('lastActivity').then((serverValue) => { | ||||
|     const localValue = store.get('lastActivity'); | ||||
|     store.set('cacheValid', localValue === serverValue); | ||||
|     store.set('lastActivity', serverValue); | ||||
|     console.log('Server value: ' + serverValue); | ||||
|     console.log('Local value:  ' + localValue); | ||||
|     console.log('Cache valid:  ' + (localValue === serverValue)); | ||||
| }).then(() => { | ||||
|     Promise.all([ | ||||
|         getVariable('viewRange'), | ||||
|         getVariable('darkMode'), | ||||
|         getVariable('locale'), | ||||
|         getVariable('language'), | ||||
|     ]).then((values) => { | ||||
|         if (!store.get('start') || !store.get('end')) { | ||||
|             // calculate new start and end, and store them. | ||||
|             const range = getViewRange(values[0], new Date); | ||||
|             store.set('start', range.start); | ||||
|             store.set('end', range.end); | ||||
|         } | ||||
|  | ||||
|         // save local in window.__ something | ||||
|         window.__localeId__ = values[2]; | ||||
|         store.set('language', values[3]); | ||||
|         store.set('locale', values[3]); | ||||
|  | ||||
|         const event = new Event('firefly-iii-bootstrapped'); | ||||
|         document.dispatchEvent(event); | ||||
|         window.bootstrapped = true; | ||||
|     }); | ||||
| }); | ||||
| // wait for 3 promises, because we need those later on. | ||||
|  | ||||
| window.axios = axios; | ||||
| window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; | ||||
|   | ||||
| @@ -33,6 +33,8 @@ let chart = null; | ||||
| let chartData = null; | ||||
| let afterPromises = false; | ||||
|  | ||||
| const CHART_CACHE_KEY = 'dashboard-accounts-chart'; | ||||
| const ACCOUNTS_CACHE_KEY = 'dashboard-accounts-data'; | ||||
| export default () => ({ | ||||
|     loading: false, | ||||
|     loadingAccounts: false, | ||||
| @@ -44,12 +46,28 @@ export default () => ({ | ||||
|         setVariable('autoConversion', this.autoConversion); | ||||
|     }, | ||||
|     getFreshData() { | ||||
|         const dashboard = new Dashboard(); | ||||
|         dashboard.dashboard(new Date(window.store.get('start')), new Date(window.store.get('end')), null).then((response) => { | ||||
|             this.chartData = response.data; | ||||
|             this.drawChart(this.generateOptions(this.chartData)); | ||||
|         const cacheValid = window.store.get('cacheValid'); | ||||
|         let cachedData = window.store.get(CHART_CACHE_KEY); | ||||
|  | ||||
|         if (cacheValid && typeof cachedData !== 'undefined') { | ||||
|             let options = window.store.get(CHART_CACHE_KEY); | ||||
|             this.drawChart(options); | ||||
|             this.loading = false; | ||||
|         }); | ||||
|             console.log('Chart from cache'); | ||||
|         } | ||||
|         if (!cacheValid || typeof cachedData === 'undefined') { | ||||
|             const dashboard = new Dashboard(); | ||||
|             dashboard.dashboard(new Date(window.store.get('start')), new Date(window.store.get('end')), null).then((response) => { | ||||
|                 this.chartData = response.data; | ||||
|                 // cache generated options: | ||||
|                 let options = this.generateOptions(this.chartData); | ||||
|                 window.store.set(CHART_CACHE_KEY, options); | ||||
|                 this.drawChart(options); | ||||
|                 this.loading = false; | ||||
|                 console.log('Chart FRESH'); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|     }, | ||||
|     generateOptions(data) { | ||||
|         currencies = []; | ||||
| @@ -145,6 +163,15 @@ export default () => ({ | ||||
|             this.loadingAccounts = false; | ||||
|             return; | ||||
|         } | ||||
|         const cacheValid = window.store.get('cacheValid'); | ||||
|         let cachedData = window.store.get(ACCOUNTS_CACHE_KEY); | ||||
|  | ||||
|         if (cacheValid && typeof cachedData !== 'undefined') { | ||||
|             this.accountList = cachedData; | ||||
|             this.loadingAccounts = false; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // console.log('loadAccounts continue!'); | ||||
|         const max = 10; | ||||
|         let totalAccounts = 0; | ||||
| @@ -180,7 +207,10 @@ export default () => ({ | ||||
|                                     group.transactions.push({ | ||||
|                                         description: currentTransaction.description, | ||||
|                                         id: current.id, | ||||
|                                         type: currentTransaction.type, | ||||
|                                         amount_raw: parseFloat(currentTransaction.amount), | ||||
|                                         amount: formatMoney(currentTransaction.amount, currentTransaction.currency_code), | ||||
|                                         native_amount_raw: parseFloat(currentTransaction.native_amount), | ||||
|                                         native_amount: formatMoney(currentTransaction.native_amount, currentTransaction.native_code), | ||||
|                                     }); | ||||
|                                 } | ||||
| @@ -190,7 +220,9 @@ export default () => ({ | ||||
|                             accounts.push({ | ||||
|                                 name: parent.attributes.name, | ||||
|                                 id: parent.id, | ||||
|                                 balance_raw: parseFloat(parent.attributes.current_balance), | ||||
|                                 balance: formatMoney(parent.attributes.current_balance, parent.attributes.currency_code), | ||||
|                                 native_balance_raw: parseFloat(parent.attributes.native_current_balance), | ||||
|                                 native_balance: formatMoney(parent.attributes.native_current_balance, parent.attributes.native_code), | ||||
|                                 groups: groups, | ||||
|                             }); | ||||
| @@ -198,6 +230,7 @@ export default () => ({ | ||||
|                             if (count === totalAccounts) { | ||||
|                                 this.accountList = accounts; | ||||
|                                 this.loadingAccounts = false; | ||||
|                                 window.store.set(ACCOUNTS_CACHE_KEY, accounts); | ||||
|                             } | ||||
|                         }); | ||||
|                     }); | ||||
|   | ||||
| @@ -21,16 +21,66 @@ | ||||
| import '../../boot/bootstrap.js'; | ||||
| import dates from '../../pages/shared/dates.js'; | ||||
| import {createEmptySplit} from "./shared/create-empty-split.js"; | ||||
| import {parseFromEntries} from "./shared/parse-from-entries.js"; | ||||
| import formatMoney from "../../util/format-money.js"; | ||||
| //import Autocomplete from "bootstrap5-autocomplete"; | ||||
| import Post from "../../api/v2/model/transaction/post.js"; | ||||
|  | ||||
| let transactions = function () { | ||||
|     return { | ||||
|         count: 0, | ||||
|         totalAmount: 0, | ||||
|         entries: [], | ||||
|  | ||||
|         // error and success messages: | ||||
|         showError: false, | ||||
|         showSuccess: false, | ||||
|  | ||||
|         init() { | ||||
|             this.entries.push(createEmptySplit()); | ||||
|             const opts = { | ||||
|                 onSelectItem: console.log, | ||||
|             }; | ||||
|             let src = []; | ||||
|             for (let i = 0; i < 50; i++) { | ||||
|                 src.push({ | ||||
|                     title: "Option " + i, | ||||
|                     id: "opt" + i, | ||||
|                     data: { | ||||
|                         key: i, | ||||
|                     }, | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             // for each thing, make autocomplete? | ||||
|  | ||||
|  | ||||
|             this.addSplit(); | ||||
|             console.log('Ik ben init hoera'); | ||||
|  | ||||
|             // let element = document.getElementById('source_0'); | ||||
|             // new Autocomplete(element, { | ||||
|             //     items: src, | ||||
|             //     valueField: "id", | ||||
|             //     labelField: "title", | ||||
|             //     highlightTyped: true, | ||||
|             //     onSelectItem: console.log, | ||||
|             // }); | ||||
|         }, | ||||
|         submitTransaction() { | ||||
|             let transactions = parseFromEntries(this.entries); | ||||
|             let submission = { | ||||
|                 group_title: null, | ||||
|                 fire_webhooks: false, | ||||
|                 apply_rules: false, | ||||
|                 transactions: transactions | ||||
|             }; | ||||
|             let poster = new Post(); | ||||
|             console.log(submission); | ||||
|             poster.post(submission).then((response) => { | ||||
|                 console.log(response); | ||||
|             }).catch((error) => { | ||||
|                 console.error(error); | ||||
|             }); | ||||
|         }, | ||||
|         addSplit() { | ||||
|             this.entries.push(createEmptySplit()); | ||||
|   | ||||
| @@ -19,6 +19,8 @@ | ||||
|  */ | ||||
|  | ||||
|  | ||||
| import format from "date-fns/format"; | ||||
|  | ||||
| function getAccount() { | ||||
|     return { | ||||
|         id: '', | ||||
| @@ -27,10 +29,13 @@ function getAccount() { | ||||
| } | ||||
|  | ||||
| export function createEmptySplit() { | ||||
|     let now = new Date(); | ||||
|     let formatted = format(now, 'yyyy-MM-dd HH:mm'); | ||||
|     return { | ||||
|         description: 'I am descr', | ||||
|         description: 'OK then', | ||||
|         amount: '', | ||||
|         source_account: getAccount(), | ||||
|         destination_account: getAccount(), | ||||
|         date: formatted | ||||
|     }; | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,47 @@ | ||||
| /* | ||||
|  * parse-from-entries.js | ||||
|  * Copyright (c) 2023 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/>. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @param entries | ||||
|  */ | ||||
| export function parseFromEntries(entries) { | ||||
|     let returnArray = []; | ||||
|     for (let i in entries) { | ||||
|         if (entries.hasOwnProperty(i)) { | ||||
|             const entry = entries[i]; | ||||
|             let current = {}; | ||||
|  | ||||
|             // fields for transaction | ||||
|             current.description = entry.description; | ||||
|             current.source_name = entry.source_account.name; | ||||
|             current.destination_name = entry.destination_account.name; | ||||
|             current.amount = entry.amount; | ||||
|             current.date = entry.date; | ||||
|  | ||||
|             // TODO transaction type is hard coded: | ||||
|             current.type = 'withdrawal'; | ||||
|  | ||||
|  | ||||
|             returnArray.push(current); | ||||
|         } | ||||
|     } | ||||
|     return returnArray; | ||||
| } | ||||
							
								
								
									
										42
									
								
								resources/assets/v2/store/get-fresh-variable.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								resources/assets/v2/store/get-fresh-variable.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| /* | ||||
|  * get-variable.js | ||||
|  * Copyright (c) 2023 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| import Get from "../api/v1/preferences/index.js"; | ||||
| import Post from "../api/v1/preferences/post.js"; | ||||
|  | ||||
| export function getFreshVariable(name, defaultValue = null) { | ||||
|     let getter = (new Get); | ||||
|     return getter.getByName(name).then((response) => { | ||||
|         // console.log('Get from API'); | ||||
|         return Promise.resolve(parseResponse(name, response)); | ||||
|     }).catch(() => { | ||||
|         // preference does not exist (yet). | ||||
|         // POST it and then return it anyway. | ||||
|         let poster = (new Post); | ||||
|         poster.post(name, defaultValue).then((response) => { | ||||
|             return Promise.resolve(parseResponse(name, response)); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function parseResponse(name, response) { | ||||
|     return response.data.data.attributes.data; | ||||
| } | ||||
|  | ||||
| @@ -23,27 +23,30 @@ import Post from "../api/v1/preferences/post.js"; | ||||
|  | ||||
| export function getVariable(name, defaultValue = null) { | ||||
|  | ||||
|     const validCache = window.store.get('cacheValid'); | ||||
|     // currently unused, window.X can be used by the blade template | ||||
|     // to make things available quicker than if the store has to grab it through the API. | ||||
|     // then again, it's not that slow. | ||||
|     if (window.hasOwnProperty(name)) { | ||||
|     if (validCache && window.hasOwnProperty(name)) { | ||||
|         // console.log('Get from window'); | ||||
|         return Promise.resolve(window[name]); | ||||
|     } | ||||
|     // load from store2, if it's present. | ||||
|     if (window.store.get(name)) { | ||||
|         // console.log('Get from store'); | ||||
|         return Promise.resolve(window.store.get(name)); | ||||
|     const fromStore = window.store.get(name); | ||||
|     if (validCache && typeof fromStore !== 'undefined') { | ||||
|         // console.log('Get "' + name + '" from store'); | ||||
|         return Promise.resolve(fromStore); | ||||
|     } | ||||
|     let getter = (new Get); | ||||
|     return getter.getByName(name).then((response) => { | ||||
|         // console.log('Get from API'); | ||||
|         // console.log('Get "' + name + '" from API'); | ||||
|         return Promise.resolve(parseResponse(name, response)); | ||||
|     }).catch(() => { | ||||
|         // preference does not exist (yet). | ||||
|         // POST it and then return it anyway. | ||||
|         let poster = (new Post); | ||||
|         poster.post(name, defaultValue).then((response) => { | ||||
|  | ||||
|             return Promise.resolve(parseResponse(name, response)); | ||||
|         }); | ||||
|     }); | ||||
| @@ -52,7 +55,7 @@ export function getVariable(name, defaultValue = null) { | ||||
| function parseResponse(name, response) { | ||||
|     let value = response.data.data.attributes.data; | ||||
|     window.store.set(name, value); | ||||
|     // console.log('Store from API'); | ||||
|     // console.log('Store "' + name + '" in localStorage'); | ||||
|     return value; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user