/*******************************************************************************
***  This file is part of Dux Rei Nummariae.                                 ***
***                                                                          ***
***  Copyright (C) 2017, 2018, 2019, 2020, 2021                              ***
***  John Schneiderman <Licensing _AT_ Schneiderman _DOT_ me>                ***
***                                                                          ***
***  This program is free software: you can redistribute it and/or modify it ***
***  under the terms of the GNU Affero General Public License as published   ***
***  by the Free Software Foundation, either version 3 of the License, or    ***
***  (at your option) any later version.                                     ***
***                                                                          ***
***  This program is distributed in the hope that it will be useful, but     ***
***  WITHOUT ANY WARRANTY; without even the implied warranty of              ***
***  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                    ***
***  See the GNU Affero General Public License for more details.             ***
***                                                                          ***
***  You should have received a copy of the GNU Affero General Public License***
***  along with this program. If not, see <http://www.gnu.org/licenses/>.    ***
*******************************************************************************/
#include <pecunia/Codes.h>
using pecunia::currency::Iso4217Codes;
#include <pecunia/Money.h>
using pecunia::currency::Money;
#include <pecunia/MoneyTypes.h>
using pecunia::converter;
using pecunia::FloatingPoint;
#include <QDate>
using ::QDate;
#include <QLocale>
using ::QLocale;
#include <QObject>
using ::QObject;
// using Q_OBJECT
#include <QTest>
// using QTEST_MAIN
// using QVERIFY_EXCEPTION_THROWN
#include <qt-mocks/Matchers.h>
using qt_mocks::equals;
using qt_mocks::isFalse;
using qt_mocks::isTrue;
using qt_mocks::memberFunction;
using qt_mocks::notEquals;
// using QVERIFY_FALSE;
// using QVERIFY_THAT;
// using QVERIFY_TRUE;

#include <chrono>
using std::chrono::system_clock;
#include <map>
using std::map;
#include <set>
using std::set;
#include <stdexcept>
using std::logic_error;
#include <string>
using namespace std::string_literals;
using std::string;
#include <vector>
using std::vector;

#include <accounting/Account.h>
using drn::accounting::Account;
using drn::accounting::AccountCode;
using drn::accounting::AccountTypes;
#include <accounting/AccountNumberGenerator.h>
using drn::accounting::SequentialAccountNumberGenerator;
#include <accounting/AccountingErrors.h>
using drn::accounting::AccountError;
using drn::accounting::TransactionError;
#include <accounting/LedgerBooks.h>
using drn::accounting::GeneralLedger;
using drn::accounting::LedgerAccount;
#include <accounting/Transaction.h>
using drn::accounting::Transaction;
using drn::accounting::TransactionNumber;
using drn::accounting::TransactionStatuses;
#include <foundation/Error.h>
using drn::foundation::Error;
#include <internal/AccountTransactionKey.hpp>
using drn::accounting::internal::AccountTransactionKey;


namespace drn
{
namespace accounting
{
namespace tests
{

struct GeneralLedgerUT : QObject
{
	Q_OBJECT

private slots:
	void initTestCase()
	{
		converter =
				[] (const Iso4217Codes& from, const Iso4217Codes& to) -> FloatingPoint
				{
					if (from == to)
						return static_cast<FloatingPoint>(1.0);
					switch (from)
					{
					case Iso4217Codes::XXX:
						return static_cast<FloatingPoint>(1.0);
					case Iso4217Codes::PLN:
						switch (to)
						{
						case Iso4217Codes::USD:
							return static_cast<FloatingPoint>(0.27);
						case Iso4217Codes::XXX:
							return static_cast<FloatingPoint>(1.0);
						default:
							throw logic_error{"Unhandled to code."};
						};
					case Iso4217Codes::USD:
						switch (to)
						{
						case Iso4217Codes::PLN:
							return static_cast<FloatingPoint>(3.76);
						case Iso4217Codes::XXX:
							return static_cast<FloatingPoint>(1.0);
						default:
							throw logic_error{"Unhandled to code."};
						};
					default:
						throw logic_error{"Unhandled from code."};
					}
				};
	}
	void init()
	{
		this->sang_ = AccountNumber{};
	}

	//{ constructor Tests

	void constructor_default_ShouldBeDefaults()
	{
		const GeneralLedger gl{};
		QVERIFY_TRUE(gl.accounts().empty());
		QVERIFY_TRUE(gl.ledgers().empty());
		QVERIFY_THAT(gl.openingAccountCode().hasValue(), isFalse());
	}

