[Feature] Option not to delete linked transfer transaction #314

Closed
opened 2026-02-28 18:58:19 -06:00 by GiteaMirror · 21 comments
Owner

Originally created by @winklevos on GitHub (Mar 26, 2023).

Verified feature request does not already exist?

  • I have searched and found no existing issue

💻

  • Would you like to implement this feature?

Pitch: what problem are you trying to solve?

If you delete a transaction from account B which is a transfer which has been linked to another transaction in account A it will delete both the transactions.

This is problematic if you are correcting an account where it would be easier to delete transactions back to a date where it was reconciled and import the source from that date. However, this would break any accounts transferred from or to the account. Making it very painful to fix.

This is especially prevalent when you import multiple accounts which have many transfers between them.

If you don't mark transfers and link them they show as categorized and throw off budgets

Describe your ideal solution to this problem

Either scope delete to the account that the delete is originating from, or an option in settings to change the behavior.

Teaching and learning

No response

Originally created by @winklevos on GitHub (Mar 26, 2023). ### Verified feature request does not already exist? - [X] I have searched and found no existing issue ### 💻 - [x] Would you like to implement this feature? ### Pitch: what problem are you trying to solve? If you delete a transaction from account B which is a transfer which has been linked to another transaction in account A it will delete both the transactions. This is problematic if you are correcting an account where it would be easier to delete transactions back to a date where it was reconciled and import the source from that date. However, this would break any accounts transferred from or to the account. Making it very painful to fix. This is especially prevalent when you import multiple accounts which have many transfers between them. If you don't mark transfers and link them they show as categorized and throw off budgets ### Describe your ideal solution to this problem Either scope delete to the account that the delete is originating from, or an option in settings to change the behavior. ### Teaching and learning _No response_
GiteaMirror added the feature label 2026-02-28 18:58:20 -06:00
Author
Owner

@winklevos commented on GitHub (Mar 26, 2023):

We could have it only delete the related transaction if it has not yet been cleared. It seems that when a import relates to an existing transfer transaction it will update it to be cleared. By excluding those there is no data loss potential

In packages\loot-core\src\server\accounts\transfer.js

FROM

export async function removeTransfer(transaction) {
  let transferTrans = await db.getTransaction(transaction.transfer_id);
  if (transferTrans.is_child) {
    // If it's a child transaction, we don't delete it because that
    // would invalidate the whole split transaction. Instead of turn
    // it into a normal transaction
    await db.updateTransaction({
      id: transaction.transfer_id,
      transfer_id: null,
      payee: null
    });
  } else {
    await db.deleteTransaction({ id: transaction.transfer_id });
  }
  await db.updateTransaction({ id: transaction.id, transfer_id: null });
  return { id: transaction.id, transfer_id: null };
}

TO:

