Skip to content

Poc/import from dotmny#192

Draft
marksimpson wants to merge 12 commits into
kenlasko:mainfrom
marksimpson:poc/import-from-dotmny
Draft

Poc/import from dotmny#192
marksimpson wants to merge 12 commits into
kenlasko:mainfrom
marksimpson:poc/import-from-dotmny

Conversation

@marksimpson
Copy link
Copy Markdown

This is a proof of concept - not ready to merge

Initial attempt at a migration pipeline. Put source.mny in migration, and run migrate.sh. It should take care of the rest.

This successfully imported my MS Money 2004 file, but I haven't done a full reconciliation to make sure all is correct. It looks very close though.

Submitting this for feedback and learning - I do not recommend merging it yet. I also have no idea how it will behave with .mny files from different versions.

- transform.ts: pure functions for reading .mdb tables and transforming
  Money data (dates, account types, frequencies, currency maps)
- migrate.ts: full import pipeline with cleanup, reference data, accounts,
  transactions, splits, transfers, investments, security prices, exchange
  rates, scheduled transactions, and balance computation

Fixes ON CONFLICT handling for payees and securities to correctly map
IDs when duplicates exist. Uses explicit existence check for categories
to handle NULL parent_id (where UNIQUE constraint doesn't prevent dupes).
- Load parent .env for POSTGRES_* vars (migration .env only needs
  MONEY_FILE_PASSWORD and MIGRATION_USER_EMAIL)
- Truncate security symbol to 20 chars when falling back to name
  (securities.symbol is VARCHAR(20))
- Update .env.example to document that Postgres settings are inherited
- Rebuild holdings from investment transactions using the same algorithm
  as holdings.service.ts rebuildFromTransactions() (78 holdings created)
- Deactivate scheduled transactions with next_due_date in the past so
  they don't show as overdue in Bills & Deposits (2,085 deactivated)
…ions

Money's TRN.act field determines investment transaction types. The LOT
table proves act=16 closes lots (transfer out / sell) despite its
misleading name, while act=15 opens lots (add shares / cost basis).

Previous logic inferred action from quantity sign, which was always
positive in Money. Holdings now match Money's LOT table exactly.
@kenlasko
Copy link
Copy Markdown
Owner

kenlasko commented Mar 8, 2026

I ran this and it actually worked pretty well! From what I can tell, most bank accounts migrated over completely seamlessly. It even kept my favourite tags! I don't know if it handles open/closed accounts because I had to open all of them to export the QIF. Here's the output:

Step 1: Decrypting .mny -> .mdb via sunriise...
0 [main] INFO  com.hungle.sunriise.export.ExportToMdb.main(ExportToMdb.java:398) - dbFile=source.mny
11 [main] INFO  com.hungle.sunriise.io.MnyDb.openDb(MnyDb.java:405) - # Opening dbFile=/home/ken/monize/migration/source.mny, readOnly=true, encrypted=true
4288 [main] INFO  com.hungle.sunriise.io.MnyDb.close(MnyDb.java:212) - Closing dbFile=source.mny
4289 [main] INFO  com.hungle.sunriise.export.ExportToMdb.main(ExportToMdb.java:403) - destFile=/home/ken/monize/migration/source.mdb
4289 [main] INFO  com.hungle.sunriise.export.ExportToMdb.main(ExportToMdb.java:404) - < DONE
  Done: source.mdb

Step 2: Running import...
Connected to PostgreSQL.

Importing for user: ken.lasko@gmail.com (0a4f284d-cf60-48e9-837a-0ce0ea8723fe)

Deleting existing data...

Ensuring currencies...
  Currency map: 155 entries

Reference data:
  Payees: 3247
  Categories: 329
  Securities: 98

Accounts:
  Accounts: 56 (10 investment-cash links)

Transactions:
  Transactions: 37120  (1667 recurring phantoms, 13 orphaned transfers skipped)
  Splits: 10142
  Transfers: 4501

Investments:
  Investment transactions: 3429

Prices & rates:
  Security prices: 68854
  Exchange rates: 128

Scheduled transactions:
  Scheduled transactions: 1844

Computing balances and holdings:
  Balances updated: 56 accounts
  Holdings: 84

Post-import cleanup:
  Deactivated past scheduled transactions: 1828

Migration complete.

Import complete.

Things I noticed:

I had to manually install pg via npm install pg. No biggie.

All loans show up as Mortgages with no transactions in them. The bill payment splits from the source account for the principal show up blank. It should be a TYPE: Transfer and transfer to the loan account

It imported every scheduled bill I've ever had since 2003-Oct-15. Not sure why that date, because I have data going back to 1994. I thought they were long gone, but are obviously still in the database. There are now 1844 bills/deposits, where I actually have 20. All are shown as Inactive, which is correct for the vast majority, but not the "real" ones.

There are a few weird payees called # and * with zero transactions to them. Not sure why they're there

There are some categories that don't appear in MS Money like alimony.

All securities imported properly.

HOWEVER, my investment accounts are a mess. Shares don't line up at all. Can't make any sense of how it arrived at the numbers. One shows as having negative transactions for some reason.

Overall, its quite promising. I think the loan issue should be easy to rectify, but I have no idea what's going so wrong with investments.

@marksimpson
Copy link
Copy Markdown
Author

Oh yes, mortgage is on my todo list. I haven't leant into that yet, as I switched to using liability accounts a decade ago (far more flexible). But I should make sure my historical data is right.

Interesting about the bills and deposits. I haven't checked the data quality in my database yet.

That's interesting about the accounts. Mine carried over almost perfectly. There is issues with things like the cost basis calculation (for investments in a different currency to your base currency, Money seems to store the base value in the base currency as the exchange rate was at the time). There are some subtle issues with determining buys versus sells that it might have tripped up on.

What version of MS Money was your data file from?

@kenlasko
Copy link
Copy Markdown
Owner

I'm using the Sunset Edition.

@kenlasko kenlasko marked this pull request as draft March 10, 2026 13:02
@gerardfarrell11
Copy link
Copy Markdown

I've tested this with MSMoney 2001 and after a little help from Claude it seemed to work.
I imported ~25 years of accounts and investment transactions.
I see closed accounts as closed, with a re-open option. Balances in multiple currencies seem to be correct.
Installed as a dedicated Proxmox LXC (Debian 12, 10GB disk, 2 cores, 2GB RAM) running Docker.
Notes below as they may help others.

I was initially unable to get the backend to start, which Claude identified as:

Migration 056_monte_carlo_scenarios.sql tries to create a trigger that already exists in your database,
and the migration runner crashes instead of skipping it. It then keeps restarting and retrying in a loop.

This happened because the migration was partially applied at some point.
The table/trigger got created, but the migration wasn't recorded as complete.

Fix: manually mark the migration as done:

Connect to your postgres container and mark the migration as already applied:

docker exec -it monize-postgres psql -U $POSTGRES_USER -d $POSTGRES_DB

Once inside psql, check how migrations are tracked:

\dt

Look for a table like migrations, schema_migrations, or migration_history, then check what's in it:

SELECT * FROM migrations LIMIT 10;

Then insert the missing record (the exact syntax depends on the table structure, but likely something like):

INSERT INTO schema_migrations (filename, applied_at) VALUES ('056_monte_carlo_scenarios.sql', NOW());

Exit psql with \q, then restart the backend:

docker compose -f docker-compose.prod.yml restart backend
docker compose -f docker-compose.prod.yml up -d frontend

I then copied my MSMoney file over to the LXC, renamed it to source.mny and ran the migration tool.

	cd ~/monize/migration
	npm install
	chmod +x migrate.sh
	./migrate.sh

This failed I was missing libatomic.so.1

	apt-get install -y libatomic1
	./migrate.sh

It got further, but was missing pg

	cd ~/monize/migration
	npm install pg
	./migrate.sh

I then had a few Postgres issues, mainly making sure the .env file was set up correctly with the docker IP address,
and my random password contained $T which Claude thought may have caused hashing issues if the shell tried to use it as variable. Once all those were figured out

./migrate.sh

got further, but failed on table BILL.

Claude walked me through checking the tables and modifying the scripts:

nano ~/monize/migration/migrate.ts

First fix — line 347, change:

typescriptconst billRows = readTable(MDB_FILE, 'BILL')

to:

typescriptconst billRows = tableExists(MDB_FILE, 'BILL') ? readTable(MDB_FILE, 'BILL') : []

Second fix — line 726, same change:

typescriptconst billRows = tableExists(MDB_FILE, 'BILL') ? readTable(MDB_FILE, 'BILL') : []

Then:
nano ~/monize/migration/transform.ts

Add this function right after the closing } of readTable:

