From 9fcd93e350e46b891eeeca9a201c2d32f3405ea7 Mon Sep 17 00:00:00 2001 From: Stefano Tranquillini Date: Fri, 8 Nov 2024 18:04:34 +0100 Subject: [PATCH 01/11] Adding functionality to trigger the rules of transaction from the transaction view Signed-off-by: Stefano Tranquillini --- .../src/components/accounts/Account.tsx | 26 +++++++++++++++++++ .../src/components/accounts/Header.tsx | 3 +++ .../SelectedTransactionsButton.tsx | 18 ++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index 37936e10c7d..f31bc2c7344 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -44,6 +44,7 @@ import { type RuleConditionEntity, type TransactionEntity, type TransactionFilterEntity, + RuleEntity, } from 'loot-core/src/types/models'; import { useAccounts } from '../../hooks/useAccounts'; @@ -705,6 +706,30 @@ class AccountInternal extends PureComponent< return groupById<{ id: string; balance: number }>(data); } + onRunRules = async (ids: string[]) => { + // this should be probably inside the onEdit function that the others button are calling + // but I'm not sure how to do it and if i'll break anything + let transactions = this.state.transactions; + for (const transId of ids) { + let trans = transactions.find(({ id }) => transId === id); + const the_rules = send('payees-get-rules', { + id: trans.payee, + }).then((the_rules: RuleEntity[]) => { + the_rules.forEach((rule: RuleEntity) => { + send('rule-apply-actions', { + transactions: [trans], + actions: rule.actions, + }).then(() => { + // once rule is applied, fetch the transaction again. + // should just update the one that has been updated, but not sure how to do it. + // probably via the updateTransaction function ? + this.fetchTransactions(); + }); + }); + }); + } + + }; onAddTransaction = () => { this.setState({ isAdding: true }); }; @@ -1736,6 +1761,7 @@ class AccountInternal extends PureComponent< onImport={this.onImport} onBatchDelete={this.onBatchDelete} onBatchDuplicate={this.onBatchDuplicate} + onRunRules={this.onRunRules} onBatchEdit={this.onBatchEdit} onBatchLinkSchedule={this.onBatchLinkSchedule} onBatchUnlinkSchedule={this.onBatchUnlinkSchedule} diff --git a/packages/desktop-client/src/components/accounts/Header.tsx b/packages/desktop-client/src/components/accounts/Header.tsx index c45ae73eb1c..9d58731d1ae 100644 --- a/packages/desktop-client/src/components/accounts/Header.tsx +++ b/packages/desktop-client/src/components/accounts/Header.tsx @@ -95,6 +95,7 @@ type AccountHeaderProps = { onMenuSelect: AccountMenuProps['onMenuSelect']; onReconcile: ComponentProps['onReconcile']; onBatchEdit: ComponentProps['onEdit']; + onRunRules: ComponentProps['onRunRules']; onBatchDelete: ComponentProps['onDelete']; onBatchDuplicate: ComponentProps< typeof SelectedTransactionsButton @@ -177,6 +178,7 @@ export function AccountHeader({ onDeleteFilter, onScheduleAction, onSetTransfer, + onRunRules, onMakeAsSplitTransaction, onMakeAsNonSplitTransactions, }: AccountHeaderProps) { @@ -359,6 +361,7 @@ export function AccountHeader({ onDuplicate={onBatchDuplicate} onDelete={onBatchDelete} onEdit={onBatchEdit} + onRunRules={onRunRules} onLinkSchedule={onBatchLinkSchedule} onUnlinkSchedule={onBatchUnlinkSchedule} onCreateRule={onCreateRule} diff --git a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx index fa1c85836ee..bf27d45ed36 100644 --- a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx +++ b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx @@ -6,11 +6,12 @@ import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { isPreviewId } from 'loot-core/shared/transactions'; import { validForTransfer } from 'loot-core/src/client/transfer'; -import { type TransactionEntity } from 'loot-core/types/models'; +import { RuleEntity, type TransactionEntity } from 'loot-core/types/models'; import { useSelectedItems } from '../../hooks/useSelected'; import { Menu } from '../common/Menu'; import { SelectedItemsButton } from '../table'; +import { send } from 'loot-core/src/platform/client/fetch'; type SelectedTransactionsButtonProps = { getTransaction: (id: string) => TransactionEntity | undefined; @@ -31,6 +32,7 @@ type SelectedTransactionsButtonProps = { onLinkSchedule: (selectedIds: string[]) => void; onUnlinkSchedule: (selectedIds: string[]) => void; onCreateRule: (selectedIds: string[]) => void; + onRunRules: (selectedIds: string[]) => void; onSetTransfer: (selectedIds: string[]) => void; onScheduleAction: ( action: 'post-transaction' | 'skip', @@ -50,6 +52,7 @@ export function SelectedTransactionsButton({ onLinkSchedule, onUnlinkSchedule, onCreateRule, + onRunRules, onSetTransfer, onScheduleAction, showMakeTransfer, @@ -193,6 +196,10 @@ export function SelectedTransactionsButton({ onEdit, selectedIds, ]); + useHotkeys('r', () => onRunRules(selectedIds), hotKeyOptions, [ + onRunRules, + selectedIds, + ]); useHotkeys( 's', () => @@ -253,7 +260,13 @@ export function SelectedTransactionsButton({ name: 'create-rule', text: t('Create rule'), } as const, + { + name: 'run-rules', + text: t('Run Rules'), + key: 'R', + } as const, ]), + ...(showMakeTransfer ? [ { @@ -325,6 +338,9 @@ export function SelectedTransactionsButton({ case 'create-rule': onCreateRule(selectedIds); break; + case 'run-rules': + onRunRules(selectedIds); + break; case 'set-transfer': onSetTransfer(selectedIds); break; From f857d26e478869e8e4671679f5125f522896644a Mon Sep 17 00:00:00 2001 From: Stefano Tranquillini Date: Fri, 8 Nov 2024 18:05:48 +0100 Subject: [PATCH 02/11] fix warnings Signed-off-by: Stefano Tranquillini --- packages/desktop-client/src/components/accounts/Account.tsx | 2 +- .../src/components/transactions/SelectedTransactionsButton.tsx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index f31bc2c7344..0c7037b0418 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -712,7 +712,7 @@ class AccountInternal extends PureComponent< let transactions = this.state.transactions; for (const transId of ids) { let trans = transactions.find(({ id }) => transId === id); - const the_rules = send('payees-get-rules', { + send('payees-get-rules', { id: trans.payee, }).then((the_rules: RuleEntity[]) => { the_rules.forEach((rule: RuleEntity) => { diff --git a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx index bf27d45ed36..6ff7985af9c 100644 --- a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx +++ b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx @@ -6,12 +6,11 @@ import { useDispatch } from 'react-redux'; import { pushModal } from 'loot-core/client/actions'; import { isPreviewId } from 'loot-core/shared/transactions'; import { validForTransfer } from 'loot-core/src/client/transfer'; -import { RuleEntity, type TransactionEntity } from 'loot-core/types/models'; +import { type TransactionEntity } from 'loot-core/types/models'; import { useSelectedItems } from '../../hooks/useSelected'; import { Menu } from '../common/Menu'; import { SelectedItemsButton } from '../table'; -import { send } from 'loot-core/src/platform/client/fetch'; type SelectedTransactionsButtonProps = { getTransaction: (id: string) => TransactionEntity | undefined; From a27e6d02de885f8edaa1c98790847e98108ec49a Mon Sep 17 00:00:00 2001 From: Stefano Tranquillini <1928354+esseti@users.noreply.github.com> Date: Fri, 8 Nov 2024 18:30:08 +0100 Subject: [PATCH 03/11] Fixing errors on the checks: adding changelog and lint Signed-off-by: Stefano Tranquillini <1928354+esseti@users.noreply.github.com> --- .../desktop-client/src/components/accounts/Account.tsx | 7 +++---- upcoming-release-notes/3805.md | 6 ++++++ 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 upcoming-release-notes/3805.md diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index 0c7037b0418..a13b317b14d 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -44,7 +44,7 @@ import { type RuleConditionEntity, type TransactionEntity, type TransactionFilterEntity, - RuleEntity, + type RuleEntity, } from 'loot-core/src/types/models'; import { useAccounts } from '../../hooks/useAccounts'; @@ -709,9 +709,9 @@ class AccountInternal extends PureComponent< onRunRules = async (ids: string[]) => { // this should be probably inside the onEdit function that the others button are calling // but I'm not sure how to do it and if i'll break anything - let transactions = this.state.transactions; + const transactions = this.state.transactions; for (const transId of ids) { - let trans = transactions.find(({ id }) => transId === id); + const trans = transactions.find(({ id }) => transId === id); send('payees-get-rules', { id: trans.payee, }).then((the_rules: RuleEntity[]) => { @@ -728,7 +728,6 @@ class AccountInternal extends PureComponent< }); }); } - }; onAddTransaction = () => { this.setState({ isAdding: true }); diff --git a/upcoming-release-notes/3805.md b/upcoming-release-notes/3805.md new file mode 100644 index 00000000000..1355b1f1a98 --- /dev/null +++ b/upcoming-release-notes/3805.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [esseti] +--- + +Enables rule activation from the account view via dropdown menu or by pressing 'R' From a7861a8cbfefbd6c9d583c8f69b78821cb39ba76 Mon Sep 17 00:00:00 2001 From: Stefano Tranquillini <1928354+esseti@users.noreply.github.com> Date: Sun, 10 Nov 2024 13:57:53 +0100 Subject: [PATCH 04/11] Applying suggestion from the bot. Signed-off-by: Stefano Tranquillini <1928354+esseti@users.noreply.github.com> --- .../src/components/accounts/Account.tsx | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index a13b317b14d..4b12caeed40 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -707,28 +707,41 @@ class AccountInternal extends PureComponent< } onRunRules = async (ids: string[]) => { - // this should be probably inside the onEdit function that the others button are calling - // but I'm not sure how to do it and if i'll break anything - const transactions = this.state.transactions; - for (const transId of ids) { - const trans = transactions.find(({ id }) => transId === id); - send('payees-get-rules', { - id: trans.payee, - }).then((the_rules: RuleEntity[]) => { - the_rules.forEach((rule: RuleEntity) => { - send('rule-apply-actions', { + try { + this.setState({ workingHard: true }); + // Bulk fetch transactions + const transactions = this.state.transactions.filter(trans => + ids.includes(trans.id), + ); + // Process transactions sequentially to maintain order + for (const trans of transactions) { + if (!trans.payee) continue; + // Fetch rules for the transaction's payee + const rules = (await send('payees-get-rules', { + id: trans.payee, + })) as RuleEntity[]; + + // Apply all rules for this transaction + for (const rule of rules) { + await send('rule-apply-actions', { transactions: [trans], actions: rule.actions, - }).then(() => { - // once rule is applied, fetch the transaction again. - // should just update the one that has been updated, but not sure how to do it. - // probably via the updateTransaction function ? - this.fetchTransactions(); }); - }); + } + } + // Fetch updated transactions once at the end + await this.fetchTransactions(); + } catch (error) { + console.error('Error applying rules:', error); + this.props.addNotification({ + type: 'error', + message: 'Failed to apply rules to transactions', }); + } finally { + this.setState({ workingHard: false }); } }; + onAddTransaction = () => { this.setState({ isAdding: true }); }; From 01af7fe8036285c1905a8a4c15c5256a11e21cd1 Mon Sep 17 00:00:00 2001 From: Stefano Tranquillini <1928354+esseti@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:57:26 +0100 Subject: [PATCH 05/11] =?UTF-8?q?=E2=9C=A8=20Enhance=20transaction=20proce?= =?UTF-8?q?ssing=20in=20Account=20component=20by=20implementing=20rules=20?= =?UTF-8?q?execution=20and=20batch=20updates.=20Added=20utility=20function?= =?UTF-8?q?=20imports=20for=20improved=20functionality.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/accounts/Account.tsx | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index f82c63cb417..0ba40083067 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -39,7 +39,11 @@ import { makeChild, makeAsNonChildTransactions, } from 'loot-core/src/shared/transactions'; -import { applyChanges, groupById } from 'loot-core/src/shared/util'; +import { + applyChanges, + getChangedValues, + groupById, +} from 'loot-core/src/shared/util'; import { type NewRuleEntity, type RuleActionEntity, @@ -50,7 +54,6 @@ import { type TransactionFilterEntity, type RuleEntity, } from 'loot-core/src/types/models'; - import { useAccountPreviewTransactions } from '../../hooks/useAccountPreviewTransactions'; import { useAccounts } from '../../hooks/useAccounts'; import { useActions } from '../../hooks/useActions'; @@ -710,22 +713,31 @@ class AccountInternal extends PureComponent< const transactions = this.state.transactions.filter(trans => ids.includes(trans.id), ); - // Process transactions sequentially to maintain order - for (const trans of transactions) { - if (!trans.payee) continue; - // Fetch rules for the transaction's payee - const rules = (await send('payees-get-rules', { - id: trans.payee, - })) as RuleEntity[]; - - // Apply all rules for this transaction - for (const rule of rules) { - await send('rule-apply-actions', { - transactions: [trans], - actions: rule.actions, - }); - } + console.log('transactions', transactions); + //call the runrules function + let changed_transactions = []; + for (const transaction of transactions) { + await send('rules-run', { + transaction: transaction, + }).then((res: any) => { + if (res) { + changed_transactions.push(res); + } + console.log( + 'rules-run response for transaction', + transaction.id, + res, + ); + }); } + + // If we have changed transactions, update them in the database + if (changed_transactions.length > 0) { + await send('transactions-batch-update', { + updated: changed_transactions, + }); + } + // Fetch updated transactions once at the end await this.fetchTransactions(); } catch (error) { From 5c520d192ae09020507a1a4a19b92fdfa1d5fe27 Mon Sep 17 00:00:00 2001 From: Stefano <1928354+esseti@users.noreply.github.com> Date: Fri, 20 Dec 2024 12:09:20 +0100 Subject: [PATCH 06/11] Update packages/desktop-client/src/components/accounts/Account.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../src/components/accounts/Account.tsx | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index 0ba40083067..296b5a87308 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -713,28 +713,22 @@ class AccountInternal extends PureComponent< const transactions = this.state.transactions.filter(trans => ids.includes(trans.id), ); - console.log('transactions', transactions); //call the runrules function - let changed_transactions = []; + const changedTransactions = []; for (const transaction of transactions) { await send('rules-run', { - transaction: transaction, - }).then((res: any) => { + transaction, + }).then((res: TransactionEntity | null) => { if (res) { - changed_transactions.push(res); + changedTransactions.push(res); } - console.log( - 'rules-run response for transaction', - transaction.id, - res, - ); }); } // If we have changed transactions, update them in the database - if (changed_transactions.length > 0) { + if (changedTransactions.length > 0) { await send('transactions-batch-update', { - updated: changed_transactions, + updated: changedTransactions, }); } From b201f3c72cec506d0a594507e2704147394c7726 Mon Sep 17 00:00:00 2001 From: Stefano Tranquillini <1928354+esseti@users.noreply.github.com> Date: Fri, 20 Dec 2024 12:27:48 +0100 Subject: [PATCH 07/11] Refactor Account component imports by removing unused utility functions for cleaner code. --- .../desktop-client/src/components/accounts/Account.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index 296b5a87308..26d2dc22d16 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -39,11 +39,7 @@ import { makeChild, makeAsNonChildTransactions, } from 'loot-core/src/shared/transactions'; -import { - applyChanges, - getChangedValues, - groupById, -} from 'loot-core/src/shared/util'; +import { applyChanges, groupById } from 'loot-core/src/shared/util'; import { type NewRuleEntity, type RuleActionEntity, @@ -52,8 +48,8 @@ import { type RuleConditionEntity, type TransactionEntity, type TransactionFilterEntity, - type RuleEntity, } from 'loot-core/src/types/models'; + import { useAccountPreviewTransactions } from '../../hooks/useAccountPreviewTransactions'; import { useAccounts } from '../../hooks/useAccounts'; import { useActions } from '../../hooks/useActions'; From f66f64f87d0bcccf61167e5be5993136836e07c7 Mon Sep 17 00:00:00 2001 From: Stefano <1928354+esseti@users.noreply.github.com> Date: Sat, 28 Dec 2024 23:27:07 +0100 Subject: [PATCH 08/11] Update packages/desktop-client/src/components/accounts/Account.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../src/components/accounts/Account.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index d6c3a290c0e..674216e7325 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -709,6 +709,19 @@ class AccountInternal extends PureComponent< const transactions = this.state.transactions.filter(trans => ids.includes(trans.id), ); + const changedTransactions = []; + for (const transaction of transactions) { + const res: TransactionEntity | null = await send('rules-run', { + transaction, + }); + if (res) { + changedTransactions.push(res); + } + } + // Bulk fetch transactions + const transactions = this.state.transactions.filter(trans => + ids.includes(trans.id), + ); //call the runrules function const changedTransactions = []; for (const transaction of transactions) { From a627297b691ec0c3294056dd9e4709b8d37ac91c Mon Sep 17 00:00:00 2001 From: UnderKoen Date: Tue, 7 Jan 2025 21:42:03 +0100 Subject: [PATCH 09/11] chore: correct coderabbitai --- .../src/components/accounts/Account.tsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index 674216e7325..2574bd45f54 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -718,21 +718,6 @@ class AccountInternal extends PureComponent< changedTransactions.push(res); } } - // Bulk fetch transactions - const transactions = this.state.transactions.filter(trans => - ids.includes(trans.id), - ); - //call the runrules function - const changedTransactions = []; - for (const transaction of transactions) { - await send('rules-run', { - transaction, - }).then((res: TransactionEntity | null) => { - if (res) { - changedTransactions.push(res); - } - }); - } // If we have changed transactions, update them in the database if (changedTransactions.length > 0) { @@ -742,7 +727,7 @@ class AccountInternal extends PureComponent< } // Fetch updated transactions once at the end - await this.fetchTransactions(); + this.fetchTransactions(); } catch (error) { console.error('Error applying rules:', error); this.props.addNotification({ From 61a3564013a3f84c45edd45e64e8cd91cbd142ad Mon Sep 17 00:00:00 2001 From: Stefano Tranquillini <1928354+esseti@users.noreply.github.com> Date: Fri, 10 Jan 2025 16:37:17 +0100 Subject: [PATCH 10/11] Removed hotkey --- .../src/components/transactions/SelectedTransactionsButton.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx index 6ff7985af9c..d92ab64895b 100644 --- a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx +++ b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx @@ -262,7 +262,6 @@ export function SelectedTransactionsButton({ { name: 'run-rules', text: t('Run Rules'), - key: 'R', } as const, ]), From b4b789ae9899eb52d7b086e462eab79269edcda1 Mon Sep 17 00:00:00 2001 From: Koen van Staveren Date: Sun, 12 Jan 2025 15:27:55 +0100 Subject: [PATCH 11/11] Update packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx --- .../components/transactions/SelectedTransactionsButton.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx index 109a15a7c2a..77a6966b17c 100644 --- a/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx +++ b/packages/desktop-client/src/components/transactions/SelectedTransactionsButton.tsx @@ -195,10 +195,6 @@ export function SelectedTransactionsButton({ onEdit, selectedIds, ]); - useHotkeys('r', () => onRunRules(selectedIds), hotKeyOptions, [ - onRunRules, - selectedIds, - ]); useHotkeys( 's', () =>