changeset 900:5c3c58aa9448

IN 32: Moved the survey completion logic out of the container of surveys and into the object itself where it belongs. Added an upsert member function to the container of surveys.
author John Schneiderman <JohnMS@member.fsf.org>
date Wed, 05 Oct 2022 18:05:26 +0200
parents 563ef254bc3a
children 4501ccdb0cfc
files src/file-storage/internal/XmlBudgetFile.cpp src/file-storage/unit-tests/BudgetFileContents.cpp src/navigation/internal/BudgetBankLedgers.cpp src/surveying/external/surveying/ReconciledSurvey.cpp src/surveying/external/surveying/ReconciledSurvey.h src/surveying/unit-tests/ReconciledSurvey-unit-tests.cpp
diffstat 6 files changed, 101 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/src/file-storage/internal/XmlBudgetFile.cpp	Mon Oct 03 19:37:42 2022 +0200
+++ b/src/file-storage/internal/XmlBudgetFile.cpp	Wed Oct 05 18:05:26 2022 +0200
@@ -643,7 +643,8 @@
 					},
 					reconciliationElement.date_
 				},
-				{} // TODO: distribution amounts and validity value
+				{}, // TODO: distribution amounts
+				{} // TODO: validity value
 			}
 		);
 	}
--- a/src/file-storage/unit-tests/BudgetFileContents.cpp	Mon Oct 03 19:37:42 2022 +0200
+++ b/src/file-storage/unit-tests/BudgetFileContents.cpp	Wed Oct 05 18:05:26 2022 +0200
@@ -687,12 +687,13 @@
 						Money{1234, 5600u, Iso4217Codes::PLN},
 						QDate{2022, 8, 31}
 					},
+					{},
 					{}
 				}
 			},
 			{
 				c1stSavings,
-				ReconciledSurvey{ReconciledBankAccount{c1stSavings, Iso4217Codes::PLN}, {}}
+				ReconciledSurvey{c1stSavings, Iso4217Codes::PLN}
 			}
 		}
 	};
