support using duplicate checker to prevent duplicate submissions for new transaction record

This commit is contained in:
MaysWind
2024-07-07 21:28:07 +08:00
parent a2d6aff28b
commit 847349dcbd
28 changed files with 371 additions and 31 deletions
+25 -1
View File
@@ -2,6 +2,8 @@ import Clipboard from 'clipboard';
import CryptoJS from 'crypto-js';
import uaParser from 'ua-parser-js';
import { base64encode } from './common.js';
export function asyncLoadAssets(type, assetUrl) {
return new Promise(function (resolve, reject) {
let addElement = false;
@@ -71,10 +73,32 @@ export function asyncLoadAssets(type, assetUrl) {
}
export function generateRandomString() {
const baseString = 'ebk_' + Math.round(new Date().getTime() / 1000) + '_' + Math.random();
let baseString = 'ebk_' + new Date().getTime();
if (crypto && crypto.getRandomValues) {
const randoms = new Uint8Array(256);
crypto.getRandomValues(randoms);
baseString += '_' + base64encode(randoms);
} else {
baseString += '_' + Math.random();
}
return CryptoJS.SHA256(baseString).toString();
}
export function generateRandomUUID() {
const randomString = generateRandomString();
// convert hash string to UUID Version 8
const uuid = randomString.substring(0, 8) + '-'
+ randomString.substring(8, 12) + '-'
+ '8' + randomString.substring(13, 16) + '-'
+ (0x8 | (parseInt(randomString.charAt(16), 16) & 0x3)).toString(16) + randomString.substring(17, 20) + '-'
+ randomString.substring(20, 32);
return uuid;
}
export function parseUserAgent(ua) {
const uaParseRet = uaParser(ua);
+9 -6
View File
@@ -238,7 +238,7 @@ export default {
getAccount: ({ id }) => {
return axios.get('v1/accounts/get.json?id=' + id);
},
addAccount: ({ category, type, name, icon, color, currency, balance, comment, subAccounts }) => {
addAccount: ({ category, type, name, icon, color, currency, balance, comment, subAccounts, clientSessionId }) => {
return axios.post('v1/accounts/add.json', {
category,
type,
@@ -248,7 +248,8 @@ export default {
currency,
balance,
comment,
subAccounts
subAccounts,
clientSessionId
});
},
modifyAccount: ({ id, category, name, icon, color, comment, hidden, subAccounts }) => {
@@ -383,7 +384,7 @@ export default {
getTransaction: ({ id }) => {
return axios.get(`v1/transactions/get.json?id=${id}&trim_account=true&trim_category=true&trim_tag=true`);
},
addTransaction: ({ type, categoryId, time, sourceAccountId, destinationAccountId, sourceAmount, destinationAmount, hideAmount, tagIds, comment, geoLocation, utcOffset }) => {
addTransaction: ({ type, categoryId, time, sourceAccountId, destinationAccountId, sourceAmount, destinationAmount, hideAmount, tagIds, comment, geoLocation, utcOffset, clientSessionId }) => {
return axios.post('v1/transactions/add.json', {
type,
categoryId,
@@ -396,7 +397,8 @@ export default {
tagIds,
comment,
geoLocation,
utcOffset
utcOffset,
clientSessionId
});
},
modifyTransaction: ({ id, type, categoryId, time, sourceAccountId, destinationAccountId, sourceAmount, destinationAmount, hideAmount, tagIds, comment, geoLocation, utcOffset }) => {
@@ -427,14 +429,15 @@ export default {
getTransactionCategory: ({ id }) => {
return axios.get('v1/transaction/categories/get.json?id=' + id);
},
addTransactionCategory: ({ name, type, parentId, icon, color, comment }) => {
addTransactionCategory: ({ name, type, parentId, icon, color, comment, clientSessionId }) => {
return axios.post('v1/transaction/categories/add.json', {
name,
type,
parentId,
icon,
color,
comment
comment,
clientSessionId
});
},
addTransactionCategoryBatch: ({ categories }) => {
+5 -1
View File
@@ -736,7 +736,7 @@ export const useAccountsStore = defineStore('accounts', {
});
});
},
saveAccount({ account, subAccounts, isEdit }) {
saveAccount({ account, subAccounts, isEdit, clientSessionId }) {
const self = this;
const submitSubAccounts = [];
@@ -776,6 +776,10 @@ export const useAccountsStore = defineStore('accounts', {
subAccounts: account.type === accountConstants.allAccountTypes.SingleAccount ? null : submitSubAccounts,
};
if (clientSessionId) {
submitAccount.clientSessionId = clientSessionId;
}
if (isEdit) {
submitAccount.id = account.id;
submitAccount.hidden = !account.visible;
+5 -1
View File
@@ -745,7 +745,7 @@ export const useTransactionsStore = defineStore('transactions', {
});
});
},
saveTransaction({ transaction, defaultCurrency, isEdit }) {
saveTransaction({ transaction, defaultCurrency, isEdit, clientSessionId }) {
const self = this;
const settingsStore = useSettingsStore();
const exchangeRatesStore = useExchangeRatesStore();
@@ -764,6 +764,10 @@ export const useTransactionsStore = defineStore('transactions', {
utcOffset: transaction.utcOffset
};
if (clientSessionId) {
submitTransaction.clientSessionId = clientSessionId;
}
if (transaction.type === transactionConstants.allTransactionTypes.Expense) {
submitTransaction.categoryId = transaction.expenseCategory;
} else if (transaction.type === transactionConstants.allTransactionTypes.Income) {
+5 -1
View File
@@ -257,7 +257,7 @@ export const useTransactionCategoriesStore = defineStore('transactionCategories'
});
});
},
saveCategory({ category, isEdit }) {
saveCategory({ category, isEdit, clientSessionId }) {
const self = this;
const submitCategory = {
@@ -269,6 +269,10 @@ export const useTransactionCategoriesStore = defineStore('transactionCategories'
comment: category.comment
};
if (clientSessionId) {
submitCategory.clientSessionId = clientSessionId;
}
if (isEdit) {
submitCategory.id = category.id;
submitCategory.hidden = !category.visible;
@@ -181,6 +181,7 @@ import accountConstants from '@/consts/account.js';
import iconConstants from '@/consts/icon.js';
import colorConstants from '@/consts/color.js';
import { isNumber } from '@/lib/common.js';
import { generateRandomUUID } from '@/lib/misc.js';
import {
setAccountModelByAnotherAccount,
setAccountSuitableIcon
@@ -208,6 +209,7 @@ export default {
showState: false,
activeTab: 'account',
editAccountId: null,
clientSessionId: '',
loading: false,
account: newAccount,
subAccounts: [],
@@ -314,6 +316,7 @@ export default {
}
self.editAccountId = null;
self.clientSessionId = generateRandomUUID();
self.loading = false;
}
@@ -370,7 +373,8 @@ export default {
self.accountsStore.saveAccount({
account: self.account,
subAccounts: self.subAccounts,
isEdit: !!self.editAccountId
isEdit: !!self.editAccountId,
clientSessionId: self.clientSessionId
}).then(() => {
self.submitting = false;
@@ -101,6 +101,7 @@ import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
import categoryConstants from '@/consts/category.js';
import iconConstants from '@/consts/icon.js';
import colorConstants from '@/consts/color.js';
import { generateRandomUUID } from '@/lib/misc.js';
import {
setCategoryModelByAnotherCategory,
allVisiblePrimaryTransactionCategoriesByType
@@ -121,6 +122,7 @@ export default {
return {
showState: false,
editCategoryId: null,
clientSessionId: '',
loading: false,
category: newTransactionCategory,
submitting: false,
@@ -220,6 +222,7 @@ export default {
self.category.type = categoryType;
self.category.parentId = options.parentId;
self.clientSessionId = generateRandomUUID();
self.loading = false;
}
@@ -242,7 +245,8 @@ export default {
self.transactionCategoriesStore.saveCategory({
category: self.category,
isEdit: !!self.editCategoryId
isEdit: !!self.editCategoryId,
clientSessionId: self.clientSessionId
}).then(() => {
self.submitting = false;
@@ -338,6 +338,7 @@ import {
getTimezoneOffsetMinutes,
getCurrentUnixTime
} from '@/lib/datetime.js';
import { generateRandomUUID } from '@/lib/misc.js';
import {
getFirstAvailableCategoryId
} from '@/lib/category.js';
@@ -370,6 +371,7 @@ export default {
activeTab: 'basicInfo',
editTransactionId: null,
originalTransactionEditable: false,
clientSessionId: '',
loading: true,
transaction: newTransaction,
geoLocationStatus: null,
@@ -642,6 +644,10 @@ export default {
self.transaction.type = parseInt(options.type);
}
if (self.mode === 'add') {
self.clientSessionId = generateRandomUUID();
}
Promise.all(promises).then(function (responses) {
if (self.editTransactionId && !responses[3]) {
if (self.reject) {
@@ -691,7 +697,8 @@ export default {
self.transactionsStore.saveTransaction({
transaction: self.transaction,
defaultCurrency: self.defaultCurrency,
isEdit: self.mode === 'edit'
isEdit: self.mode === 'edit',
clientSessionId: self.clientSessionId
}).then(() => {
self.submitting = false;
+5 -1
View File
@@ -428,6 +428,7 @@ import iconConstants from '@/consts/icon.js';
import colorConstants from '@/consts/color.js';
import transactionConstants from '@/consts/transaction.js';
import { getNameByKeyValue } from '@/lib/common.js';
import { generateRandomUUID } from '@/lib/misc.js';
import {
setAccountModelByAnotherAccount,
setAccountSuitableIcon
@@ -447,6 +448,7 @@ export default {
return {
editAccountId: null,
clientSessionId: '',
loading: false,
loadingError: null,
account: newAccount,
@@ -547,6 +549,7 @@ export default {
}
});
} else {
self.clientSessionId = generateRandomUUID();
self.loading = false;
}
},
@@ -614,7 +617,8 @@ export default {
self.accountsStore.saveAccount({
account: self.account,
subAccounts: self.subAccounts,
isEdit: !!self.editAccountId
isEdit: !!self.editAccountId,
clientSessionId: self.clientSessionId
}).then(() => {
self.submitting = false;
self.$hideLoading();
+5 -1
View File
@@ -153,6 +153,7 @@ import categoryConstants from '@/consts/category.js';
import iconConstants from '@/consts/icon.js';
import colorConstants from '@/consts/color.js';
import { getNameByKeyValue } from '@/lib/common.js';
import { generateRandomUUID } from '@/lib/misc.js';
import {
setCategoryModelByAnotherCategory,
allVisiblePrimaryTransactionCategoriesByType
@@ -172,6 +173,7 @@ export default {
return {
editCategoryId: null,
clientSessionId: '',
loading: false,
loadingError: null,
category: newTransactionCategory,
@@ -257,6 +259,7 @@ export default {
return;
}
self.clientSessionId = generateRandomUUID();
self.loading = false;
}
},
@@ -280,7 +283,8 @@ export default {
self.transactionCategoriesStore.saveCategory({
category: self.category,
isEdit: !!self.editCategoryId
isEdit: !!self.editCategoryId,
clientSessionId: self.clientSessionId
}).then(() => {
self.submitting = false;
self.$hideLoading();
+8 -1
View File
@@ -367,6 +367,7 @@ import {
getUtcOffsetByUtcOffsetMinutes,
getActualUnixTimeForStore
} from '@/lib/datetime.js';
import { generateRandomUUID } from '@/lib/misc.js';
import {
getTransactionPrimaryCategoryName,
getTransactionSecondaryCategoryName,
@@ -389,6 +390,7 @@ export default {
mode: 'add',
editTransactionId: null,
transaction: newTransaction,
clientSessionId: '',
loading: true,
loadingError: null,
geoLocationStatus: null,
@@ -663,6 +665,10 @@ export default {
self.transaction.type = parseInt(query.type);
}
if (self.mode === 'add') {
self.clientSessionId = generateRandomUUID();
}
Promise.all(promises).then(function (responses) {
if (query.id && !responses[3]) {
self.$toast('Unable to retrieve transaction');
@@ -723,7 +729,8 @@ export default {
self.transactionsStore.saveTransaction({
transaction: self.transaction,
defaultCurrency: self.defaultCurrency,
isEdit: self.mode === 'edit'
isEdit: self.mode === 'edit',
clientSessionId: self.clientSessionId
}).then(() => {
self.submitting = false;
self.$hideLoading();