export async function removeTransfer(transaction) {
  let transferTrans = await db.getTransaction(transaction.transfer_id);  
  if (transferTrans.is_child) {
    // If it's a child transaction, we don't delete it because that
    // would invalidate the whole split transaction. Instead of turn
    // it into a normal transaction
    await db.updateTransaction({
      id: transferTrans.id,
      transfer_id: null,
      payee: null
    });
  } else if(transferTrans.cleared) {
    // do not delete related transaction if cleared just null the transfer_id and reset payee
    await db.updateTransaction({ 
      id: transferTrans.id, 
      transfer_id: null,  
      payee: transferTrans.imported_payee || null
    });  
  } else {
    await db.deleteTransaction({ id: transaction.transfer_id });
  }
  await db.updateTransaction({ id: transaction.id, transfer_id: null });
  return { id: transaction.id, transfer_id: null };
}
@winklevos commented on GitHub (Mar 26, 2023): We could have it only delete the related transaction if it has not yet been cleared. It seems that when a import relates to an existing transfer transaction it will update it to be cleared. By excluding those there is no data loss potential In [packages\loot-core\src\server\accounts\transfer.js](https://github.com/actualbudget/actual/blob/master/packages/loot-core/src/server/accounts/transfer.js#L91) FROM ``` javascript export async function removeTransfer(transaction) { let transferTrans = await db.getTransaction(transaction.transfer_id); if (transferTrans.is_child) { // If it's a child transaction, we don't delete it because that // would invalidate the whole split transaction. Instead of turn // it into a normal transaction await db.updateTransaction({ id: transaction.transfer_id, transfer_id: null, payee: null }); } else { await db.deleteTransaction({ id: transaction.transfer_id }); } await db.updateTransaction({ id: transaction.id, transfer_id: null }); return { id: transaction.id, transfer_id: null }; } ``` TO: ``` javascript export async function removeTransfer(transaction) { let transferTrans = await db.getTransaction(transaction.transfer_id); if (transferTrans.is_child) { // If it's a child transaction, we don't delete it because that // would invalidate the whole split transaction. Instead of turn // it into a normal transaction await db.updateTransaction({ id: transferTrans.id, transfer_id: null, payee: null }); } else if(transferTrans.cleared) { // do not delete related transaction if cleared just null the transfer_id and reset payee await db.updateTransaction({ id: transferTrans.id, transfer_id: null, payee: transferTrans.imported_payee || null }); } else { await db.deleteTransaction({ id: transaction.transfer_id }); } await db.updateTransaction({ id: transaction.id, transfer_id: null }); return { id: transaction.id, transfer_id: null }; } ```
Author
Owner

@MatissJanis commented on GitHub (Mar 26, 2023):

👋 Could you pitch the problem once more? The current problem definition reads more as a solution to problem X. But I don't understand what the problem X is. Sounds like it has something to do with imports?

If you delete a transaction from account B which is a transfer which has been linked to another transaction in account A it will delete both the transactions.

This is by-design and should not be tampered with. A transfer between accounts will always create 2x linked transactions. The alternative approach you can take is to manually create 2x single transactions (one per each account). But again: I don't yet understand the problem you're facing, so my suggestion might not work for you.

@MatissJanis commented on GitHub (Mar 26, 2023): 👋 Could you pitch **the problem** once more? The current problem definition reads more as a solution to problem X. But I don't understand what the problem X is. Sounds like it has something to do with imports? > If you delete a transaction from account B which is a transfer which has been linked to another transaction in account A it will delete both the transactions. This is by-design and should not be tampered with. A transfer between accounts will always create 2x linked transactions. The alternative approach you can take is to manually create 2x single transactions (one per each account). But again: I don't yet understand the problem you're facing, so my suggestion might not work for you.
Author
Owner

@winklevos commented on GitHub (Apr 8, 2023):

more detail in https://github.com/actualbudget/actual/pull/815 @MatissJanis

This is to ensure there is no data loss when you import multiple accounts and they have transfers between them.

@winklevos commented on GitHub (Apr 8, 2023): more detail in https://github.com/actualbudget/actual/pull/815 @MatissJanis This is to ensure there is no data loss when you import multiple accounts and they have transfers between them.
Author
Owner

@winklevos commented on GitHub (Apr 8, 2023):

This is by-design and should not be tampered with.

The design is flawed. A transfer between two accounts, is two transactions. They're not the same data point. As such you should be able to delete one without deleting the other.

I understand the intended purpose of this current functionality with Actual heavily geared towards people manually entering transactions. However, if Actual it to support any sort of automation or bulk processes this functionality needs refining.

If you don't have imported transfers marked as transfers by a rule then they will require categorization which is incorrect. So Actual by design forces you to have this linked to have accurate budgeting. So the

The alternative approach you can take is to manually create 2x single transactions

Isn't a valid alternative

My solution in #815 seeks to resolve this issue

@winklevos commented on GitHub (Apr 8, 2023): > This is by-design and should not be tampered with. The design is flawed. A transfer between two accounts, is two transactions. They're not the same data point. As such you should be able to delete one without deleting the other. I understand the intended purpose of this current functionality with Actual heavily geared towards people manually entering transactions. However, if Actual it to support any sort of automation or bulk processes this functionality needs refining. If you don't have imported transfers marked as transfers by a rule then they will require categorization which is incorrect. So Actual by design forces you to have this linked to have accurate budgeting. So the > The alternative approach you can take is to manually create 2x single transactions Isn't a valid alternative My solution in #815 seeks to resolve this issue
Author
Owner

@MatissJanis commented on GitHub (Apr 8, 2023):

Sorry to ask again, but could you spell out the problem once more? I still don't understand the problem you're facing. Maybe provide an example import file as an illustration.

@MatissJanis commented on GitHub (Apr 8, 2023): Sorry to ask again, but could you spell out **the problem** once more? I still don't understand the problem you're facing. Maybe provide an example import file as an illustration.
Author
Owner

@winklevos commented on GitHub (Apr 10, 2023):

Consider these examples, where we have a transfer from one account to another.

Account A001

Date Description Amount
2023-01-01 Transfer to A002 -1.00

Account A002

Date Description Amount
2023-01-02 Transfer from A001 1.00

Importing these into Actual without any additional configuration would have these transfers as "uncategorized". So to have your budget correct you would either have to leave the uncategorized or mark them as a transfer. So creating a rule for these descriptions to automatically mark them as transfers going forward, which is what we want.

However, these are two transactions linked, not one transaction. So the behavior on deletion of one of these is where problems begin.

If you delete the transaction from A002, the transaction in A001 will also be deleted. Which then throws off the balance of that account.

Likewise as the data columns are synced between them you may lose the original data from one of the transactions, like the actual transaction date. https://github.com/actualbudget/actual/issues/661.

@winklevos commented on GitHub (Apr 10, 2023): Consider these examples, where we have a transfer from one account to another. **Account A001** | Date | Description | Amount | |--------|--------|--------| | 2023-01-01 | Transfer to A002 | -1.00 | **Account A002** | Date | Description | Amount | |--------|--------|--------| | 2023-01-02 | Transfer from A001 | 1.00 | Importing these into Actual without any additional configuration would have these transfers as "uncategorized". So to have your budget correct you would either have to leave the uncategorized or mark them as a transfer. So creating a rule for these descriptions to automatically mark them as transfers going forward, which is what we want. However, these are two transactions linked, not one transaction. So the behavior on deletion of one of these is where problems begin. If you delete the transaction from A002, the transaction in A001 will also be deleted. Which then throws off the balance of that account. Likewise as the data columns are synced between them you may lose the original data from one of the transactions, like the actual transaction date. https://github.com/actualbudget/actual/issues/661.
Author
Owner

@MatissJanis commented on GitHub (Apr 10, 2023):

So the behavior on deletion of one of these is where problems begin.

Why are you deleting a transfer transaction after importing? What exactly are you trying to accomplish?

Did you consider removing the rule that creates the transfers from your imports? If the transactions are not transfers - you can delete them as you want and it will not affect the other accounts.

Likewise as the data columns are synced between them you may lose the original data from one of the transactions, like the actual transaction date. https://github.com/actualbudget/actual/issues/661.

Yes, this is an issue that we can (and should) address. I.e. - the date getting overridden. But the deletion behaviour would still remain unchanged with the fix.

@MatissJanis commented on GitHub (Apr 10, 2023): > So the behavior on deletion of one of these is where problems begin. Why are you deleting a transfer transaction after importing? What exactly are you trying to accomplish? Did you consider removing the rule that creates the transfers from your imports? If the transactions are not transfers - you can delete them as you want and it will not affect the other accounts. > Likewise as the data columns are synced between them you may lose the original data from one of the transactions, like the actual transaction date. https://github.com/actualbudget/actual/issues/661. Yes, this is an issue that we can (and should) address. I.e. - the date getting overridden. But the deletion behaviour would still remain unchanged with the fix.
Author
Owner

@winklevos commented on GitHub (Apr 10, 2023):

Why are you deleting a transfer transaction after importing? What exactly are you trying to accomplish?

"This is problematic if you are correcting an account where it would be easier to delete transactions back to a date where it was reconciled and import the source from that date. However, this would break any accounts transferred from or to the account. Making it very painful to fix."

My PR fixes this behaviour. It will not delete the transaction in the other account if it is cleared.

@winklevos commented on GitHub (Apr 10, 2023): > Why are you deleting a transfer transaction after importing? What exactly are you trying to accomplish? "This is problematic if you are correcting an account where it would be easier to delete transactions back to a date where it was reconciled and import the source from that date. However, this would break any accounts transferred from or to the account. Making it very painful to fix." My PR fixes this behaviour. It will not delete the transaction in the other account if it is cleared.
Author
Owner

@MatissJanis commented on GitHub (Apr 10, 2023):

Did you consider removing the rule that creates the transfers from your imports? If the transactions are not transfers - you can delete them as you want and it will not affect the other accounts.

@MatissJanis commented on GitHub (Apr 10, 2023): Did you consider removing the rule that creates the transfers from your imports? If the transactions are not transfers - you can delete them as you want and it will not affect the other accounts.
Author
Owner

@winklevos commented on GitHub (Apr 10, 2023):

Then they will always are always uncategorized. Unless I create a category for transfers that defeats the purpose.

@winklevos commented on GitHub (Apr 10, 2023): Then they will always are always uncategorized. Unless I create a category for transfers that defeats the purpose.
Author
Owner

@MatissJanis commented on GitHub (Apr 10, 2023):

Right, but that would solve your problem. You would have 2x un-linked transactions and you could delete/modify them as you pleased. Changes in one account would not affect the other.


I see why you want to delete the transfer transaction in one account, but keep it in the other, but this opens up a whole world of additional problems. For example: what exactly happens if you delete only one side of the transfer? Where does that money go? It won't appear in the budget. But it will be deducted from the account total. So it sort of just "disappeared". Is it even still a "transfer" if it only has one side of the transaction? How does that reflect in reports?

I don't expect to get answers to these questions btw. They are rhetorical in order to give you a sense of the complexity of doing the proposed change.

And this is why I am against doing the proposed change. Money should not just "disappear". There should be a precise log of transactions of how money moves in to the budget. Later on from account to account. And how it flows out of the account.

@MatissJanis commented on GitHub (Apr 10, 2023): Right, but that would solve your problem. You would have 2x un-linked transactions and you could delete/modify them as you pleased. Changes in one account would not affect the other. --- I see why you want to delete the transfer transaction in one account, but keep it in the other, but this opens up a whole world of additional problems. For example: what exactly happens if you delete only one side of the transfer? Where does that money go? It won't appear in the budget. But it will be deducted from the account total. So it sort of just "disappeared". Is it even still a "transfer" if it only has one side of the transaction? How does that reflect in reports? I don't expect to get answers to these questions btw. They are rhetorical in order to give you a sense of the complexity of doing the proposed change. And this is why I am against doing the proposed change. Money should not just "disappear". There should be a precise log of transactions of how money moves in to the budget. Later on from account to account. And how it flows out of the account.
Author
Owner

@winklevos commented on GitHub (Apr 10, 2023):

I'm sorry. I disagree that there is complexity here.

That account you have actioned would be incorrect and simply that account would need to be corrected. Unless the linked transaction isn't cleared then it would be deleted.

Right now you would completely invalidate the balance of two accounts if you delete it. Where as this would reduce the scope.

Regardless of the solution, deleting a transaction means money goes into nowhere...

@winklevos commented on GitHub (Apr 10, 2023): I'm sorry. I disagree that there is complexity here. That account you have actioned would be incorrect and simply that account would need to be corrected. Unless the linked transaction isn't cleared then it would be deleted. Right now you would completely invalidate the balance of two accounts if you delete it. Where as this would reduce the scope. Regardless of the solution, deleting a transaction means money goes into nowhere...
Author
Owner

@Jackenmen commented on GitHub (Apr 10, 2023):

As I understand the problem, winklevos wants to delete transactions (some of which are transfers) from one account and later re-add them by importing transactions starting from some date. So presumably, that would mean that after the deletion, the transfer on the other side would become a regular transaction, counting against the budget (this is the only sensible solution, as the money obviously can't disappear). However, after importing, they would most definitely need a way to (manually, I imagine) transform two transactions on different accounts into a transfer again. The latter part does seem like a rather complex problem to resolve, especially in a user-friendly way.

I do have an alternative suggestion that, based on my understanding of the problem, would still resolve it, just in a bit different way. How about we extend the transactions filter functionality to support filtering based on whether transactions are transfers? This way, @winklevos could filter their transactions with a new "is not transfer" option and be able to easily select all other transactions, delete them, and then import transactions again. This way, the transfers would just not get removed making the feature proposed here unnecessary, and, after the import, those transfers would just get reconciled appropriately.

@Jackenmen commented on GitHub (Apr 10, 2023): As I understand the problem, winklevos wants to delete transactions (some of which are transfers) from one account and later re-add them by importing transactions starting from some date. So presumably, that would mean that after the deletion, the transfer on the other side would become a regular transaction, counting against the budget (this is the only sensible solution, as the money obviously can't disappear). *However*, after importing, they would most definitely need a way to (manually, I imagine) transform two transactions on different accounts into a transfer again. The latter part does seem like a rather complex problem to resolve, especially in a user-friendly way. I do have an alternative suggestion that, based on my understanding of the problem, would still resolve it, just in a bit different way. How about we extend the transactions filter functionality to support filtering based on whether transactions are transfers? This way, @winklevos could filter their transactions with a new "is not transfer" option and be able to easily select all other transactions, delete them, and then import transactions again. This way, the transfers would just not get removed making the feature proposed here unnecessary, and, after the import, those transfers would just get reconciled appropriately.
Author
Owner

@winklevos commented on GitHub (Apr 10, 2023):

However, after importing, they would most definitely need a way to (manually, I imagine) transform two transactions on different accounts into a transfer again. This does seem like a rather complex problem to resolve in a user-friendly way.

This is covered by the rule processing. It would automatically match a transaction in the target account.

As for the other suggestion, that would mean you would need to filter out the transfers out of the import source as not to duplicate, which sure I could do. But isn't the idea to reduce the amount of stuff someone needs to do

@winklevos commented on GitHub (Apr 10, 2023): > However, after importing, they would most definitely need a way to (manually, I imagine) transform two transactions on different accounts into a transfer again. This does seem like a rather complex problem to resolve in a user-friendly way. This is covered by the rule processing. It would automatically match a transaction in the target account. As for the other suggestion, that would mean you would need to filter out the transfers out of the import source as not to duplicate, which sure I could do. But isn't the idea to reduce the amount of stuff someone needs to do
Author
Owner

@Jackenmen commented on GitHub (Apr 10, 2023):

This is covered by the rule processing. It would automatically match a transaction in the target account.

It wouldn't be automatically merged on the other side during the import though, would it? It would end up in another transaction being created since the one on the other side wouldn't have a payee (since the account-linked payees are limited to transfers and that transaction would no longer be a transfer and as such, would have to have the payee set to (none)).

As for the other suggestion, that would mean you would need to filter out the transfers out of the import source as not to duplicate, which sure I could do. But isn't the idea to reduce the amount of stuff someone needs to do

Transactions are matched during the import (indicated by the 🔗 icon and Actual marking them as Checked automatically) so there would not be a need to filter it out from the imported file beforehand as long as the matching works. And if it doesn't (I'm not sure there would be a reason for it not to since transactions are matched by their amount and date but if), you'll probably have better luck removing the duplicated transactions after the import since they would show up as not "Checked" on the other side of the transfer and would be easy to filter out.

@Jackenmen commented on GitHub (Apr 10, 2023): > This is covered by the rule processing. It would automatically match a transaction in the target account. It wouldn't be automatically merged on the other side during the import though, would it? It would end up in another transaction being created since the one on the other side wouldn't have a payee (since the account-linked payees are limited to transfers and that transaction would no longer be a transfer and as such, would have to have the payee set to `(none)`). > As for the other suggestion, that would mean you would need to filter out the transfers out of the import source as not to duplicate, which sure I could do. But isn't the idea to reduce the amount of stuff someone needs to do Transactions are matched during the import (indicated by the :link: icon and Actual marking them as Checked automatically) so there would not be a need to filter it out from the imported file beforehand as long as the matching works. And if it doesn't (I'm not sure there would be a reason for it not to since transactions are matched by their amount and date *but if*), you'll probably have better luck removing the duplicated transactions after the import since they would show up as not "Checked" on the other side of the transfer and would be easy to filter out.
Author
Owner

@winklevos commented on GitHub (Apr 10, 2023):

It wouldn't be automatically merged on the other side during the import though, would it?

I'm pretty sure it will match on the other account like it does on import. I'll test again but I think that's how it behaved in my dev branch

As the logic updates the payee on both to be the relative account id

@winklevos commented on GitHub (Apr 10, 2023): > It wouldn't be automatically merged on the other side during the import though, would it? I'm pretty sure it will match on the other account like it does on import. I'll test again but I think that's how it behaved in my dev branch As the logic updates the payee on both to be the relative account id
Author
Owner

@Jackenmen commented on GitHub (Apr 10, 2023):

I'll test again but I think that's how it behaved in my dev branch

I haven't looked at your PR but do you actually clear the payee after transforming a transfer into a regular transaction? And I guess I should also ask whether you even transform it into a regular transaction.

@Jackenmen commented on GitHub (Apr 10, 2023): > I'll test again but I think that's how it behaved in my dev branch I haven't looked at your PR but do you actually clear the payee after transforming a transfer into a regular transaction? And I guess I should also ask whether you even transform it into a regular transaction.
Author
Owner

@winklevos commented on GitHub (Apr 10, 2023):

Yeah I change the payee back to the imported payee if it has one

@winklevos commented on GitHub (Apr 10, 2023): Yeah I change the payee back to the imported payee if it has one
Author
Owner

@shall0pass commented on GitHub (Apr 10, 2023):

I've been quietly following along. I'm not going to argue for or against unless it totally breaks things, but I am a bit confused about what's going on here. Interestingly enough, it doesn't affect the "To Budget" amount and only breaks the account balances.

deleteTransfers

@shall0pass commented on GitHub (Apr 10, 2023): I've been quietly following along. I'm not going to argue for or against unless it totally breaks things, but I am a bit confused about what's going on here. Interestingly enough, it doesn't affect the "To Budget" amount and only breaks the account balances. ![deleteTransfers](https://user-images.githubusercontent.com/20625555/230920762-ed53281d-f231-45ab-8e15-6dc779d5101b.gif)
Author
Owner

@MatissJanis commented on GitHub (Apr 19, 2023):

👋 I'll close off this specific feature request as not-planed. Money in a budget should never "disappear", so one-sided transfers are not a feature we plan on supporting.

However, the real problem that was outlined can be solved by other means (see discussions above). What's more: this feature request might also solve the original problem in a more sensible way.

@MatissJanis commented on GitHub (Apr 19, 2023): 👋 I'll close off this specific feature request as not-planed. Money in a budget should never "disappear", so one-sided transfers are not a feature we plan on supporting. However, the _real_ problem that was outlined can be solved by other means (see discussions above). What's more: [this feature request](https://github.com/actualbudget/actual/issues/904) might also solve the original problem in a more sensible way.
Author
Owner

@winklevos commented on GitHub (Apr 19, 2023):

That's very disappointing. And it's not the first time "budget" has been in the way of better user experience for this app.

@winklevos commented on GitHub (Apr 19, 2023): That's very disappointing. And it's not the first time "budget" has been in the way of better user experience for this app.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/actual#314