	void constructor_fullInit_ShouldBeValues()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode homeCashCode{AccountNumber{1002u}, "Cash Home", cashCode.number()};
		const Account homeCash{AccountTypes::Asset, homeCashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> cashTransactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{1u}
				}
			},
			{
				TransactionNumber{2u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{2u}
				}
			},
			{
				TransactionNumber{3u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{3u}
				}
			}
		};
		const map<TransactionNumber, Transaction> waterTransactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("SHOULD IGNORE DESCRIPTION: Paid Water Bill"),
					TransactionNumber{1u}
				}
			},
			{
				TransactionNumber{2u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("SHOULD IGNORE DESCRIPTION: Paid Water Bill"),
					TransactionNumber{2u}
				}
			},
			{
				TransactionNumber{3u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("SHOULD IGNORE DESCRIPTION: Paid Water Bill"),
					TransactionNumber{3u}
				}
			}
		};
		const LedgerAccount cashBook{cash, cashTransactions};
		const LedgerAccount homeCashBook{homeCash, {}};
		const LedgerAccount waterBook{water, waterTransactions};
		const LedgerAccount openingBook{opening, {}};
		const map<AccountCode, LedgerAccount> ledgerData{
			{waterCode, waterBook},
			{cashCode, cashBook},
			{homeCashCode, homeCashBook},
			{openingCode, openingBook}
		};
		const set<Account> ledgerAccounts{cash, homeCash, water, opening};
		const GeneralLedger gl{ledgerData, openingCode};
		QVERIFY_THAT(
			gl,
			memberFunction(&GeneralLedger::ledgers, equals(ledgerData))
		);
		QVERIFY_THAT(
			gl,
			memberFunction(&GeneralLedger::accounts, equals(ledgerAccounts))
		);
		QVERIFY_THAT(gl.openingAccountCode() == openingCode, isTrue());
	}

	void constructor_ParentDoesNotExist_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash", AccountNumber{9876u}};
		const AccountCode waterCode{AccountNumber{2001u}, "Water", cashCode.number()};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const LedgerAccount cashBook{cash, {}};
		const LedgerAccount waterBook{water, {}};
		const LedgerAccount openingBook{opening, {}};
		const map<AccountCode, LedgerAccount> ledgerData{
			{waterCode, waterBook},
			{cashCode, cashBook},
			{openingCode, openingBook}
		};
		const set<Account> ledgerAccounts{cash, water, opening};
		QVERIFY_EXCEPTION_THROWN((GeneralLedger{ledgerData, openingCode}), AccountError);
	}

	void constructor_NewParentIsChildAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash", AccountNumber{2001u}};
		const AccountCode waterCode{AccountNumber{2001u}, "Water", cashCode.number()};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const LedgerAccount cashBook{cash, {}};
		const LedgerAccount waterBook{water, {}};
		const LedgerAccount openingBook{opening, {}};
		const map<AccountCode, LedgerAccount> ledgerData{
			{waterCode, waterBook},
			{cashCode, cashBook},
			{openingCode, openingBook}
		};
		const set<Account> ledgerAccounts{cash, water, opening};
		QVERIFY_EXCEPTION_THROWN((GeneralLedger{ledgerData, openingCode}), AccountError);
	}

	void constructor_transactionAccountsDebitCreditDoNotMatch_ShouldThrow()
	{
		const Money runningBalance{123, 4567u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{5001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{8001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> transactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{1u}
				}
			}
		};
		const map<TransactionNumber, Transaction> swapped{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					cash.code(), // N.B. The other transaction is water here.
					water.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{1u}
				}
			}
		};
		const map<AccountCode, LedgerAccount> accountsToLedgers{
			{waterCode, LedgerAccount{water, swapped}},
			{cashCode, LedgerAccount{cash, transactions}}
		};
		QVERIFY_EXCEPTION_THROWN(GeneralLedger{accountsToLedgers}, TransactionError);
	}

	void constructor_transactionNumbersDoNotMatch_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{5001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{8001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> transactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{25u}
				}
			}
		};
		const map<AccountCode, LedgerAccount> ledgerData{
			{waterCode, LedgerAccount{water, transactions}},
			{cashCode, LedgerAccount{cash, transactions}}
		};
		QVERIFY_EXCEPTION_THROWN(GeneralLedger{ledgerData}, TransactionError);
	}

	void constructor_mappedAccountCodeDoesNotMatchLedgerAccountCode_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{5001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{8001u}, "Water"};
		const Account water{AccountTypes::Expense, cashCode, runningBalance};
		const map<AccountCode, LedgerAccount> ledgerData{
			{waterCode, LedgerAccount{water, {}}},
			{cashCode, LedgerAccount{cash, {}}}
		};
		QVERIFY_EXCEPTION_THROWN(GeneralLedger{ledgerData}, AccountError);
	}

	void constructor_openingAccountMissing_ShouldThrow()
	{
		const AccountCode openingAccount{AccountNumber{2002u}, "Opening Balances"};
		QVERIFY_EXCEPTION_THROWN(GeneralLedger({}, openingAccount), AccountError);
	}

	void constructor_openingAccountWrongType_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Asset, openingCode, runningBalance};
		const LedgerAccount cashBook{cash, {}};
		const LedgerAccount openingBook{opening, {}};
		const map<AccountCode, LedgerAccount> ledgerData{
			{cashCode, cashBook},
			{openingCode, openingBook}
		};
		QVERIFY_EXCEPTION_THROWN(GeneralLedger(ledgerData, openingCode), AccountError);
	}

	void constructor_openingAccountCodesDoNotMatch_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, runningBalance};
		const LedgerAccount cashBook{opening, {}};
		const LedgerAccount openingBook{opening, {}};
		const map<AccountCode, LedgerAccount> ledgerData{
			{cashCode, cashBook},
			{openingCode, openingBook}
		};
		QVERIFY_EXCEPTION_THROWN(GeneralLedger(ledgerData, openingCode), AccountError);
	}

	void constructor_transactionOnlyInOneAccountLedgerBookFirstProcessed_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> transactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					openingCode,
					cash.code(),
					amount,
					QStringLiteral("Opening Balance"),
					TransactionNumber{1u}
				}
			}
		};
		const LedgerAccount cashBook{cash, {}}; // N.B. the transaction missing here.
		const LedgerAccount openingBook{opening, transactions};
		const map<AccountCode, LedgerAccount> ledgerData{
			{cashCode, cashBook},
			{openingCode, openingBook}
		};
		const set<Account> ledgerAccounts{cash, opening};
		QVERIFY_EXCEPTION_THROWN(GeneralLedger(ledgerData, openingCode), AccountError);
	}

	void constructor_transactionOnlyInOneAccountLedgerBookSecondProcessed_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> transactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					openingCode,
					cash.code(),
					amount,
					QStringLiteral("Opening Balance"),
					TransactionNumber{1u}
				}
			}
		};
		const LedgerAccount cashBook{cash, transactions};
		const LedgerAccount openingBook{opening, {}};
		const map<AccountCode, LedgerAccount> ledgerData{
			{cashCode, cashBook},
			{openingCode, openingBook}
		};
		const set<Account> ledgerAccounts{cash, opening};
		QVERIFY_EXCEPTION_THROWN(GeneralLedger(ledgerData, openingCode), AccountError);
	}

	//}

	//{ hasLedger Tests

	void hasLedger_accountDoesExists_ShouldBeTrue()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> transactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{1u}
				}
			}
		};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> hasLedgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		const GeneralLedger gl{hasLedgers};
		QVERIFY_TRUE(gl.hasLedger(cashCode));
	}
	void hasLedger_accountDoesNotExists_ShouldBeFalse()
	{
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const GeneralLedger gl{};
		QVERIFY_FALSE(gl.hasLedger(cashCode));
	}

	//}

	//{ ledger Tests

	void ledger_accountDoesExists_ShouldBeAccount()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> transactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{1u}
				}
			}
		};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		const GeneralLedger gl{ledgers};
		QVERIFY_THAT(gl.ledger(cashCode), equals(cashBook));
	}
	void ledger_accountDoesNotExists_ShouldBeAccount()
	{
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const GeneralLedger gl{};
		QVERIFY_EXCEPTION_THROWN(gl.ledger(cashCode), AccountError);
	}

	//}

	//{ post Tests

	void post_hasCreditAndDebitAccount_ShouldBePosted()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{
			occurredOn,
			water.code(),
			cash.code(),
			amount,
			QStringLiteral("Paid Water Bill"),
			TransactionNumber{1u},
			TransactionStatuses::Reconciled
		};
		const TransactionNumber priorNumber{1u};
		const map<TransactionNumber, Transaction> transactions{{priorNumber, txn}};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const Transaction newTxn{occurredOn, water.code(), cash.code(), amount};
		const auto postedTxnNumber{gl.post(newTxn)};
		const TransactionNumber expectedTxnNumber{2u};
		const map<TransactionNumber, Transaction> expectedTransactions{
			{priorNumber, txn},
			{
				expectedTxnNumber,
				Transaction{occurredOn, water.code(), cash.code(), amount, {}, expectedTxnNumber}
			}
		};
		const Money expectedCash{73, 2000u, Iso4217Codes::USD};
		const Money expectedWater{173, 7000u, Iso4217Codes::USD};
		QVERIFY_THAT(postedTxnNumber, equals(expectedTxnNumber));
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(expectedCash));
		QVERIFY_THAT(gl.ledger(waterCode).account_.balance(), equals(expectedWater));
		QVERIFY_THAT(gl.ledger(cashCode).transactions_, equals(expectedTransactions));
		QVERIFY_THAT(gl.ledger(waterCode).transactions_, equals(expectedTransactions));
	}

	void post_hasCreditAndDebitAccountMixedCurrencies_ShouldBePosted()
	{
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, Money{Iso4217Codes::USD}};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, Money{Iso4217Codes::PLN}};
		const map<TransactionNumber, Transaction> transactions{};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const Transaction txn{
			QDate{2020, 12, 15},
			water.code(),
			cash.code(),
			Money{40, 3000u, Iso4217Codes::PLN},
			QStringLiteral("Paying off December's balance.")
		};
		const auto postedTxnNumber{gl.post(txn)};
		const TransactionNumber expectedTxnNumber{1u};
		const Money expectedCash{-10, 8800u, Iso4217Codes::USD};
		const Money expectedWater{40, 3000u, Iso4217Codes::PLN};
		QVERIFY_THAT(postedTxnNumber, equals(expectedTxnNumber));
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(expectedCash));
		QVERIFY_THAT(gl.ledger(waterCode).account_.balance(), equals(expectedWater));
		QVERIFY_TRUE(gl.ledger(cashCode).transactions_.count(expectedTxnNumber) == 1);
		const auto cashTxn{gl.ledger(cashCode).transactions_.at(expectedTxnNumber)};
		QVERIFY_THAT(cashTxn.occurredOn(), equals(txn.occurredOn()));
		QVERIFY_THAT(cashTxn.debiting(), equals(txn.debiting()));
		QVERIFY_THAT(cashTxn.crediting(), equals(txn.crediting()));
		// Testing that the amount is properly converted as expected in the transaction.
		QVERIFY_THAT(cashTxn.amount(), equals(Money(10, 8800u, Iso4217Codes::USD)));
		QVERIFY_THAT(cashTxn.amount().code(), equals(cash.balance().code()));
		QVERIFY_THAT(
			*cashTxn.description(),
			equals(
				*txn.description() + " Converted from PLN at the exchange rate of USD 0.27."
			)
		);
		const auto waterTxn{gl.ledger(waterCode).transactions_.at(expectedTxnNumber)};
		QVERIFY_THAT(waterTxn.occurredOn(), equals(txn.occurredOn()));
		QVERIFY_THAT(waterTxn.debiting(), equals(txn.debiting()));
		QVERIFY_THAT(waterTxn.crediting(), equals(txn.crediting()));
		QVERIFY_THAT(waterTxn.amount(), equals(txn.amount()));
		QVERIFY_THAT(waterTxn.amount().code(), equals(water.balance().code()));
		QVERIFY_THAT(waterTxn.description(), equals(txn.description()));
	}

	void post_hasTransactionBalance_ShouldBeEach()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn0{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn0{
			occurredOn0,
			water.code(),
			cash.code(),
			amount,
			QStringLiteral("Paid Water Bill"),
			TransactionNumber{1u}
		};
		const map<TransactionNumber, Transaction> transactions{{TransactionNumber{1u}, txn0}};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const Transaction newTxn0{occurredOn0, water.code(), cash.code(), amount};
		gl.post(newTxn0);
		const QDate occurredOn1{2018, 6, 2};
		const Transaction newTxn1{occurredOn1, water.code(), cash.code(), amount};
		gl.post(newTxn1);
		const Money expectedCash{22, 9500u, Iso4217Codes::USD};
		const Money expectedWater{223, 9500u, Iso4217Codes::USD};
		QVERIFY_THAT(gl.ledger(cashCode).transactions_.size(), equals(size_t(3)));
		QVERIFY_THAT(gl.ledger(waterCode).transactions_.size(), equals(size_t(3)));
		auto cashTransaction{gl.ledger(cashCode).transactions_.cbegin()};
		++cashTransaction; // Skip the initial transaction, and go to the posted ones.
		QVERIFY_THAT(*cashTransaction->second.number(), equals(TransactionNumber{2u}));
		++cashTransaction;
		QVERIFY_THAT(*cashTransaction->second.number(), equals(TransactionNumber{3u}));
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(expectedCash));
		auto waterTransaction{gl.ledger(waterCode).transactions_.cbegin()};
		++waterTransaction; // Skip the initial transaction, and go to the posted ones.
		QVERIFY_THAT(*waterTransaction->second.number(), equals(TransactionNumber{2u}));
		++waterTransaction;
		QVERIFY_THAT(*waterTransaction->second.number(), equals(TransactionNumber{3u}));
		QVERIFY_THAT(gl.ledger(waterCode).account_.balance(), equals(expectedWater));
	}

	void post_missingDebitAccount_ShouldThrow()
	{
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, Iso4217Codes::USD};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, Iso4217Codes::USD};
		LedgerAccount cashBook{cash, {}};
		LedgerAccount waterBook{water, {}};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{occurredOn, AccountCode{AccountNumber{18u}}, cash.code(), amount};
		QVERIFY_EXCEPTION_THROWN(gl.post(txn), TransactionError);
	}

	void post_missingCreditAccount_ShouldThrow()
	{
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, Iso4217Codes::USD};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, Iso4217Codes::USD};
		LedgerAccount cashBook{cash, {}};
		LedgerAccount waterBook{water, {}};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{occurredOn, water.code(), AccountCode{AccountNumber{18u}}, amount};
		QVERIFY_EXCEPTION_THROWN(gl.post(txn), TransactionError);
	}

	void post_hasClosedCreditAndOpenDebitAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		Account cash{AccountTypes::Asset, cashCode, runningBalance};
		cash.close();
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{
			occurredOn,
			water.code(),
			cash.code(),
			amount,
			QStringLiteral("Paid Water Bill"),
			TransactionNumber{1u},
			TransactionStatuses::Reconciled
		};
		const map<TransactionNumber, Transaction> transactions{{TransactionNumber{1u}, txn}};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const Transaction newTxn{occurredOn, water.code(), cash.code(), amount};
		QVERIFY_EXCEPTION_THROWN(gl.post(newTxn), TransactionError);
	}

	void post_hasOpenCreditAndClosedDebitAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		Account water{AccountTypes::Expense, waterCode, runningBalance};
		water.close();
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{
			occurredOn,
			water.code(),
			cash.code(),
			amount,
			QStringLiteral("Paid Water Bill"),
			TransactionNumber{1u},
			TransactionStatuses::Reconciled
		};
		const map<TransactionNumber, Transaction> transactions{{TransactionNumber{1u}, txn}};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const Transaction newTxn{occurredOn, water.code(), cash.code(), amount};
		QVERIFY_EXCEPTION_THROWN(gl.post(newTxn), TransactionError);
	}

	void post_hasClearedTransaction_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{
			occurredOn,
			water.code(),
			cash.code(),
			amount,
			QStringLiteral("Paid Water Bill"),
			TransactionNumber{1u},
			TransactionStatuses::Reconciled
		};
		const map<TransactionNumber, Transaction> transactions{{TransactionNumber{1u}, txn}};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		Transaction newTxn{occurredOn, water.code(), cash.code(), amount};
		newTxn.markCleared();
		QVERIFY_EXCEPTION_THROWN(gl.post(newTxn), TransactionError);
	}

	void post_hasReconciledTransaction_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{
			occurredOn,
			water.code(),
			cash.code(),
			amount,
			QStringLiteral("Paid Water Bill"),
			TransactionNumber{1u},
			TransactionStatuses::Reconciled
		};
		const map<TransactionNumber, Transaction> transactions{{TransactionNumber{1u}, txn}};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		Transaction newTxn{occurredOn, water.code(), cash.code(), amount};
		newTxn.markReconciled();
		QVERIFY_EXCEPTION_THROWN(gl.post(newTxn), TransactionError);
	}

	void post_transactionUnknownCurrency_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2u, Iso4217Codes::XXX};
		const Transaction txn{
			occurredOn,
			water.code(),
			cash.code(),
			amount,
			QStringLiteral("Paid Water Bill"),
			TransactionNumber{1u},
			TransactionStatuses::Reconciled
		};
		const map<TransactionNumber, Transaction> transactions{{TransactionNumber{1u}, txn}};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const Transaction newTxn{occurredOn, water.code(), cash.code(), amount};
		QVERIFY_EXCEPTION_THROWN(gl.post(newTxn), TransactionError);
	}
	//}

	//{ repost Tests

	void repost_hasNewTransactionBalance_ShouldBeNewBalance()
	{
		const Money emptyBalance{0, 0u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, emptyBalance};
		LedgerAccount cashBook{cash, {}};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, emptyBalance};
		LedgerAccount waterBook{water, {}};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{occurredOn, water.code(), cash.code(), amount};
		gl.post(txn);
		const auto newAmount{amount + Money{5, 5500u, Iso4217Codes::USD}};
		const QDate newOccurredOn{2018, 6, 3};
		const Transaction newTxn{newOccurredOn,  water.code(), cash.code(), newAmount};
		gl.repost(TransactionNumber{1u}, newTxn);
		const Money expectedCash{-55, 8000u, Iso4217Codes::USD};
		auto cashTransaction{gl.ledger(cashCode).transactions_.cbegin()};
		QVERIFY_THAT(*cashTransaction->second.number(), equals(TransactionNumber{1u}));
		QVERIFY_THAT(cashTransaction->second.occurredOn(), equals(newOccurredOn));
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(expectedCash));
		const Money expectedWater{55, 8000u, Iso4217Codes::USD};
		auto waterTransaction{gl.ledger(waterCode).transactions_.cbegin()};
		QVERIFY_THAT(*waterTransaction->second.number(), equals(TransactionNumber{1u}));
		QVERIFY_THAT(waterTransaction->second.occurredOn(), equals(newOccurredOn));
		QVERIFY_THAT(gl.ledger(waterCode).account_.balance(), equals(expectedWater));
	}

	void repost_hasNewTransactionDifferentAccounts_ShouldBeRevertOldAndUpdateNew()
	{
		const Money emptyBalance{0, 0u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, emptyBalance};
		LedgerAccount cashBook{cash, {}};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, emptyBalance};
		LedgerAccount waterBook{water, {}};
		const AccountCode currentCode{AccountNumber{1005u}, "Current"};
		const Account current{AccountTypes::Asset, currentCode, emptyBalance};
		LedgerAccount currentBook{current, {}};
		const AccountCode electricityCode{AccountNumber{2005u}, "Electricity"};
		const Account electricity{AccountTypes::Expense, electricityCode, emptyBalance};
		LedgerAccount electricityBook{electricity, {}};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook},
			{currentCode, currentBook},
			{electricityCode, electricityBook}
		};
		GeneralLedger gl{ledgers};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{occurredOn, water.code(), cash.code(), amount};
		gl.post(txn);
		const Transaction changeTxn1{occurredOn, electricity.code(), current.code(), amount};
		gl.repost(TransactionNumber{1u}, changeTxn1);
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(emptyBalance));
		QVERIFY_THAT(gl.ledger(waterCode).account_.balance(), equals(emptyBalance));
		QVERIFY_THAT(gl.ledger(currentCode).account_.balance(), equals(-amount));
		QVERIFY_THAT(gl.ledger(electricityCode).account_.balance(), equals(amount));
	}

	void repost_missingDebitAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{
			occurredOn,
			water.code(),
			cash.code(),
			amount,
			QStringLiteral("Paid Water Bill"),
			TransactionNumber{1u},
			TransactionStatuses::Reconciled
		};
		const map<TransactionNumber, Transaction> transactions{{TransactionNumber{1u}, txn}};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const Transaction newTxn{occurredOn, AccountCode{AccountNumber{18u}}, cash.code(), amount};
		QVERIFY_EXCEPTION_THROWN(gl.repost(TransactionNumber{1u}, newTxn), TransactionError);
	}

	void repost_missingCreditAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{
			occurredOn,
			water.code(),
			cash.code(),
			amount,
			QStringLiteral("Paid Water Bill"),
			TransactionNumber{1u},
			TransactionStatuses::Reconciled
		};
		const map<TransactionNumber, Transaction> transactions{{TransactionNumber{1u}, txn}};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const Transaction newTxn{occurredOn, water.code(), AccountCode{AccountNumber{18u}}, amount};
		QVERIFY_EXCEPTION_THROWN(gl.repost(TransactionNumber{1u}, newTxn), TransactionError);
	}

	void repost_hasClosedCreditAndOpenDebitAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		Account cash{AccountTypes::Asset, cashCode, runningBalance};
		cash.close();
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{
			occurredOn,
			water.code(),
			cash.code(),
			amount,
			QStringLiteral("Paid Water Bill"),
			TransactionNumber{1u},
			TransactionStatuses::Reconciled
		};
		const map<TransactionNumber, Transaction> transactions{{TransactionNumber{1u}, txn}};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const Transaction newTxn{occurredOn, water.code(), cash.code(), amount};
		QVERIFY_EXCEPTION_THROWN(gl.repost(TransactionNumber{1u}, newTxn), TransactionError);
	}

	void repost_hasOpenCreditAndClosedDebitAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		Account water{AccountTypes::Expense, waterCode, runningBalance};
		water.close();
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{
			occurredOn,
			water.code(),
			cash.code(),
			amount,
			QStringLiteral("Paid Water Bill"),
			TransactionNumber{1u},
			TransactionStatuses::Reconciled
		};
		const map<TransactionNumber, Transaction> transactions{{TransactionNumber{1u}, txn}};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const Transaction newTxn{occurredOn, water.code(), cash.code(), amount};
		QVERIFY_EXCEPTION_THROWN(gl.repost(TransactionNumber{1u}, newTxn), TransactionError);
	}

	void repost_transactionNumberMissing_ShouldThrow()
	{
		const Money emptyBalance{0, 0u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, emptyBalance};
		LedgerAccount cashBook{cash, {}};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, emptyBalance};
		LedgerAccount waterBook{water, {}};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{occurredOn, water.code(), cash.code(), amount};
		// gl.post(txn);
		const auto newAmount{amount + Money{5, 55u, Iso4217Codes::USD}};
		const QDate newOccurredOn{2018, 6, 3};
		const Transaction newTxn{newOccurredOn,  water.code(), cash.code(), newAmount};
		QVERIFY_EXCEPTION_THROWN(gl.repost(TransactionNumber{1u}, newTxn), TransactionError);
	}

	void repost_newTransactionIsInvalid_ShouldNotChange()
	{
		const Money emptyBalance{0, 0u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, emptyBalance};
		LedgerAccount cashBook{cash, {}};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, emptyBalance};
		LedgerAccount waterBook{water, {}};
		const AccountCode currentCode{AccountNumber{1005u}, "Current"};
		const Account current{AccountTypes::Asset, currentCode, emptyBalance};
		LedgerAccount currentBook{current, {}};
		const AccountCode electricityCode{AccountNumber{2005u}, "Electricity"};
		const Account electricity{AccountTypes::Expense, electricityCode, emptyBalance, true};
		LedgerAccount electricityBook{electricity, {}};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook},
			{currentCode, currentBook},
			{electricityCode, electricityBook}
		};
		GeneralLedger gl{ledgers};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{occurredOn, water.code(), cash.code(), amount};
		gl.post(txn);
		const Transaction changeTxn1{occurredOn, electricity.code(), current.code(), amount};
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(-amount));
		QVERIFY_THAT(gl.ledger(waterCode).account_.balance(), equals(amount));
		QVERIFY_THAT(gl.ledger(currentCode).account_.balance(), equals(emptyBalance));
		QVERIFY_THAT(gl.ledger(electricityCode).account_.balance(), equals(emptyBalance));
		QVERIFY_EXCEPTION_THROWN(
			// Post will fail since the account type is not known.
			gl.repost(TransactionNumber{1u}, changeTxn1),
			TransactionError
		);
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(-amount));
		QVERIFY_THAT(gl.ledger(waterCode).account_.balance(), equals(amount));
		QVERIFY_THAT(gl.ledger(currentCode).account_.balance(), equals(emptyBalance));
		QVERIFY_THAT(gl.ledger(electricityCode).account_.balance(), equals(emptyBalance));
	}

	void repost_originalTransactionReconciled_ShouldThrow()
	{
		const Money emptyBalance{0, 0u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, emptyBalance};
		LedgerAccount cashBook{cash, {}};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, emptyBalance};
		LedgerAccount waterBook{water, {}};
		const AccountCode currentCode{AccountNumber{1005u}, "Current"};
		const Account current{AccountTypes::Asset, currentCode, emptyBalance};
		LedgerAccount currentBook{current, {}};
		const AccountCode electricityCode{AccountNumber{2005u}, "Electricity"};
		const Account electricity{AccountTypes::Expense, electricityCode, emptyBalance};
		LedgerAccount electricityBook{electricity, {}};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook},
			{currentCode, currentBook},
			{electricityCode, electricityBook}
		};
		GeneralLedger gl{ledgers};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{occurredOn, water.code(), cash.code(), amount};
		gl.post(txn);
		const Transaction changeTxn1{occurredOn, electricity.code(), current.code(), amount};
		gl.reconcile({TransactionNumber{1u}});
		QVERIFY_EXCEPTION_THROWN(gl.repost(TransactionNumber{1u}, changeTxn1), TransactionError);
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(-amount));
		QVERIFY_THAT(gl.ledger(waterCode).account_.balance(), equals(amount));
	}
	void repost_transactionUnknownCurrency_ShouldThrow()
	{
		const Money emptyBalance{0, 0u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, emptyBalance};
		LedgerAccount cashBook{cash, {}};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, emptyBalance};
		LedgerAccount waterBook{water, {}};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn{occurredOn, water.code(), cash.code(), amount};
		gl.post(txn);
		const Money newAmount{5, 55u, Iso4217Codes::XXX};
		const QDate newOccurredOn{2018, 6, 3};
		const Transaction newTxn{newOccurredOn,  water.code(), cash.code(), newAmount};
		QVERIFY_EXCEPTION_THROWN(gl.repost(TransactionNumber{1u}, newTxn), TransactionError);
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(-amount));
		QVERIFY_THAT(gl.ledger(waterCode).account_.balance(), equals(amount));
	}

	//}

	//{ add Account Tests

	void add_newAssetAccountPositiveBalance_ShouldBeAdded()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		const auto txnNum{gl.add(cash, this->sang_)};
		QVERIFY_TRUE(txnNum.hasValue());
		QVERIFY_THAT(*txnNum, equals(TransactionNumber(1)));
		// N.B. This account is expected to be automatically created.
		const AccountCode openingCode{AccountNumber{1002u}, "Opening Balances"};
		QVERIFY_THAT(gl.openingAccountCode().valueOrDefault(), equals(openingCode));
		QVERIFY_THAT(gl.ledger(openingCode).account_.code(), equals(openingCode));
		QVERIFY_THAT(
			gl.ledger(openingCode).account_.code().name(),
			equals(openingCode.name())
		);
		QVERIFY_THAT(gl.ledger(openingCode).account_.type(), equals(AccountTypes::Capital));
		QVERIFY_THAT(gl.ledger(openingCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(gl.ledger(openingCode).account_.isClosed(), isFalse());
		QVERIFY_TRUE(gl.hasLedger(cashCode));
		QVERIFY_THAT(gl.ledger(cashCode).account_.code(), equals(cashCode));
		QVERIFY_THAT(gl.ledger(cashCode).account_.type(), equals(AccountTypes::Asset));
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(gl.ledger(cashCode).account_.isClosed(), isFalse());
		QVERIFY_THAT(gl.ledger(cashCode).transactions_.size(), equals(static_cast<size_t>(1)));
		const auto txn{gl.ledger(cashCode).transactions_.at(TransactionNumber{1})};
		QVERIFY_THAT(txn.debiting(), equals(cashCode));
		QVERIFY_THAT(txn.crediting(), equals(openingCode));
		QVERIFY_THAT(*this->sang_, equals(openingCode.number()));
	}
	void add_newAssetAccountNegative_ShouldBeAdded()
	{
		const Money runningBalance{-123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		// N.B. This account is expected to be automatically created.
		const AccountCode openingCode{AccountNumber{1002u}, QStringLiteral("Opening Balance")};
		QVERIFY_THAT(gl.openingAccountCode().valueOrDefault(), equals(openingCode));
		QVERIFY_THAT(gl.ledger(openingCode).account_.code(), equals(openingCode));
		QVERIFY_THAT(gl.ledger(openingCode).account_.type(), equals(AccountTypes::Capital));
		QVERIFY_THAT(gl.ledger(openingCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(gl.ledger(openingCode).account_.isClosed(), isFalse());
		QVERIFY_TRUE(gl.hasLedger(cashCode));
		QVERIFY_THAT(gl.ledger(cashCode).account_.code(), equals(cashCode));
		QVERIFY_THAT(gl.ledger(cashCode).account_.type(), equals(AccountTypes::Asset));
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(gl.ledger(cashCode).account_.isClosed(), isFalse());
		QVERIFY_THAT(gl.ledger(cashCode).transactions_.size(), equals(static_cast<size_t>(1)));
		const auto txn{gl.ledger(cashCode).transactions_.at(TransactionNumber{1})};
		QVERIFY_THAT(txn.debiting(), equals(openingCode));
		QVERIFY_THAT(txn.crediting(), equals(cashCode));
		QVERIFY_THAT(*this->sang_, equals(openingCode.number()));
	}
	void add_newLiabilityAccountPositiveBalance_ShouldBeAdded()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode visaCode{AccountNumber{1001u}, "Visa"};
		const Account visa{AccountTypes::Liability, visaCode, runningBalance};
		GeneralLedger gl{};
		gl.add(visa, this->sang_);
		// N.B. This account is expected to be automatically created.
		const AccountCode openingCode{AccountNumber{1002u}, "Opening Balances"};
		QVERIFY_THAT(gl.openingAccountCode().valueOrDefault(), equals(openingCode));
		QVERIFY_THAT(gl.ledger(openingCode).account_.code(), equals(openingCode));
		QVERIFY_THAT(
			gl.ledger(openingCode).account_.code().name(),
			equals(openingCode.name())
		);
		QVERIFY_THAT(gl.ledger(openingCode).account_.type(), equals(AccountTypes::Capital));
		QVERIFY_THAT(gl.ledger(openingCode).account_.balance(), equals(-runningBalance));
		QVERIFY_THAT(gl.ledger(openingCode).account_.isClosed(), isFalse());
		QVERIFY_TRUE(gl.hasLedger(visaCode));
		QVERIFY_THAT(gl.ledger(visaCode).account_.code(), equals(visaCode));
		QVERIFY_THAT(gl.ledger(visaCode).account_.type(), equals(AccountTypes::Liability));
		QVERIFY_THAT(gl.ledger(visaCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(gl.ledger(visaCode).account_.isClosed(), isFalse());
		QVERIFY_THAT(gl.ledger(visaCode).transactions_.size(), equals(static_cast<size_t>(1)));
		const auto txn{gl.ledger(visaCode).transactions_.at(TransactionNumber{1})};
		QVERIFY_THAT(txn.debiting(), equals(openingCode));
		QVERIFY_THAT(txn.crediting(), equals(visaCode));
		QVERIFY_THAT(*this->sang_, equals(openingCode.number()));
	}
	void add_newLiabilityAccountNegativeBalance_ShouldBeAdded()
	{
		const Money runningBalance{-123, 4500u, Iso4217Codes::USD};
		const AccountCode visaCode{AccountNumber{1001u}, "Visa"};
		const Account visa{AccountTypes::Liability, visaCode, runningBalance};
		GeneralLedger gl{};
		gl.add(visa, this->sang_);
		// N.B. This account is expected to be automatically created.
		const AccountCode openingCode{AccountNumber{1002u}, "Opening Balances"};
		QVERIFY_THAT(gl.openingAccountCode().valueOrDefault(), equals(openingCode));
		QVERIFY_THAT(gl.ledger(openingCode).account_.code(), equals(openingCode));
		QVERIFY_THAT(
			gl.ledger(openingCode).account_.code().name(),
			equals(openingCode.name())
		);
		QVERIFY_THAT(gl.ledger(openingCode).account_.type(), equals(AccountTypes::Capital));
		QVERIFY_THAT(gl.ledger(openingCode).account_.balance(), equals(-runningBalance));
		QVERIFY_THAT(gl.ledger(openingCode).account_.isClosed(), isFalse());
		QVERIFY_TRUE(gl.hasLedger(visaCode));
		QVERIFY_THAT(gl.ledger(visaCode).account_.code(), equals(visaCode));
		QVERIFY_THAT(gl.ledger(visaCode).account_.type(), equals(AccountTypes::Liability));
		QVERIFY_THAT(gl.ledger(visaCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(gl.ledger(visaCode).account_.isClosed(), isFalse());
		QVERIFY_THAT(gl.ledger(visaCode).transactions_.size(), equals(static_cast<size_t>(1)));
		const auto txn{gl.ledger(visaCode).transactions_.at(TransactionNumber{1})};
		QVERIFY_THAT(txn.debiting(), equals(visaCode));
		QVERIFY_THAT(txn.crediting(), equals(openingCode));
		QVERIFY_THAT(*this->sang_, equals(openingCode.number()));
	}
	void add_newAccountSameCodeAsOpeningAccountCode_ShouldBeNextGenerated()
	{
		// This ensures that the user never sees an error when selecting their account code and the
		// opening account code has not yet been generated giving them an error.
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode openingCode{AccountNumber{1002u}, "Opening Balances"};
		const AccountCode cashCode{openingCode.number(), "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode openingPlusOne{
			AccountNumber{openingCode.number().integer() + 1}, openingCode.name()
		};
		QVERIFY_THAT(gl.openingAccountCode().valueOrDefault(), equals(openingPlusOne));
		QVERIFY_THAT(gl.ledger(openingPlusOne).account_.code(), equals(openingPlusOne));
		QVERIFY_THAT(
			gl.ledger(openingPlusOne).account_.code().name(),
			equals(openingPlusOne.name())
		);
		QVERIFY_THAT(gl.ledger(openingPlusOne).account_.type(), equals(AccountTypes::Capital));
		QVERIFY_THAT(gl.ledger(openingPlusOne).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(gl.ledger(openingPlusOne).account_.isClosed(), isFalse());
		QVERIFY_TRUE(gl.hasLedger(cashCode));
		QVERIFY_THAT(gl.ledger(cashCode).account_.code(), equals(cashCode));
		QVERIFY_THAT(gl.ledger(cashCode).account_.type(), equals(AccountTypes::Asset));
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(gl.ledger(cashCode).account_.isClosed(), isFalse());
	}
	void add_newAccountZeroBalance_ShouldNotHaveOpeningBalance()
	{
		const Money runningBalance{0, 0u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		const auto txnNum{gl.add(cash, this->sang_)};
		QVERIFY_FALSE(txnNum.hasValue());
		const AccountCode openingCode{AccountNumber{1002u}, QStringLiteral("Opening Balance")};
		QVERIFY_THAT(gl.openingAccountCode().valueOrDefault(), notEquals(openingCode));
		QVERIFY_FALSE(gl.hasLedger(openingCode));
		QVERIFY_TRUE(gl.hasLedger(cashCode));
		QVERIFY_THAT(gl.ledgers().size(), equals(static_cast<size_t>(1)));
		QVERIFY_THAT(gl.ledger(cashCode).account_.code(), equals(cashCode));
		QVERIFY_THAT(gl.ledger(cashCode).account_.type(), equals(AccountTypes::Asset));
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(gl.ledger(cashCode).account_.isClosed(), isFalse());
	}
	void add_parentIsDifferentAccountType_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode canadianCode{
			AccountNumber{1003u},
			"Canadian Cash",
			cashCode.number()
		};
		const Account canadian{AccountTypes::Liability, canadianCode, runningBalance};
		QVERIFY_EXCEPTION_THROWN(gl.add(canadian, this->sang_), AccountError);
	}
	void add_parentDoesNotExist_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode canadianCode{AccountNumber{1003u}, "Canadian Cash", AccountNumber{1234u}};
		const Account canadian{AccountTypes::Asset, canadianCode, runningBalance};
		QVERIFY_EXCEPTION_THROWN(gl.add(canadian, this->sang_), AccountError);
	}
	void add_duplicateAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		QVERIFY_NO_THROW(gl.add(cash, this->sang_));
		QVERIFY_EXCEPTION_THROWN(gl.add(cash, this->sang_), AccountError);
	}
	void add_emptyAccount_ShouldThrow()
	{
		GeneralLedger gl{};
		QVERIFY_EXCEPTION_THROWN(gl.add(Account{}, this->sang_), AccountError);
	}
	void add_closedAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance, true};
		GeneralLedger gl{};
		QVERIFY_EXCEPTION_THROWN(gl.add(cash, this->sang_), AccountError);
	}

	//}

	//{ remove Account Tests

	void remove_unusedAccount_ShouldBeRemoved()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		QVERIFY_NO_THROW(gl.remove(cashCode));
		QVERIFY_FALSE(gl.hasLedger(cashCode));
		const AccountCode openingCode{AccountNumber{1002u}, QStringLiteral("Opening Balance")};
		const Money epxectedBalance{0, 0u, Iso4217Codes::USD};
		QVERIFY_THAT(
			gl.ledger(openingCode).transactions_.empty(),
			isTrue()
		);
		QVERIFY_THAT(gl.ledger(openingCode).account_.balance(), equals(epxectedBalance));
	}
	void remove_isAParentAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode canadianCode{
			AccountNumber{1003u},
			"Canadian Cash",
			cashCode.number()
		};
		const Account canadian{AccountTypes::Asset, canadianCode, runningBalance};
		gl.add(canadian, this->sang_);
		QVERIFY_EXCEPTION_THROWN(gl.remove(cashCode), AccountError);
		QVERIFY_TRUE(gl.hasLedger(cashCode));
		QVERIFY_TRUE(gl.hasLedger(canadianCode));
	}
	void remove_openingBalanceAccountWithoutTransactions_ShouldBeRemoved()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_); // N.B. Creates the opening balance account.
		QVERIFY_NO_THROW(gl.remove(cashCode));
		QVERIFY_FALSE(gl.hasLedger(cashCode));
		const AccountCode openingCode{AccountNumber{1002u}, QStringLiteral("Opening Balance")};
		QVERIFY_NO_THROW(gl.remove(openingCode));
		QVERIFY_FALSE(gl.hasLedger(openingCode));
	}
	void remove_openingBalanceAccountWithTransactions_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const AccountCode openingCode{AccountNumber{3001u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> transactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					opening.code(),
					cash.code(),
					amount,
					QStringLiteral("Opening Balance"),
					TransactionNumber{1u}
				}
			},
			{
				TransactionNumber{2u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Bill"),
					TransactionNumber{2u}
				}
			},
			{
				TransactionNumber{3u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Bill"),
					TransactionNumber{3u}
				}
			}
		};
		const LedgerAccount cashBook{cash, transactions};
		const LedgerAccount waterBook{
			water,
			{
				{TransactionNumber{2u}, transactions.at(TransactionNumber{2u})},
				{TransactionNumber{3u}, transactions.at(TransactionNumber{3u})}
			}
		};
		const LedgerAccount openingBook{
			opening,
			{
				{TransactionNumber{1u}, transactions.at(TransactionNumber{1u})}
			}
		};
		const map<AccountCode, LedgerAccount> ledgerData{
			{openingCode, openingBook},
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		const set<Account> ledgerAccounts{cash, water, opening};
		GeneralLedger gl{ledgerData, openingCode};
		QVERIFY_EXCEPTION_THROWN(gl.remove(cashCode), AccountError);
	}
	void remove_closedAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		gl.close(cashCode);
		QVERIFY_EXCEPTION_THROWN(gl.remove(cashCode), AccountError);
	}
	void remove_accountDoesNotExist_ShouldThrow()
	{
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		GeneralLedger gl{};
		QVERIFY_EXCEPTION_THROWN(gl.remove(cashCode), AccountError);
	}
	void remove_usedAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn0{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn0{occurredOn0, water.code(), cash.code(), amount, QStringLiteral("Paid Water Bill")};
		const map<TransactionNumber, Transaction> transactions{};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		gl.post(txn0);
		QVERIFY_EXCEPTION_THROWN(gl.remove(cashCode), AccountError);
	}

	//}

	//{ close Account Tests

	void close_hasAccount_ShouldBeClosed()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		gl.close(cashCode);
		QVERIFY_TRUE(gl.ledger(cashCode).account_.isClosed());
	}
	void close_isAParentAccountChildrenNotClosed_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode canadianCode{
			AccountNumber{1003u},
			"Canadian Cash",
			cashCode.number()
		};
		const Account canadian{AccountTypes::Asset, canadianCode, runningBalance};
		gl.add(canadian, this->sang_);
		QVERIFY_EXCEPTION_THROWN(gl.close(cashCode), AccountError);
		QVERIFY_FALSE(gl.ledger(cashCode).account_.isClosed());
		QVERIFY_FALSE(gl.ledger(canadianCode).account_.isClosed());
	}
	void close_isAParentAccountChildrenClosed_ShouldClose()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode canadianCode{
			AccountNumber{1003u},
			"Canadian Cash",
			cashCode.number()
		};
		const Account canadian{AccountTypes::Asset, canadianCode, runningBalance};
		gl.add(canadian, this->sang_);
		gl.close(canadianCode);
		QVERIFY_NO_THROW(gl.close(cashCode));
		QVERIFY_TRUE(gl.ledger(cashCode).account_.isClosed());
		QVERIFY_TRUE(gl.ledger(canadianCode).account_.isClosed());
	}
	void close_accountIsOpeningAccount_ShouldCloseAndReset()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode openingCode{AccountNumber{1002u}, QStringLiteral("Opening Balance")};
		gl.close(openingCode);
		QVERIFY_TRUE(gl.ledger(openingCode).account_.isClosed());
		QVERIFY_THAT(gl.openingAccountCode().valueOrDefault(), notEquals(openingCode));
	}
	void close_missingAccount_ShouldThrow()
	{
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		GeneralLedger gl{};
		QVERIFY_EXCEPTION_THROWN(gl.close(cashCode), AccountError);
	}

	//}

	//{ open Account Tests

	void open_hasAccount_ShouldBeOpened()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		gl.close(cashCode);
		gl.open(cashCode);
		QVERIFY_FALSE(gl.ledger(cashCode).account_.isClosed());
	}
	void open_hasChildParentAccountClosed_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode canadianCode{
			AccountNumber{1003u},
			"Canadian Cash",
			cashCode.number()
		};
		const Account canadian{AccountTypes::Asset, canadianCode, runningBalance};
		gl.add(canadian, this->sang_);
		gl.close(canadianCode);
		gl.close(cashCode);
		QVERIFY_EXCEPTION_THROWN(gl.open(canadianCode), AccountError);
		QVERIFY_TRUE(gl.ledger(cashCode).account_.isClosed());
		QVERIFY_TRUE(gl.ledger(canadianCode).account_.isClosed());
	}
	void open_hasChildParentAccountOpened_ShouldOpen()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode canadianCode{
			AccountNumber{1003u},
			"Canadian Cash",
			cashCode.number()
		};
		const Account canadian{AccountTypes::Asset, canadianCode, runningBalance};
		gl.add(canadian, this->sang_);
		gl.close(canadianCode);
		QVERIFY_NO_THROW(gl.open(canadianCode));
		QVERIFY_FALSE(gl.ledger(cashCode).account_.isClosed());
		QVERIFY_FALSE(gl.ledger(canadianCode).account_.isClosed());
	}
	void open_missingAccount_ShouldThrow()
	{
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		GeneralLedger gl{};
		QVERIFY_EXCEPTION_THROWN(gl.open(cashCode), AccountError);
	}

	//{ recode Account Tests

	void recode_hasAccount_ShouldBeRecoded()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode newCashCode{AccountNumber{1050u}, "Cash (Dollars)"};
		QVERIFY_NO_THROW(gl.recode(cashCode, newCashCode));
		QVERIFY_FALSE(gl.hasLedger(cashCode));
		QVERIFY_THAT(gl.ledger(newCashCode).account_.code(), equals(newCashCode));
		QVERIFY_THAT(gl.ledger(newCashCode).account_.type(), equals(AccountTypes::Asset));
		QVERIFY_THAT(gl.ledger(newCashCode).account_.isClosed(), isFalse());
		QVERIFY_THAT(gl.ledger(newCashCode).account_.balance(), equals(runningBalance));
	}
	void recode_parentAndAll_ShouldBeRecodedAndChildren()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode canadianCode{
			AccountNumber{1003u},
			"Canadian Cash",
			cashCode.number()
		};
		const Account canadian{AccountTypes::Asset, canadianCode, runningBalance};
		gl.add(canadian, this->sang_);
		const AccountCode newCashCode{AccountNumber{1010u}, "New Cash"};
		QVERIFY_NO_THROW(gl.recode(cashCode, newCashCode));
		QVERIFY_FALSE(gl.hasLedger(cashCode));
		QVERIFY_TRUE(gl.hasLedger(newCashCode));
		QVERIFY_TRUE(gl.ledger(canadianCode).account_.code().parent().hasValue());
		QVERIFY_THAT(
			*gl.ledger(canadianCode).account_.code().parent(),
			equals(newCashCode.number())
		);
	}
	void recode_parentRemoved_ShouldBeRecoded()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode canadianCode{
			AccountNumber{1003u},
			"Canadian Cash",
			cashCode.number()
		};
		const Account canadian{AccountTypes::Asset, canadianCode, runningBalance};
		gl.add(canadian, this->sang_);
		const auto txnNum{
			gl.post(
				Transaction{
					QDate{2019, 7, 15},
					cashCode,
					canadianCode,
					Money{15, 5000u, Iso4217Codes::USD},
					QStringLiteral("Testing")
				}
			)
		};
		QVERIFY_TRUE(gl.ledger(canadianCode).account_.code().parent().hasValue());
		QVERIFY_THAT(gl.ledger(canadianCode).transactions_.at(txnNum).debiting(), equals(cashCode));
		QVERIFY_THAT(gl.ledger(canadianCode).transactions_.at(txnNum).crediting(), equals(canadianCode));
		QVERIFY_TRUE(
			gl.ledger(canadianCode).transactions_.at(txnNum).crediting().parent().hasValue()
		);
		const AccountCode newCanadianCode{
			AccountNumber{1003u},
			"Canadian Cash"
		};
		QVERIFY_NO_THROW(gl.recode(canadianCode, newCanadianCode));
		QVERIFY_TRUE(gl.hasLedger(canadianCode));
		QVERIFY_FALSE(gl.ledger(canadianCode).account_.code().parent().hasValue());
		QVERIFY_THAT(gl.ledger(canadianCode).transactions_.at(txnNum).debiting(), equals(cashCode));
		QVERIFY_THAT(gl.ledger(canadianCode).transactions_.at(txnNum).crediting(), equals(newCanadianCode));
		QVERIFY_FALSE(
			gl.ledger(canadianCode).transactions_.at(txnNum).crediting().parent().hasValue()
		);
	}
	void recode_onlyParentChanged_ShouldBeNewParent()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode canadianCode{AccountNumber{1003u}, "Canadian Cash"};
		const Account canadian{AccountTypes::Asset, canadianCode, runningBalance};
		gl.add(canadian, this->sang_);
		QVERIFY_FALSE(gl.ledger(canadianCode).account_.code().parent().hasValue());
		const AccountCode newCanadianCode{
			canadianCode.number(),
			canadianCode.name(),
			cashCode.number()
		};
		QVERIFY_NO_THROW(gl.recode(canadianCode, newCanadianCode));
		QVERIFY_TRUE(gl.hasLedger(canadianCode));
		QVERIFY_TRUE(gl.ledger(canadianCode).account_.code().parent().hasValue());
		QVERIFY_THAT(
			*gl.ledger(canadianCode).account_.code().parent(),
			equals(cashCode.number())
		);
	}
	void recode_ParentDoesNotExist_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode newCashCode{
			cashCode.number(),
			cashCode.name(),
			AccountNumber{9876u}
		};
		QVERIFY_EXCEPTION_THROWN(gl.recode(cashCode, newCashCode), AccountError);
		QVERIFY_TRUE(gl.hasLedger(cashCode));
		QVERIFY_FALSE(gl.ledger(cashCode).account_.code().parent().hasValue());
	}
	void recode_NewParentIsChildAccount_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode canadianCode{AccountNumber{1003u}, "Canadian Cash", cashCode.number()};
		const Account canadian{AccountTypes::Asset, canadianCode, runningBalance};
		gl.add(canadian, this->sang_);
		const AccountCode newCashCode{
			cashCode.number(),
			cashCode.name(),
			canadianCode.number()
		};
		QVERIFY_EXCEPTION_THROWN(gl.recode(cashCode, newCashCode), AccountError);
		QVERIFY_TRUE(gl.hasLedger(cashCode));
		QVERIFY_FALSE(gl.ledger(cashCode).account_.code().parent().hasValue());
	}
	void recode_accountIsOpeningAccount_ShouldBeRecoded()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode newOpeningCode{AccountNumber{4000u}, "Opening Account Balance"};
		const AccountCode openingCode{AccountNumber{1002u}, QStringLiteral("Opening Balance")};
		QVERIFY_NO_THROW(gl.recode(openingCode, newOpeningCode));
		QVERIFY_FALSE(gl.hasLedger(openingCode));
		QVERIFY_THAT(gl.openingAccountCode().valueOrDefault(), equals(newOpeningCode));
		memberFunction(&Account::code, equals(newOpeningCode));
		QVERIFY_THAT(gl.ledger(newOpeningCode).account_.code(), equals(newOpeningCode));
		QVERIFY_THAT(gl.ledger(newOpeningCode).account_.type(), equals(AccountTypes::Capital));
		QVERIFY_THAT(gl.ledger(newOpeningCode).account_.isClosed(), isFalse());
		QVERIFY_THAT(gl.ledger(newOpeningCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(
			gl.ledger(newOpeningCode).account_.code().name(),
			equals(newOpeningCode.name())
		);
	}
	void recode_sameNumberDifferentName_ShouldBeRecoded()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		const AccountCode newCashCode{AccountNumber{1001u}, "Cash (Dollars)"};
		gl.recode(cashCode, newCashCode);
		QVERIFY_THAT(gl.ledger(newCashCode).account_.code(), equals(newCashCode));
		QVERIFY_THAT(gl.ledger(newCashCode).account_.type(), equals(AccountTypes::Asset));
		QVERIFY_THAT(gl.ledger(newCashCode).account_.isClosed(), isFalse());
		QVERIFY_THAT(gl.ledger(newCashCode).account_.balance(), equals(runningBalance));
	}
	void recode_hasAccountWithTransaction_ShouldBeRecodedAndTransactions()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn0{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn0{occurredOn0, water.code(), cash.code(), amount, QStringLiteral("Paid Water Bill")};
		const QDate occurredOn1{2018, 6, 3};
		const Transaction txn1{occurredOn1, cash.code(), water.code(), amount, QStringLiteral("Refund")};
		const map<TransactionNumber, Transaction> transactions{};
		const LedgerAccount cashBook{cash, transactions};
		const LedgerAccount waterBook{water, transactions};
		const map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		gl.post(txn0);
		gl.post(txn1);
		const AccountCode newCashCode{AccountNumber{1050u}, "Cash (Dollars)"};
		gl.recode(cashCode, newCashCode);
		QVERIFY_FALSE(gl.hasLedger(cashCode));
		QVERIFY_THAT(gl.ledger(newCashCode).account_.code(), equals(newCashCode));
		QVERIFY_THAT(
			gl.ledger(newCashCode).transactions_.at(TransactionNumber{1u}).crediting(),
			equals(newCashCode)
		);
		QVERIFY_THAT(
			gl.ledger(newCashCode).transactions_.at(TransactionNumber{2u}).debiting(),
			equals(newCashCode)
		);
		QVERIFY_THAT(
			gl.ledger(water.code()).transactions_.at(TransactionNumber{1u}).crediting(),
			equals(newCashCode)
		);
		QVERIFY_THAT(
			gl.ledger(water.code()).transactions_.at(TransactionNumber{2u}).debiting(),
			equals(newCashCode)
		);
	}
	void recode_accountClosed_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		gl.close(cashCode);
		const AccountCode newCashCode{AccountNumber{1000u}, "Cash (Dollars)"};
		QVERIFY_EXCEPTION_THROWN(gl.recode(cashCode, newCashCode), AccountError);
	}
	void recode_missingAccount_ShouldThrow()
	{
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const AccountCode newCashCode{AccountNumber{1000u}, "Cash (Dollars)"};
		GeneralLedger gl{};
		QVERIFY_EXCEPTION_THROWN(gl.recode(cashCode, newCashCode), AccountError);
	}
	void recode_accountInUse_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode chequeCode{AccountNumber{1003u}, "Cheque"};
		const Account cheque{AccountTypes::Asset, chequeCode, runningBalance};
		GeneralLedger gl{};
		gl.add(cash, this->sang_);
		gl.add(cheque, this->sang_);
		QVERIFY_EXCEPTION_THROWN(gl.recode(cashCode, chequeCode), AccountError);
	}

	//}

	//{ clear Tests

	void clear_hasTransactions_ShouldClearThem()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn0{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn0{occurredOn0, water.code(), cash.code(), amount, QStringLiteral("Paid Water Bill")};
		const QDate occurredOn1{2018, 6, 3};
		const Transaction txn1{occurredOn1, water.code(), cash.code(), amount};
		const map<TransactionNumber, Transaction> transactions{};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		gl.post(txn0);
		gl.post(txn1);
		gl.clear({TransactionNumber{1u}, TransactionNumber{2u}});

		for (const auto& accountLedger : gl.ledgers())
			for (const auto& transaction : accountLedger.second.transactions_)
				QVERIFY_THAT(
					transaction.second,
					memberFunction(
						&Transaction::status,
						equals(TransactionStatuses::Cleared)
					)
				);
	}

	void clear_missingTransactions_ShouldThrow()
	{
		GeneralLedger gl{};
		QVERIFY_EXCEPTION_THROWN(gl.clear({TransactionNumber{1u}}), Error);
	}

	//}

	//{ reconcile Tests

	void reconcile_hasTransactions_ShouldReconcileThem()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn0{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn0{occurredOn0, water.code(), cash.code(), amount, QStringLiteral("Paid Water Bill")};
		const QDate occurredOn1{2018, 6, 3};
		const Transaction txn1{occurredOn1, water.code(), cash.code(), amount};
		const map<TransactionNumber, Transaction> transactions{};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		gl.post(txn0);
		gl.post(txn1);
		gl.reconcile({TransactionNumber{1u}, TransactionNumber{2u}});

		for (const auto& accountLedger : gl.ledgers())
			for (const auto& transaction : accountLedger.second.transactions_)
				QVERIFY_THAT(
					transaction.second,
					memberFunction(
						&Transaction::status,
						equals(TransactionStatuses::Reconciled)
					)
				);
	}

	void reconcile_missingTransactions_ShouldThrow()
	{
		GeneralLedger gl{};
		QVERIFY_EXCEPTION_THROWN(gl.reconcile({TransactionNumber{1u}}), Error);
	}

	//}

	//{ remove Transaction Tests

	void remove_hasTransactions_ShouldRemoveThem()
	{
		const Money runningBalance{0, 0u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn0{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn0{occurredOn0, water.code(), cash.code(), amount, QStringLiteral("Paid Water Bill")};
		const QDate occurredOn1{2018, 6, 3};
		const Transaction txn1{occurredOn1, water.code(), cash.code(), amount};
		const map<TransactionNumber, Transaction> transactions{};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		gl.post(txn0);
		gl.post(txn1);
		gl.remove({TransactionNumber{1u}, TransactionNumber{2u}});
		QVERIFY_THAT(gl.ledger(cashCode).transactions_.empty(), isTrue());
		QVERIFY_THAT(gl.ledger(waterCode).transactions_.empty(), isTrue());
		const Transaction txn2{occurredOn1, water.code(), cash.code(), amount};
		gl.post(txn2);
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(-amount));
		QVERIFY_THAT(gl.ledger(waterCode).account_.balance(), equals(amount));
	}

	void remove_missingTransactions_ShouldThrow()
	{
		GeneralLedger gl{};
		QVERIFY_EXCEPTION_THROWN(gl.remove({TransactionNumber{1u}}), Error);
	}
	void remove_hasReconciledTransaction_ShouldThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn0{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const Transaction txn0{occurredOn0, water.code(), cash.code(), amount, QStringLiteral("Paid Water Bill")};
		const map<TransactionNumber, Transaction> transactions{};
		LedgerAccount cashBook{cash, transactions};
		LedgerAccount waterBook{water, transactions};
		map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		gl.post(txn0);
		gl.reconcile({TransactionNumber{1u}});
		QVERIFY_EXCEPTION_THROWN(gl.remove({TransactionNumber{1u}}), Error);
	}

	//}

	//{ convertTo Tests

	void convertTo_hasTransactions_ShouldConvertBalance()
	{
		const Money runningBalance{0, 0u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn0{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const QDate occurredOn1{2018, 6, 3};
		const map<TransactionNumber, Transaction> transactions{};
		const LedgerAccount cashBook{cash, transactions};
		const LedgerAccount waterBook{water, transactions};
		const map<AccountCode, LedgerAccount> ledgers{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		GeneralLedger gl{ledgers};
		Transaction txn0{occurredOn0, water.code(), cash.code(), amount, QStringLiteral("Paid Water Bill")};
		gl.post(txn0);
		const AccountTransactionKey key{};
		txn0.setNumber(TransactionNumber{1u}, key);
		Transaction txn1{occurredOn1, water.code(), cash.code(), amount};
		gl.post(txn1);
		txn1.setNumber(TransactionNumber{2u}, key);
		Transaction txn2{occurredOn1, water.code(), cash.code(), amount};
		gl.post(txn2);
		txn2.setNumber(TransactionNumber{3u}, key);
		QVERIFY_THAT(
			gl.ledger(cashCode).account_.balance(),
			equals(Money(-150, 7500u, Iso4217Codes::USD))
		);
		QVERIFY_NO_THROW(gl.convertTo(cashCode, Iso4217Codes::PLN, this->sang_));
		QVERIFY_THAT(gl.ledger(cashCode).transactions_.at(*txn0.number()), equals(txn0));
		QVERIFY_THAT(gl.ledger(cashCode).transactions_.at(*txn1.number()), equals(txn1));
		QVERIFY_THAT(gl.ledger(cashCode).transactions_.at(*txn2.number()), equals(txn2));
		const AccountCode foreignCurrencyCode{AccountNumber{1u}, "Foreign Currency Transactions"};
		const Account conversion{
			AccountTypes::Capital,
			foreignCurrencyCode,
			Money{0, 0u, Iso4217Codes::USD} // Use opening account code if set.
		};
		Transaction cashTxn{
			QDate::currentDate(),
			cashCode,
			foreignCurrencyCode,
			Money{0, 0u, Iso4217Codes::PLN},
			QStringLiteral(
				"Account balance currency change. Converted from PLN at the exchange rate of USD 0.27."
			)
		};
		cashTxn.setNumber(TransactionNumber{4u}, key);
		const auto txnBalance{runningBalance - txn0.amount() - txn1.amount() - txn2.amount()};
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(txnBalance));
		QVERIFY_THAT(gl.ledger(foreignCurrencyCode).account_, equals(conversion));
		QVERIFY_TRUE(
			gl.ledger(foreignCurrencyCode).transactions_.count(*cashTxn.number()) == 1
		);
		QVERIFY_THAT(
			gl.ledger(cashCode).transactions_.at(*cashTxn.number()),
			equals(cashTxn)
		);
		QVERIFY_THAT(
			gl.ledger(cashCode).account_.balance(),
			equals(Money(-566, 8200u, Iso4217Codes::PLN))
		);
	}

	void convertTo_accountDoesNotExist_ShouldThrow()
	{
		const Money runningBalance{150, 2500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const LedgerAccount cashBook{cash, {}};
		const map<AccountCode, LedgerAccount> ledgers{};
		GeneralLedger gl{ledgers};
		QVERIFY_EXCEPTION_THROWN(gl.convertTo(cashCode, Iso4217Codes::PLN, this->sang_), AccountError);
		QVERIFY_THAT(*this->sang_, equals(AccountNumber()));
	}

	void convertTo_accountIsClosed_ShouldThrow()
	{
		const Money runningBalance{150, 2500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const LedgerAccount cashBook{cash, {}};
		const map<AccountCode, LedgerAccount> ledgers{{cashCode, cashBook}};
		GeneralLedger gl{ledgers};
		gl.close(cashCode);
		QVERIFY_EXCEPTION_THROWN(
			gl.convertTo(cashCode, Iso4217Codes::PLN, this->sang_),
			TransactionError
		);
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(*this->sang_, equals(AccountNumber{1u}));
	}

	void convertTo_accountIsInThatCurrency_ShouldThrow()
	{
		const Money runningBalance{150, 2500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const LedgerAccount cashBook{cash, {}};
		const map<AccountCode, LedgerAccount> ledgers{{cashCode, cashBook}};
		GeneralLedger gl{ledgers};
		QVERIFY_EXCEPTION_THROWN(
			gl.convertTo(cashCode, cash.balance().code(), this->sang_),
			AccountError
		);
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(*this->sang_, equals(AccountNumber()));
	}

	void convertTo_currencyIsUnknown_ShouldThrow()
	{
		const Money runningBalance{150, 2500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const LedgerAccount cashBook{cash, {}};
		const AccountCode foreignCurrencyCode{AccountNumber{1u}, "Foreign Currency Transactions"};
		this->sang_ = AccountNumber{1u};
		const Account foreignCurrency{AccountTypes::Capital, foreignCurrencyCode, runningBalance};
		const LedgerAccount foreignCurrencyBook{foreignCurrency, {}};
		const map<AccountCode, LedgerAccount> ledgers{
			{cashCode, cashBook},
			{foreignCurrencyCode, foreignCurrencyBook}
		};
		GeneralLedger gl{ledgers, {}};
		QVERIFY_EXCEPTION_THROWN(
			gl.convertTo(cashCode, Iso4217Codes::XXX, this->sang_),
			AccountError
		);
		QVERIFY_THAT(gl.ledger(cashCode).account_.balance(), equals(runningBalance));
		QVERIFY_THAT(*this->sang_, equals(AccountNumber{1u}));
		QVERIFY_THAT(gl.openingAccountCode().hasValue(), isFalse());
	}

	//}

	//{ canRemove Tests

	void canRemove_hasNoTransactions_ShouldBeTrue()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const LedgerAccount cashBook{cash, {}};
		const LedgerAccount waterBook{water, {}};
		const map<AccountCode, LedgerAccount> ledgerData{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		const set<Account> ledgerAccounts{cash, water};
		const GeneralLedger gl{ledgerData};
		QVERIFY_TRUE(gl.canRemove(cashCode));
		QVERIFY_TRUE(gl.canRemove(waterCode));
	}

	void canRemove_hasOnlyOpeningTransaction_ShouldBeTrue()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> transactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					openingCode,
					cash.code(),
					amount,
					QStringLiteral("Opening Balance"),
					TransactionNumber{1u}
				}
			}
		};
		const LedgerAccount cashBook{cash, transactions};
		const LedgerAccount openingBook{opening, transactions};
		const map<AccountCode, LedgerAccount> ledgerData{
			{cashCode, cashBook},
			{openingCode, openingBook}
		};
		const set<Account> ledgerAccounts{cash, opening};
		const GeneralLedger gl{ledgerData, openingCode};
		QVERIFY_TRUE(gl.canRemove(cashCode));
		// Cannot remove the opening account balance account when it has transactions.
		QVERIFY_FALSE(gl.canRemove(openingCode));
	}

	void canRemove_hasTransactions_ShouldBeFalse()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> transactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{1u}
				}
			},
			{
				TransactionNumber{2u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{2u}
				}
			},
			{
				TransactionNumber{3u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{3u}
				}
			}
		};
		const LedgerAccount cashBook{cash, transactions};
		const LedgerAccount waterBook{water, transactions};
		const map<AccountCode, LedgerAccount> ledgerData{
			{waterCode, waterBook},
			{cashCode, cashBook}
		};
		const set<Account> ledgerAccounts{cash, water};
		const GeneralLedger gl{ledgerData};
		QVERIFY_FALSE(gl.canRemove(cashCode));
		QVERIFY_FALSE(gl.canRemove(waterCode));
	}

	//}

	//{ setOpeningAccountCode

	void setOpeningAccountCode_valid_shouldSet()
	{
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, Money{Iso4217Codes::USD}};
		GeneralLedger gl{};
		gl.add(opening, this->sang_);
		QVERIFY_NO_THROW(gl.setOpeningAccountCode(openingCode));
		QVERIFY_THAT(gl.openingAccountCode().valueOrDefault(), equals(openingCode));
	}

	void setOpeningAccountCode_empty_shouldSet()
	{
		GeneralLedger gl{};
		QVERIFY_NO_THROW(gl.setOpeningAccountCode({}));
		QVERIFY_FALSE(gl.openingAccountCode().hasValue());
	}

	void setOpeningAccountCode_bothOpeningAndforeignEmpty_shouldSet()
	{
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, Money{Iso4217Codes::USD}};
		GeneralLedger gl{};
		gl.add(opening, this->sang_);
		QVERIFY_NO_THROW(gl.setOpeningAccountCode(openingCode));
		QVERIFY_THAT(gl.openingAccountCode().valueOrDefault(), equals(openingCode));
		QVERIFY_NO_THROW(gl.setOpeningAccountCode({}));
		QVERIFY_FALSE(gl.openingAccountCode().hasValue());
	}

	void setOpeningAccountCode_invalidAccounttype_shouldThrow()
	{
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Asset, openingCode, Money{Iso4217Codes::USD}};
		GeneralLedger gl{};
		gl.add(opening, this->sang_);
		QVERIFY_EXCEPTION_THROWN(gl.setOpeningAccountCode(openingCode), AccountError);
	}

	void setOpeningAccountCode_missingAccount_shouldThrow()
	{
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, Money{Iso4217Codes::USD}};
		GeneralLedger gl{};
		QVERIFY_EXCEPTION_THROWN(gl.setOpeningAccountCode(openingCode), AccountError);
	}

	//}

	//{ accountCodes

	void accountCodes_hasCodes_ShouldGive()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> transactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{1u}
				}
			},
			{
				TransactionNumber{2u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{2u}
				}
			},
			{
				TransactionNumber{3u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{3u}
				}
			}
		};
		const LedgerAccount cashBook{cash, transactions};
		const LedgerAccount waterBook{water, transactions};
		const LedgerAccount openingBook{opening, {}};
		const map<AccountCode, LedgerAccount> ledgerData{
			{waterCode, waterBook},
			{cashCode, cashBook},
			{openingCode, openingBook}
		};
		const set<Account> ledgerAccounts{cash, water, opening};
		const GeneralLedger gl{ledgerData, openingCode};
		const set<AccountNumber> accounts{
			waterCode.number(),
			cashCode.number(),
			openingCode.number()
		};
		set<AccountCode> codes{};
		QVERIFY_NO_THROW(codes = gl.accountCodes(accounts));
		const set<AccountCode> expected{waterCode, cashCode, openingCode};
		QVERIFY_THAT(codes, equals(expected));
	}

	void accountCodes_hasNoCodes_ShouldBeEmpty()
	{
		const GeneralLedger gl{};
		set<AccountCode> codes{};
		QVERIFY_NO_THROW(codes = gl.accountCodes({}));
		QVERIFY_TRUE(codes.empty());
	}

	void accountCodes_missingAccountNumber_ShouldBeThrow()
	{
		const Money runningBalance{123, 4500u, Iso4217Codes::USD};
		const AccountCode cashCode{AccountNumber{1001u}, "Cash"};
		const Account cash{AccountTypes::Asset, cashCode, runningBalance};
		const AccountCode waterCode{AccountNumber{2001u}, "Water"};
		const Account water{AccountTypes::Expense, waterCode, runningBalance};
		const AccountCode openingCode{AccountNumber{2002u}, "Opening Balances"};
		const Account opening{AccountTypes::Capital, openingCode, runningBalance};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{50, 2500u, Iso4217Codes::USD};
		const map<TransactionNumber, Transaction> transactions{
			{
				TransactionNumber{1u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{1u}
				}
			},
			{
				TransactionNumber{2u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{2u}
				}
			},
			{
				TransactionNumber{3u},
				Transaction{
					occurredOn,
					water.code(),
					cash.code(),
					amount,
					QStringLiteral("Paid Water Bill"),
					TransactionNumber{3u}
				}
			}
		};
		const LedgerAccount cashBook{cash, transactions};
		const LedgerAccount waterBook{water, transactions};
		const LedgerAccount openingBook{opening, {}};
		const map<AccountCode, LedgerAccount> ledgerData{
			{waterCode, waterBook},
			{cashCode, cashBook},
			{openingCode, openingBook}
		};
		const set<Account> ledgerAccounts{cash, water, opening};
		const GeneralLedger gl{ledgerData, openingCode};
		const set<AccountNumber> accounts{
			waterCode.number(),
			cashCode.number(),
			openingCode.number(),
			AccountNumber{1234567890u}
		};
		QVERIFY_EXCEPTION_THROWN(gl.accountCodes(accounts), AccountError);
	}

	//}

public:
	SequentialAccountNumberGenerator sang_{};
};

}}}

QTEST_MAIN(drn::accounting::tests::GeneralLedgerUT)
#include "GeneralLedger-unit-tests.moc"