--- a/src/navigation/internal/BudgetBankLedgers.cpp	Mon Oct 03 19:37:42 2022 +0200
+++ b/src/navigation/internal/BudgetBankLedgers.cpp	Wed Oct 05 18:05:26 2022 +0200
@@ -928,13 +928,9 @@
 
 	try
 	{
-		this->surveys_.completed(
-			ReconciledSurvey{
-				ReconciledBankAccount{ba, reconciledBalance, completedOn},
-				unusedBudget
-			},
-			reconciledBalance
-		);
+		auto survey{previousSurvey.valueOr(ReconciledSurvey{ba, ledger.account_.balance().code()})};
+		survey.completed(completedOn, reconciledBalance, unusedBudget);
+		this->surveys_.upsert(survey);
 	}
 	catch (const exception&)
 	{
--- a/src/surveying/external/surveying/ReconciledSurvey.cpp	Mon Oct 03 19:37:42 2022 +0200
+++ b/src/surveying/external/surveying/ReconciledSurvey.cpp	Wed Oct 05 18:05:26 2022 +0200
@@ -29,6 +29,8 @@
 using drn::surveying::ReconciledSurvey;
 using drn::surveying::ReconciledSurveys;
 
+#include <pecunia/Codes.h>
+using pecunia::currency::Iso4217Codes;
 #include <pecunia/Math.h>
 using pecunia::math::sum;
 #include <QCryptographicHash>
@@ -65,15 +67,17 @@
 {
 
 QString calculateValidity(
-	ReconciledBankAccount reconciled,
-	map<BudgetItemIdentifier, Money> distribution
+	const BankAccount& ba,
+	const Money& balance,
+	const Optional<QDate>& reconciledOn,
+	const map<BudgetItemIdentifier, Money>& distribution
 )
 {
-	QString value{presentationText(reconciled.bankAccount())};
-	value = value % presentationText(reconciled.balance(), false, true);
+	QString value{presentationText(ba)};
+	value = value % presentationText(balance, false, true);
 
-	if (reconciled.reconciledOn().hasValue())
-		value = value % reconciled.reconciledOn()->toString(DateFormat::ISODate);
+	if (reconciledOn.hasValue())
+		value = value % reconciledOn->toString(DateFormat::ISODate);
 
 	for (const auto& itemAmount : distribution)
 		value = value % presentationText(itemAmount.first)
@@ -85,6 +89,10 @@
 
 //{ ReconciledSurvey
 
+ReconciledSurvey::ReconciledSurvey(BankAccount ba, const Iso4217Codes& currency) :
+	ReconciledSurvey{ReconciledBankAccount{ba, currency}, {}, {}}
+{}
+
 ReconciledSurvey::ReconciledSurvey(
 	ReconciledBankAccount reconciled,
 	map<BudgetItemIdentifier, Money> distribution,
@@ -96,7 +104,14 @@
 {
 	if (this->validity_.hasValue())
 	{
-		const auto validityCheck{calculateValidity(this->reconciled_, this->distribution_)};
+		const auto validityCheck{
+			calculateValidity(
+				this->reconciled_.bankAccount(),
+				this->reconciled_.balance(),
+				this->reconciled_.reconciledOn(),
+				this->distribution_
+			)
+		};
 
 		if (this->validity_ != validityCheck)
 			throw Error{
@@ -123,6 +138,32 @@
 	return this->validity_;
 }
 
+void ReconciledSurvey::completed(
+	const QDate& completedOn,
+	const Money& reconciledBalance,
+	const map<BudgetItemIdentifier, Money>& distribution
+)
+{
+	const auto distributionBalance{sum(this->reconciled_.balance().code(), distribution)};
+	const auto ba{this->reconciled_.bankAccount()};
+
+	if (reconciledBalance != distributionBalance)
+		throw BankError{
+			ba.bank_,
+			QObject::tr("The surveyed balance %1 does not match the unused %2 balance.")
+				.arg(presentationText(reconciledBalance))
+				.arg(presentationText(distributionBalance))
+		};
+// 	this->validity_ = calculateValidity(
+// 		this->reconciled_.bankAccount(),
+// 		reconciledBalance,
+// 		completedOn,
+// 		distribution
+// 	);
+	this->reconciled_.reconcile(completedOn, reconciledBalance);
+	this->distribution_ = move(distribution);
+}
+
 bool drn::surveying::operator==(const ReconciledSurvey& lhs, const ReconciledSurvey& rhs)
 {
 	return tie(lhs.reconciled(), lhs.distribution()) == tie(rhs.reconciled(), rhs.distribution());
@@ -188,24 +229,14 @@
 	return account->second;
 }
 
-void ReconciledSurveys::completed(ReconciledSurvey survey, const Money& reconciledBalance)
+void ReconciledSurveys::upsert(ReconciledSurvey survey)
 {
-	const auto expectedBalance{sum(survey.reconciled().balance().code(), survey.distribution())};
 	const auto ba{survey.reconciled().bankAccount()};
 
-	if (reconciledBalance != expectedBalance)
-		throw BankError{
-			ba.bank_,
-			QObject::tr("The surveyed balance %1 does not match the expected %2.")
-				.arg(presentationText(reconciledBalance))
-				.arg(presentationText(expectedBalance))
-		};
-	// TODO: survey.verification_ = calculateVerification();
-
 	if (this->count(ba) == 0)
 	{
 		const auto emplaced{(*this).emplace(ba, move(survey))};
-		assert(emplaced.second && "The new survey should always be inserted.");
+		assert(emplaced.second && "A new survey should always be inserted.");
 	}
 	else
 		(*this)[ba] = move(survey);
--- a/src/surveying/external/surveying/ReconciledSurvey.h	Mon Oct 03 19:37:42 2022 +0200
+++ b/src/surveying/external/surveying/ReconciledSurvey.h	Wed Oct 05 18:05:26 2022 +0200
@@ -56,16 +56,22 @@
 public:
 	ReconciledSurvey() = default;
 	/**
-	 * @brief Full and partial initialisation constructor.
+	 * @brief Partial initialisation constructor for creating a new survey.
+	 *
+	 * @param ba The bank account that is reconciled.
+	 * @param currency The currency held within the bank account.
+	 */
+	ReconciledSurvey(banking::BankAccount ba, const pecunia::currency::Iso4217Codes& currency);
+	/**
+	 * @brief Full initialisation constructor.
 	 *
 	 * @exception Error When a supplied validity value does not match the calculated value.
 	 *
 	 * @param reconciled The bank account that was reconciled.
 	 * @param distribution The distribution of the balance in the account.
-	 * @param validity When supplied, the value that represents an object whose values were verified
-	 * after having completed a survey. This is in lieu of verifying the balances upon object
-	 * creation since exchange rates change over time. If not supplied, no verification is
-	 * performed, i.e. the object is partially initialised.
+	 * @param validity The value that represents an object whose values were verified after having
+	 * completed a survey. This is in lieu of verifying the balances upon object creation since
+	 * exchange rates change over time preventing verification in the future.
 	 */
 	ReconciledSurvey(
 		banking::ReconciledBankAccount reconciled,
@@ -73,7 +79,7 @@
 			budgeting::BudgetItemIdentifier,
 			pecunia::currency::Money
 		> distribution,
-		foundation::Optional<::QString> validity = {}
+		foundation::Optional<::QString> validity
 	);
 	const banking::ReconciledBankAccount& reconciled() const noexcept;
 	const std::map<
@@ -84,6 +90,23 @@
 	 * @brief The value that represents a valid object after having completed a survey.
 	 */
 	const foundation::Optional<::QString>& validity() const noexcept;
+	/**
+	 * @brief Updates the survey with the completed values.
+	 *
+	 * @exception BankError When the reconciled bank balance does not match the amount of
+	 * distributed unused funds.
+	 *
+	 * @param completedOn The date the balance was confirmed.
+	 * @param reconciledBalance The balance as reported in the bank account.
+	 * @param distribution The distribution of unused funds (bank balance) in the bank account.
+	 *
+	 * @post Upon successful survey completion, the validity value is computed and stored in the object.
+	 */
+	void completed(
+		const ::QDate& completedOn,
+		const pecunia::currency::Money& reconciledBalance,
+		const std::map<budgeting::BudgetItemIdentifier, pecunia::currency::Money>& distribution
+	);
 };
 
 class DRN_SURVEYING_EXPORT ReconciledSurveys : std::map<banking::BankAccount, ReconciledSurvey>
@@ -100,13 +123,12 @@
 	using std::map<banking::BankAccount, ReconciledSurvey>::count;
 	using std::map<banking::BankAccount, ReconciledSurvey>::at;
 	using std::map<banking::BankAccount, ReconciledSurvey>::size;
-
 	foundation::Optional<ReconciledSurvey> find(const banking::BankAccount& ba) const;
 	foundation::Optional<ReconciledSurvey> find(
 		const banking::BankName& bn,
 		const accounting::AccountNumber& an
 	) const;
-	void completed(ReconciledSurvey survey, const pecunia::currency::Money& reconciledBalance);
+	void upsert(ReconciledSurvey survey);
 };
 
 DRN_SURVEYING_EXPORT bool operator==(
--- a/src/surveying/unit-tests/ReconciledSurvey-unit-tests.cpp	Mon Oct 03 19:37:42 2022 +0200
+++ b/src/surveying/unit-tests/ReconciledSurvey-unit-tests.cpp	Wed Oct 05 18:05:26 2022 +0200
@@ -111,9 +111,20 @@
 
 	void constructor_PartialInit_ShouldSet()
 	{
-		const ReconciledSurvey survey{this->reconciled_, this->distribution_};
-		QVERIFY_THAT(survey.distribution(), equals(this->distribution_));
-		QVERIFY_THAT(survey.reconciled(), equals(this->reconciled_));
+		const ReconciledSurvey survey{
+			this->reconciled_.bankAccount(),
+			this->reconciled_.balance().code()
+		};
+		QVERIFY_THAT(
+			survey.reconciled(),
+			equals(
+				ReconciledBankAccount{
+					this->reconciled_.bankAccount(),
+					this->reconciled_.balance().code()
+				}
+			)
+		);
+		QVERIFY_TRUE(survey.distribution().empty());
 		QVERIFY_FALSE(survey.validity().hasValue());
 	}