typescriptexport function tableExists(mdbFile: string, table: string): boolean {
try {
	const tables = execSync(`mdb-tables "${mdbFile}"`, { maxBuffer: MAX_BUFFER }).toString('utf-8')
	return tables.split(/\s+/).includes(table)
} catch {
	return false
}
}

Then:

nano ~/monize/migration/migrate.ts

Find the import line at the top that imports from transform.ts and add tableExists to it. It probably looks like:

typescriptimport { readTable, 
					tableExists,				<-- added this
					... } from './transform'

./migrate.sh

and got:

Deleting existing data...
Ensuring currencies...
Currency map: 45 entries
Reference data:
Payees: 1032
Categories: 392
Securities: 117
Accounts:
Accounts: 72 (6 investment-cash links)
Transactions:
Transactions: 27584 (49 recurring phantoms, 0 orphaned transfers skipped)
Splits: 115
Transfers: 1740
Investments:
Investment transactions: 281
Prices & rates:
Security prices: 3168
Exchange rates: 76
Scheduled transactions:
Scheduled transactions: 0
Computing balances and holdings:
Balances updated: 72 accounts
Holdings: 9
Post-import cleanup:
Deactivated past scheduled transactions: 0
Migration complete.
Import complete.

Scheduled transactions came across as 0, which is expected since Money 2001 didn't have a separate BILL table.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants