/*******************************************************************************
***  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 <QDate>
using ::QDate;
#include <QObject>
using ::QObject;
// using Q_OBJECT
#include <QTest>
// using QTEST_MAIN
#include <qt-mocks/Matchers.h>
using qt_mocks::equals;
// using QVERIFY_EXCEPTION_THROWN
// using QVERIFY_THAT

#include <map>
using std::map;

#include <accounting/Account.h>
using drn::accounting::Account;
using drn::accounting::AccountCode;
using drn::accounting::AccountTypes;
#include <accounting/AccountingErrors.h>
using drn::accounting::TransactionError;
#include <accounting/Transaction.h>
using drn::accounting::Transaction;
using drn::accounting::TransactionNumber;
#include <internal/TransactionPoster.h>
using drn::accounting::internal::TransactionPoster;


namespace drn
{
namespace accounting
{
namespace internal
{
namespace tests
{

struct TransactionPosterUT : QObject
{
	Q_OBJECT

public:

	TransactionPosterUT() :
		txnNum_{1u}
	{}

	TransactionNumber txnNum_;

private slots:
	//{ perform Debit Asset Tests

	void perform_debitAssetCreditLiability_ShouldBeIncreaseAssetIncreaseLiability()
	{
		Account debit{AccountTypes::Asset, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{
			AccountTypes::Liability,
			AccountCode{AccountNumber{2u}, "credit"},
			{10, 0u, Iso4217Codes::USD}
		};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{133, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitAssetCreditRevenue_ShouldBeIncreaseAssetIncreaseRevenue()
	{
		Account debit{AccountTypes::Asset, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Revenue, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitAssetCreditExpense_ShouldBeIncreaseAssetDecreaseExpense()
	{
		Account debit{AccountTypes::Asset, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Expense, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitAssetCreditCapital_ShouldBeIncreaseAssetIncreaseCapital()
	{
		Account debit{AccountTypes::Asset, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Capital, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}

	//}

	//{ perform Debit Liability Tests

	void perform_debitLiabilityCreditAsset_ShouldBeDecreaseLiabilityDecreaseAsset()
	{
		Account debit{AccountTypes::Liability, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Asset, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitLiabilityCreditRevenue_ShouldBeDecreaseLiabilityIncreaseRevenue()
	{
		Account debit{AccountTypes::Liability, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Revenue, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitLiabilityCreditExpense_ShouldBeDecreaseLiabilityDecreaseExpense()
	{
		Account debit{AccountTypes::Liability, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Expense, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitLiabilityCreditCapital_ShouldBeDecreaseLiabilityDecreaseCapital()
	{
		Account debit{AccountTypes::Liability, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Expense, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}

	//}

	//{ perform Debit Revenue Tests

	void perform_debitRevenueCreditAsset_ShouldBeDecreaseRevenueDecreaseAsset()
	{
		Account debit{AccountTypes::Revenue, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Asset, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitRevenueCreditLiability_ShouldBeDecreaseRevenueIncreaseLiability()
	{
		Account debit{AccountTypes::Revenue, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Liability, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitRevenueCreditExpense_ShouldBeDecreaseRevenueDecreaseExpense()
	{
		Account debit{AccountTypes::Revenue, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Expense, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitRevenueCreditCapital_ShouldBeDecreaseRevenueIncreaseCapital()
	{
		Account debit{AccountTypes::Revenue, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Capital, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}

	//}

	//{ perform Debit Capital Tests

	void perform_debitCapitalCreditAsset_ShouldBeDecreaseCapitalDecreaseAsset()
	{
		Account debit{AccountTypes::Capital, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Asset, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitCapitalCreditLiability_ShouldBeDecreaseCapitalIncreaseLiability()
	{
		Account debit{AccountTypes::Capital, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Liability, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitCapitalCreditRevenue_ShouldBeDecreaseCapitalIncreaseRevenue()
	{
		Account debit{AccountTypes::Capital, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Revenue, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}
	void perform_debitCapitalCreditExpense_ShouldBeDecreaseCapitalDecreaseExpense()
	{
		Account debit{AccountTypes::Capital, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Expense, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const auto creditDebitTxn{
			TransactionPoster::perform(
				debit,
				credit,
				Transaction{occurredOn, debit.code(), credit.code(), amount},
				this->txnNum_
			)
		};
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
		QVERIFY_THAT(*creditDebitTxn.first.number(), equals(this->txnNum_));
		QVERIFY_THAT(*creditDebitTxn.second.number(), equals(this->txnNum_));
	}

	//}

	void perform_debitDoesNotMatchTransaction_ShouldThrow()
	{
		Account debit{AccountTypes::Asset, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Asset, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		Account missing{AccountTypes::Asset, AccountCode{AccountNumber{10u}, "blarg"}, Iso4217Codes::USD};
		QVERIFY_EXCEPTION_THROWN(
			TransactionPoster::perform(missing, credit, transaction, this->txnNum_),
			TransactionError
		);
	}

	void perform_creditDoesNotMatchTransaction_ShouldThrow()
	{
		Account debit{AccountTypes::Asset, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Asset, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		Account missing{AccountTypes::Asset, AccountCode{AccountNumber{10u}, "blarg"}, Iso4217Codes::USD};
		QVERIFY_EXCEPTION_THROWN(
			TransactionPoster::perform(debit, missing, transaction, this->txnNum_),
			TransactionError
		);
	}

	void perform_debitUnknownCreditAsset_ShouldThrow()
	{
		Account debit{};
		Account credit{AccountTypes::Asset, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		QVERIFY_EXCEPTION_THROWN(
			TransactionPoster::perform(debit, credit, transaction, this->txnNum_),
			TransactionError
		);
	}

	void perform_debitAssetCreditUnknown_ShouldThrow()
	{
		Account debit{AccountTypes::Asset, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		QVERIFY_EXCEPTION_THROWN(
			TransactionPoster::perform(debit, credit, transaction, this->txnNum_),
			TransactionError
		);
	}

	//{ reverse Debit Asset Tests

	void reverse_debitAssetCreditLiability_ShouldBeDecreaseAssetDecreaseLiability()
	{
		Account debit{AccountTypes::Asset, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Liability, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitAssetCreditRevenue_ShouldBeDecreaseAssetDecreaseRevenue()
	{
		Account debit{AccountTypes::Asset, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Revenue, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitAssetCreditExpense_ShouldBeDecreaseAssetIncreaseExpense()
	{
		Account debit{AccountTypes::Asset, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Expense, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitAssetCreditCapital_ShouldBeDecreaseAssetDecreaseCapital()
	{
		Account debit{AccountTypes::Asset, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Capital, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{-123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}

	//}

	//{ reverse Debit Liability Tests

	void reverse_debitLiabilityCreditAsset_ShouldBeIncreaseLiabilityIncreaseAsset()
	{
		Account debit{AccountTypes::Liability, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Asset, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitLiabilityCreditRevenue_ShouldBeIncreaseLiabilityDecreaseRevenue()
	{
		Account debit{AccountTypes::Liability, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Revenue, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitLiabilityCreditExpense_ShouldBeIncreaseLiabilityIncreaseExpense()
	{
		Account debit{AccountTypes::Liability, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Expense, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitLiabilityCreditCapital_ShouldBeIncreaseLiabilityIncreaseCapital()
	{
		Account debit{AccountTypes::Liability, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Expense, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}

	//}

	//{ reverse Debit Revenue Tests

	void reverse_debitRevenueCreditAsset_ShouldBeIncreaseRevenueIncreaseAsset()
	{
		Account debit{AccountTypes::Revenue, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Asset, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitRevenueCreditLiability_ShouldBeIncreaseRevenueDecreaseLiability()
	{
		Account debit{AccountTypes::Revenue, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Liability, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitRevenueCreditExpense_ShouldBeIncreaseRevenueIncreaseExpense()
	{
		Account debit{AccountTypes::Revenue, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Expense, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitRevenueCreditCapital_ShouldBeIncreaseRevenueDecreaseCapital()
	{
		Account debit{AccountTypes::Revenue, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Capital, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}

	//}

	//{ reverse Debit Capital Tests

	void reverse_debitCapitalCreditAsset_ShouldBeIncreaseCapitalIncreaseAsset()
	{
		Account debit{AccountTypes::Capital, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Asset, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitCapitalCreditLiability_ShouldBeIncreaseCapitalDecreaseLiability()
	{
		Account debit{AccountTypes::Capital, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Liability, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitCapitalCreditRevenue_ShouldBeIncreaseCapitalDecreaseRevenue()
	{
		Account debit{AccountTypes::Capital, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Revenue, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{-123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}
	void reverse_debitCapitalCreditExpense_ShouldBeIncreaseCapitalIncreaseExpense()
	{
		Account debit{AccountTypes::Capital, AccountCode{AccountNumber{1u}, "debit"}, Iso4217Codes::USD};
		Account credit{AccountTypes::Expense, AccountCode{AccountNumber{2u}, "credit"}, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		const Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		TransactionPoster::reverse(debit, credit, transaction);
		const Money expectedDebitAmount{123, 4500u, Iso4217Codes::USD};
		const Money expectedCreditAmount{123, 4500u, Iso4217Codes::USD};
		QVERIFY_THAT(debit.balance(), equals(expectedDebitAmount));
		QVERIFY_THAT(credit.balance(), equals(expectedCreditAmount));
	}

	//}

	//{ recodeAccount Tests

	void recodeAccount_hasCurrentDebitCode_ShouldUpdateDebitCode()
	{
		const AccountCode debitCode{AccountNumber{1u}, "debit"};
		Account debit{AccountTypes::Capital, debitCode, Iso4217Codes::USD};
		const AccountCode creditCode{AccountNumber{2u}, "credit"};
		Account credit{AccountTypes::Expense, creditCode, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		const AccountCode newCode{AccountNumber{1001u}, "New Code"};
		TransactionPoster::recodeAccount(debitCode, transaction, newCode);
		QVERIFY_THAT(transaction.occurredOn(), equals(occurredOn));
		QVERIFY_THAT(transaction.debiting(), equals(newCode));
		QVERIFY_THAT(transaction.crediting(), equals(creditCode));
		QVERIFY_THAT(transaction.amount(), equals(amount));
		QVERIFY_THAT(transaction.status(), equals(TransactionStatuses::Unreconciled));
	}
	void recodeAccount_hasCurrentCreditCode_ShouldUpdateCreditCode()
	{
		const AccountCode debitCode{AccountNumber{1u}, "debit"};
		Account debit{AccountTypes::Capital, debitCode, Iso4217Codes::USD};
		const AccountCode creditCode{AccountNumber{2u}, "credit"};
		Account credit{AccountTypes::Expense, creditCode, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		const AccountCode newCode{AccountNumber{1001u}, "New Code"};
		TransactionPoster::recodeAccount(creditCode, transaction, newCode);
		QVERIFY_THAT(transaction.occurredOn(), equals(occurredOn));
		QVERIFY_THAT(transaction.debiting(), equals(debitCode));
		QVERIFY_THAT(transaction.crediting(), equals(newCode));
		QVERIFY_THAT(transaction.amount(), equals(amount));
		QVERIFY_THAT(transaction.status(), equals(TransactionStatuses::Unreconciled));
	}
	void recodeAccount_missingCode_ShouldThrow()
	{
		const AccountCode debitCode{AccountNumber{1u}, "debit"};
		Account debit{AccountTypes::Capital, debitCode, Iso4217Codes::USD};
		const AccountCode creditCode{AccountNumber{2u}, "credit"};
		Account credit{AccountTypes::Expense, creditCode, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		const AccountCode newCode{AccountNumber{1001u}, "New Code"};
		QVERIFY_EXCEPTION_THROWN(
			TransactionPoster::recodeAccount(newCode, transaction, creditCode),
			TransactionError
		);
	}
	void recodeAccount_newDebitCodeSameAsCreditCode_ShouldThrow()
	{
		const AccountCode debitCode{AccountNumber{1u}, "debit"};
		Account debit{AccountTypes::Capital, debitCode, Iso4217Codes::USD};
		const AccountCode creditCode{AccountNumber{2u}, "credit"};
		Account credit{AccountTypes::Expense, creditCode, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		QVERIFY_EXCEPTION_THROWN(
			TransactionPoster::recodeAccount(debitCode, transaction, creditCode),
			TransactionError
		);
	}
	void recodeAccount_newCreditCodeSameAsDebitCode_ShouldThrow()
	{
		const AccountCode debitCode{AccountNumber{1u}, "debit"};
		Account debit{AccountTypes::Capital, debitCode, Iso4217Codes::USD};
		const AccountCode creditCode{AccountNumber{2u}, "credit"};
		Account credit{AccountTypes::Expense, creditCode, Iso4217Codes::USD};
		const QDate occurredOn{2018, 6, 2};
		const Money amount{123, 4500u, Iso4217Codes::USD};
		Transaction transaction{occurredOn, debit.code(), credit.code(), amount};
		QVERIFY_EXCEPTION_THROWN(
			TransactionPoster::recodeAccount(creditCode, transaction, debitCode),
			TransactionError
		);
	}

	//}
};

}}}}

QTEST_MAIN(drn::accounting::internal::tests::TransactionPosterUT)
#include "TransactionPoster-unit-tests.moc"
