sdementen / piecash

Pythonic interface to GnuCash SQL documents
Other
290 stars 73 forks source link

How do you delete an account from a GnuCash book using piecash? #151

Closed bxbrenden closed 3 years ago

bxbrenden commented 3 years ago

TL:DR;

I cannot figure out how to delete an Account object from my GnuCash Book using piecash, and there is no documentation I can find to help me.

Explanation of Issue

I've been using piecash to manage my GnuCash book, and I've run into an issue: I manually deleted some accounts from the desktop GnuCash app and saved my file, and now piecash shows several accounts in book.accounts that just have GUIDs for account.fullname. I believe those accounts are just my deleted ones lingering around. I can only see those accounts in piecash, but not in the real GnuCash desktop app. I would like to know: how can I delete those using piecash?

The phantom accounts mentioned above also do not show [USD] as their commodity. They show [template]. For example, here's what one of them looks like in the book.accounts output:

Account<3be715505def415bb3f6beea189aefa7[template]>

And its type is:

piecash.core.account.Account

I was unable to find anything in the piecash documentation about deleting accounts from a Book object. I ran help(piecash.core.book.Book) and found a method called delete, but the help doc doesn't give any examples:

Help on method delete in module piecash.core.book:

delete(obj) method of piecash.core.book.Book instance
    Delete an object from the book (to remove permanently an object)

I also ran help(book.accounts) (which is of type piecash._common.CallableList) and found a method called .remove, but that looks like it was just inherited from SQLAlchemy, since this is its documentation:

Help on method remove in module sqlalchemy.orm.collections:

remove(value, _sa_initiator=None) method of piecash._common.CallableList instance
    Remove first occurrence of value.

    Raises ValueError if the value is not present.

I also tried doing this (which also did not work):

del book.acccounts[3]  #3 is the index of one of the phantom GUID accounts I want to delete

The closest thing I could find to an example of deleting something using piecash was a subsection called Working with slots in the page called Tutorial: using existing objects. I tried to view my book's slots, but running book.slots returned the following traceback:

ValueError: Smart retrieval of GUID slot with name 'Default Budget' is not yet supported. Need to retrieve proper object type in kvp module (add in SlotGUID._mapping_name_class)

So, I am completely at a loss. Can you please help me understand how I can delete an account from a GnuCash Book object?

sdementen commented 3 years ago

None of what you tried worked? Did you get exceptions? How did you check the account was not removed ? After flushing/saving?

Could you post a simplified book that can be shared showing the issue?

Do not forget to backup your data before dangerous operations.

bxbrenden commented 3 years ago

No, none of what I tried appears to have worked. I can't provide you with the file because it belongs to my friend and has his personal financial data in it. Could you please tell me the recommended way to use piecash to delete an account from a book? Is there such a method? If not, I really think there should be.

If the only way to do it is with Slots objects, then I may need to open a bug report, because slots are fully broken for me in every book I have tried.

For the record, here's an example in ipython of the del book[accounts[index] method failing on another file:

In [1]: import piecash
In [2]: book = piecash.open_book('book-sqlite3.gnucash', readonly=False, open_if_lock=True)
In [3]: book.accounts[3]
Out[3]: Account<Expenses:Gas[USD]>
In [4]: del book.accounts[3]
In [5]: book.accounts[3]
Out[5]: Account<Expenses:Gas[USD]>
In [6]: book.save()

The above action had no consequences, i.e. it did not delete the account, even after saving and opening in GnuCash.

sdementen commented 3 years ago

Using https://piecash.readthedocs.io/en/master/api/piecash.core.book.html#piecash.core.book.Book.delete works as expected on my side. Do not forget to do a book.flush() before looking again at the collection book.accounts. For example:

print(book.accounts)  # N accounts
book.delete(book.accounts[2])
book.flush()
print(book.accounts)  # N-1 accounts
sdementen commented 3 years ago

Regarding the fact that you have "ghost" accounts in your book, they may be template accounts. Could you look at the result of

for acc in book.session.query(Account):
    print(acc.name,acc.type, acc.parent)

and tell me the type and the parent of these "ghost" accounts ? Maybe gnucash is keeping the account (vs deleting it completely) because some scheluded transaction or invoice or other object is still using this account ...

The safest way would be to understand what are the specific properties of this ghost account and then adapt the query in https://github.com/sdementen/piecash/blob/master/piecash/core/book.py#L366 to only show the "standard accounts". As you see in the code, I already filter the accounts without parents (Root account and Template account).

bxbrenden commented 3 years ago

@sdementen book.flush() was the key. I had tried the book.delete(book.accounts[x]) method before, but it didn't show any difference. Now that I flushed it, it deleted the phantom accounts.

As for how they got created in the first place, I think you are right about containing scheduled transactions. These were indeed template accounts as you said. I think what happened was this:

Anyway, the issue is resolved, but I think maybe just a slight addendum to the documentation to give slightly more detail would be helpful. The book.delete method's documentation is pretty bare and does not mention flushing.

Thanks again for your help!

sdementen commented 3 years ago

Flushing is like "save the changes in memory (but do not save)". If you save the book, it should also work. I will add a comment on deleting objects and on flushing changes.

How do you fix your issue at the end? By removing in gnucash the scheduled transaction?

bxbrenden commented 3 years ago

I see, that makes sense, like flushing a buffer.

Sorry for the long answer: Just some background on my use case for piecash: I made a small Flask app that allows a user to fill out a web form to enter transactions so they can use GnuCash when they're away from their computers.

My friend is my tester. I had him spin up a server that is on UTC time by default. He lives in a GMT -7 time zone, so when he uses my app to enter a transaction, it enters it using UTC time and syncs to his GitHub. When he reads the file on his laptop, it shows those transactions in the future due to the time difference. I think he entered some transactions on the web app, pulled the changes to his laptop, deleted some accounts, then pushed the changes back to GitHub. When the app refreshed, those GUID accounts showed up.

To answer your question, all I had to do to fix it was delete the accounts and flush. I didn't have to modify any transactions. I think the time skew was small enough that by the time I modified the book, those transactions were in the past. This is just a hypothesis, though.