Skip to content

Commit c568d9c

Browse files
committed
wip: Interim changes to transfer computers
1 parent fdde052 commit c568d9c

File tree

15 files changed

+496
-98
lines changed

15 files changed

+496
-98
lines changed

README.md

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ One area _Move2GnuCash_ will attempt to diverge from `csv2cash` is that the exis
1313
- PieCash
1414
- Pandas
1515

16+
## Limitations
17+
18+
Due to Quicken's manner of exporting data, account balances and categories need to be in place in the GnuCash book prior to the import of transactions. While this can be done manually (you still need to make them match what is coming out of Quicken!), _Move2GnuCash_ has options for importing these through two processes described below.
19+
20+
The challenges are exacerbated by limitations with Quicken's export of _All Transactions_ because we cannot group all non-investment accounts together into one export file. It becomes easier to export all transactions as one file and then let _Move2GnuCash_ divide the data into investment and non-investment transactions for processing.
21+
22+
_Move2GnuCash_ assumes all investment transaction history (in support of cost basis tracking) is present in the file even if non-investment transactions are limited to recent history (i.e., the chosen date for account balances described below). The implication is that non-investment transactions prior to th opening balances date will be filtered out while investment transactions will be retained.
23+
1624
## Exports from Quicken
1725

1826
_Move2GnuCash_ requires a minimum of two files from Quicken for each import process. Because exported CSV files from Quicken contain a varying number of extraneous lines and comments, these will have to be edited to get to final form.
@@ -27,32 +35,34 @@ With non-investment accounts, I chose 1 January 2017 as the start date, and so I
2735

2836
~~I then removed the top header lines (except the date), moved the labels from the first column to the second column, and removed the the other unnecessary formatting lines. In the end, the format of your file should look like this sample:~~
2937

30-
| ~~ | Accounts | 12/31/2016 |
38+
~~
39+
| | Accounts | 12/31/2016 |
3140
| ------------------- | -------- | ---------- |
32-
| Assets | |
33-
| Cash | |
34-
| - Cash | 77.12 |
35-
| - Checking One | 440.84 |
36-
| - Checking Two | 235.38 |
37-
| - Total Cash | 753.34 |
38-
| Savings | |
39-
| - Spouse Savings | 987.12 |
40-
| - Family Savings | 3250 |
41-
| - Total Savings | 4237.12 |
42-
| Property | |
43-
| - 2012 Silverado | 30975 |
44-
| - 2012 Camper | 0 |
45-
| - Total Property | 39975 |
46-
| Total Assets | 44965.46 |
47-
| Liabilities | |
48-
| Credit Card | |
49-
| - Apple Card | 0 |
50-
| - Credit Card | -973.4 |
51-
| - Total Credit Card | -973.4 |
52-
| Loan | |
53-
| - Student Loan | -10500 |
54-
| - Total Loan | -10650 |
55-
| Total Liabilities | -11623 | ~~ |
41+
| Assets | |
42+
| Cash | |
43+
| - Cash | 77.12 |
44+
| - Checking One | 440.84 |
45+
| - Checking Two | 235.38 |
46+
| - Total Cash | 753.34 |
47+
| Savings | |
48+
| - Spouse Savings | 987.12 |
49+
| - Family Savings | 3250 |
50+
| - Total Savings | 4237.12 |
51+
| Property | |
52+
| - 2012 Silverado | 30975 |
53+
| - 2012 Camper | 0 |
54+
| - Total Property | 39975 |
55+
| Total Assets | 44965.46 |
56+
| Liabilities | |
57+
| Credit Card | |
58+
| - Apple Card | 0 |
59+
| - Credit Card | -973.4 |
60+
| - Total Credit Card | -973.4 |
61+
| Loan | |
62+
| - Student Loan | -10500 |
63+
| - Total Loan | -10650 |
64+
| Total Liabilities | -11623 | |
65+
~~
5666

