FOSSredder 0.5.0
Loading...
Searching...
No Matches
AnnualPolicy.cpp
Go to the documentation of this file.
1
5
7
8#include <algorithm>
9#include <cctype>
10#include <charconv>
11#include <optional>
12#include <regex>
13#include <utility>
14
16
19 : transactionId_(std::move(transactionId)), reason_(std::move(reason)) {}
20const std::string& AnnualValidationIssue::transactionId() const noexcept { return transactionId_; }
21const std::string& AnnualValidationIssue::reason() const noexcept { return reason_; }
22
23const std::vector<AnnualValidationIssue>& AnnualValidationResult::issues() const noexcept { return issues_; }
24void AnnualValidationIssue::setTransactionId(std::string value) { transactionId_ = std::move(value); }
25void AnnualValidationIssue::setReason(std::string value) { reason_ = std::move(value); }
26void AnnualValidationResult::setIssues(std::vector<AnnualValidationIssue> value) { issues_ = std::move(value); }
27void AnnualValidationResult::addIssue(std::string transactionId, std::string reason) { issues_.emplace_back(std::move(transactionId), std::move(reason)); }
28bool AnnualValidationResult::isValid() const noexcept { return issues_.empty(); }
29bool AnnualValidationResult::hasIssues() const noexcept { return !issues_.empty(); }
30
31bool isValidYear(int year) { return core::domain::Year::isValid(year); }
32bool isValidAnnual(int year, const std::vector<std::string>& analysisIds) { return isValidYear(year) && (!analysisIds.empty()); }
33
34std::string normalizeId(std::string value) {
35 const auto begin = std::find_if_not(value.begin(), value.end(), [](unsigned char c) { return std::isspace(c) != 0; });
36 const auto end = std::find_if_not(value.rbegin(), value.rend(), [](unsigned char c) { return std::isspace(c) != 0; }).base();
37 if (begin >= end) return {};
38 return std::string(begin, end);
39}
40
41bool containsAnalysisId(const std::vector<std::string>& analysisIds, const std::string& analysisId) {
42 const auto normalized = normalizeId(analysisId);
43 return std::find(analysisIds.begin(), analysisIds.end(), normalized) != analysisIds.end();
44}
45
46bool hasUniqueAnalysisIds(const std::vector<std::string>& analysisIds) {
47 std::vector<std::string> unique;
48 unique.reserve(analysisIds.size());
49 for (const auto& rawAnalysisId : analysisIds) {
50 const auto analysisId = normalizeId(rawAnalysisId);
51 if (analysisId.empty()) continue;
52 if (std::find(unique.begin(), unique.end(), analysisId) != unique.end()) return false;
53 unique.push_back(analysisId);
54 }
55 return true;
56}
57
58bool containsAllAnalysisIds(const std::vector<std::string>& analysisIds, const std::vector<std::string>& requiredIds) {
59 return std::all_of(requiredIds.begin(), requiredIds.end(), [&](const std::string& id) { return containsAnalysisId(analysisIds, id); });
60}
61
62void normalizeIds(std::vector<std::string>& values) {
63 std::vector<std::string> normalized;
64 normalized.reserve(values.size());
65 for (auto& rawValue : values) {
66 auto value = normalizeId(std::move(rawValue));
67 if (value.empty()) {
68 continue;
69 }
70 if (std::find(normalized.begin(), normalized.end(), value) != normalized.end()) {
71 continue;
72 }
73 normalized.push_back(std::move(value));
74 }
75 values = std::move(normalized);
76}
77
78std::optional<int> extractBookingYear(const std::string& bookingDate) {
79 const auto trimmed = normalizeId(bookingDate);
80 if (trimmed.empty()) return std::nullopt;
81 static const std::regex isoYearPattern(R"(^(\d{4})-\d{2}-\d{2}$)");
82 static const std::regex dottedYearPattern(R"(^\d{1,2}\.\d{1,2}\.(\d{4})$)");
83 static const std::regex slashYearPattern(R"(^\d{1,2}/\d{1,2}/(\d{4})$)");
84 static const std::regex standaloneYearPattern(R"((?:^|[^0-9])(\d{4})(?:[^0-9]|$))");
85 std::smatch match;
86 auto parseYear = [](const std::string& text) -> std::optional<int> {
87 int value = 0;
88 const auto result = std::from_chars(text.data(), text.data() + text.size(), value);
89 if (result.ec != std::errc{} || result.ptr != text.data() + text.size()) return std::nullopt;
90 return value;
91 };
92 if (std::regex_match(trimmed, match, isoYearPattern) && match.size() > 1) return parseYear(match[1].str());
93 if (std::regex_match(trimmed, match, dottedYearPattern) && match.size() > 1) return parseYear(match[1].str());
94 if (std::regex_match(trimmed, match, slashYearPattern) && match.size() > 1) return parseYear(match[1].str());
95 if (std::regex_search(trimmed, match, standaloneYearPattern) && match.size() > 1) return parseYear(match[1].str());
96 return std::nullopt;
97}
98
99AnnualValidationResult validateTransactionsForYear(int year, const std::vector<std::shared_ptr<core::domain::Transaction>>& transactions) {
101 if (!isValidYear(year)) {
102 result.addIssue("", "annual year is invalid");
103 return result;
104 }
105 for (const auto& transaction : transactions) {
106 if (!transaction) continue;
107 const auto bookingYear = extractBookingYear(transaction->bookingDate());
108 if (!bookingYear) {
109 result.addIssue(transaction->id(), "booking date does not contain a valid year");
110 continue;
111 }
112 if (*bookingYear != year) result.addIssue(transaction->id(), "booking date year does not match annual year");
113 }
114 return result;
115}
116
117} // namespace core::domain::policies::annual
Shared annual validation helpers.
static bool isValid(int value) noexcept
Checks whether a raw year is valid.
Definition Year.cpp:30
const std::string & reason() const noexcept
const std::string & transactionId() const noexcept
const std::vector< AnnualValidationIssue > & issues() const noexcept
void setIssues(std::vector< AnnualValidationIssue > value)
void addIssue(std::string transactionId, std::string reason)
std::optional< int > extractBookingYear(const std::string &bookingDate)
Extracts the year component from a booking date.
AnnualValidationResult validateTransactionsForYear(int year, const std::vector< std::shared_ptr< core::domain::Transaction > > &transactions)
Validates that transaction booking years match the annual year.
bool containsAllAnalysisIds(const std::vector< std::string > &analysisIds, const std::vector< std::string > &requiredIds)
Checks whether a list contains all required analysis ids.
void normalizeIds(std::vector< std::string > &values)
Normalizes and deduplicates analysis ids.
bool isValidAnnual(int year, const std::vector< std::string > &analysisIds)
Checks whether an annual aggregate is structurally valid.
bool containsAnalysisId(const std::vector< std::string > &analysisIds, const std::string &analysisId)
Checks whether an analysis id exists in a list.
bool hasUniqueAnalysisIds(const std::vector< std::string > &analysisIds)
Checks whether all analysis ids are unique.
bool isValidYear(int year)
Checks whether a year is valid for annual aggregates.
std::string normalizeId(std::string value)
Trims leading and trailing whitespace from an id.