5767
_Move2GnuCash_ function `opening_book` retrieves the date (balances as of close of business) from the first line. The second line, because it has no associated figure in the second column, will be created as a [placeholder account](https://www.gnucash.org/docs/v4/C/gnucash-help/acct-create.html#accts-placeholder) and will be the parent of the next account created, and so on, until the accounts containing transactions are created (the hyphens will be removed).
5868

@@ -97,7 +107,7 @@ TODO: Finish this documenting.
97107

98108
GnuCash uses [double entry accounting](https://www.investopedia.com/terms/d/double-entry.asp). PieCash uses the term splits to refer to these particulars in a [transaction](https://piecash.readthedocs.io/en/master/tutorial/index_new.html#creating-a-new-transaction), which can be confusing for those used to Quicken, and perhaps Mint.
99109

100-
Move-to-GnuCash will map CSV entries and account for splits by creating multi-split transactions for GnuCash.
110+
_Move2GnuCash_ will map CSV entries to account for splits by creating multi-split transactions for GnuCash.
101111

102112
For example, consider this csv file which would reflect an expense in Quicken where \$11.50 is charged to the Dining category and \$0.95 reflects the sales tax charged by the state:
103113

@@ -106,7 +116,7 @@ For example, consider this csv file which would reflect an expense in Quicken wh
106116
| S | 01/22/2023 | Payment/Deposit | Breakfast Place | Dining | -10.55 | My Credit Card |
107117
| S | 01/22/2023 | Payment/Deposit | Breakfast Place | Sales Tax | -0.95 | My Credit Card |
108118

109-
GnuCash treats categories as just another account. When the transaction is created by PieCash, it will include three splits in order to capture the changes to all three accounts. Extending the idea presented in the PieCash tutorial examples, it might look like this:
119+
GnuCash treats categories as just another account. When the transaction is created by _Move2GnuCash_/PieCash, it will include three splits in order to capture the changes to all three accounts. Extending the idea presented in the PieCash tutorial examples, it might look like this:
110120

111121
```python
112122
trans1 = Transaction(

src/move2gnucash/__main__.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""Move2GnuCash"""
2+
import argparse
3+
from unicodedata import category
4+
5+
from piecash import Book, create_book, GnucashException, open_book
6+
7+
from move2gnucash.migrations import *
8+
9+
10+
class CapitalizedHelpFormatter(argparse.HelpFormatter):
11+
"""Class needed to write command line help using proper English."""
12+
13+
def add_usage(self, usage, actions, groups, prefix=None):
14+
if prefix is None:
15+
prefix = "Usage: "
16+
return super(CapitalizedHelpFormatter, self).add_usage(usage, actions, groups, prefix)
17+
18+
19+
parser = argparse.ArgumentParser(add_help=False, formatter_class=CapitalizedHelpFormatter)
20+
parser._positionals.title = "Positional arguments"
21+
parser._optionals.title = "Optional arguments"
22+
23+
parser.add_argument(
24+
"-v",
25+
"--version",
26+
action="version",
27+
version="%(prog)s 1.0",
28+
help="Show program's version number and exit.",
29+
)
30+
31+
parser.add_argument(
32+
"input_file", help="The csv file containing the input data. Typical extension: '.csv'."
33+
)
34+
parser.add_argument(
35+
"output_file",
36+
help="The name of the GnuCash file to create/write. Typical extension: '.gnucash'.",
37+
)
38+
parser.add_argument(
39+
"action",
40+
choices=["ACCTS", "CATS", "IE"],
41+
help="Specify migration action. ACCTS for accounts/balances; CATS for categories; IE for income and expense transactions.",
42+
)
43+
parser.add_argument(
44+
"-d", "--dry-run", help="Executes without writing a GnuCash file.", action="store_true"
45+
)
46+
parser.add_argument(
47+
"-h",
48+
"--help",
49+
action="help",
50+
default=argparse.SUPPRESS,
51+
help="Show this help message and exit.",
52+
)
53+
54+
args = parser.parse_args()
55+
56+
57+
def create_memory_book() -> Book:
58+
return create_book(currency="USD")
59+
60+
61+
def get_book(book_name: str, dry_run: bool) -> Book:
62+
try:
63+
book_instance = open_book(book_name, readonly=False)
64+
print(
65+
"Existing book opened, and data will be added to it (unless dry-run is True). If you meant to create a new book, re-run using a different book name."
66+
)
67+
if dry_run:
68+
book_instance = create_memory_book()
69+
print("Since dry-run is True, an empty in-memory book was created.")
70+
71+
except GnucashException:
72+
print(
73+
"Book doesn't exists...creating. If dry-run is True, the returned book instance will be in-memory vice a file."
74+
)
75+
if dry_run:
76+
book_instance = create_memory_book()
77+
else:
78+
book_instance: Book = create_book(args.output_file, currency="USD")
79+
return book_instance
80+
81+
82+
book = get_book(args.output_file, args.dry_run)
83+
84+
match args.action:
85+
case "ACCTS":
86+
opening_balances(args.input_file, book)
87+
print("Accounts and opening balances imported.")
88+
case "CATS":
89+
category_accounts(args.input_file, book)
90+
print("Categories imported as accounts.")
91+
case "IE":
92+
transactions(args.input_file, book)
93+
case _:
94+
print("Something weird occurred.")
95+
96+
book.close()

src/move2gnucash/data_maps.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ class Transaction2Move:
5050
def _split2move_list(split):
5151
return [
5252
Split2Move(
53-
split["tran_acct_from"], # Set to path_and_name for look up
54-
decimal_to(-split["tran_amount"]),
53+
split["tran_acct_from"],
54+
decimal_to(split["tran_amount"]) * -1,
5555
split.tran_memo,
5656
),
5757
Split2Move(
5858
split["tran_acct_to"],
59-
decimal_to(split["tran_amount"]),
59+
decimal_to(split["tran_amount"]) * 1,
6060
split.tran_memo,
6161
),
6262
]
@@ -76,11 +76,12 @@ def _transaction2move(
7676
)
7777

7878

79-
def _build_multi_splits_tran(split_group):
79+
def _build_multi_splits_tran(split_group: pd.DataFrame):
8080
splits = split_group.apply(_split2move_list, axis=1)
8181
splits_list = [elem for item in splits for elem in item]
82+
8283
return _transaction2move(
83-
posted=datetime.strptime(split_group["tran_date"].iat[0], "%m/%d/%Y").date(),
84+
posted=split_group["tran_date"].iat[0],
8485
description=split_group.tran_description.iat[0],
8586
notes=split_group.tran_memo.iat[0],
8687
num=split_group.tran_num.iat[0],
@@ -91,7 +92,7 @@ def _build_multi_splits_tran(split_group):
9192
def _build_single_splits_tran(split_data: pd.Series) -> Transaction2Move:
9293
splits_list = _split2move_list(split_data)
9394
return _transaction2move(
94-
posted=datetime.strptime(split_data["tran_date"], "%m/%d/%Y").date(),
95+
posted=split_data.tran_date,
9596
description=split_data["tran_description"],
9697
notes=split_data["tran_memo"],
9798
num=split_data["tran_num"],
@@ -123,6 +124,7 @@ def _processed_transactions(transactions: pd.DataFrame):
123124
if len(single_splits_data) > 0
124125
else []
125126
)
127+
126128
return multi_split_transactions + single_split_transactions
127129

128130

0 commit comments

Comments
 (0)