Ta aplikacja do zarządzania finansami osobistymi, stworzona w C++, umożliwia użytkownikom śledzenie ich przychodów i wydatków. Dane przechowywane są w plikach XML, co zapewnia ich trwałość między sesjami. Aplikacja została zaprojektowana z wykorzystaniem zasad programowania obiektowego, co przekłada się na jej modułową i zorganizowaną strukturę.

Funkcjonalność aplikacji:
- Zarządzanie użytkownikami:
- Rejestracja nowych użytkowników.
- Logowanie istniejących użytkowników.
- Możliwość zmiany hasła przez zalogowanego użytkownika.
- Zarządzanie finansami:
- Dodawanie nowych przychodów z możliwością wyboru daty (bieżącej lub innej).
- Dodawanie nowych wydatków z możliwością wyboru daty (bieżącej lub innej).
- Wyświetlanie bilansu finansowego dla:
- Bieżącego miesiąca.
- Poprzedniego miesiąca.
- Dowolnie wybranego okresu.
- Interfejs użytkownika:
- Menu konsolowe prowadzące użytkownika przez dostępne opcje.
Aspekty techniczne i struktura:
Aplikacja jest podzielona na kilka kluczowych klas, z których każda odpowiada za określoną część funkcjonalności:
PersonalFinancesAplication(PersonalFinancesAplication.h,PersonalFinancesAplication.cpp): Główna klasa aplikacji, która zarządza interakcją z użytkownikiem poprzez menu oraz koordynuje działanie pozostałych komponentów.UserManager(UserManager.h,UserManager.cpp): Odpowiada za logikę związaną z użytkownikami, taką jak rejestracja, logowanie, zmiana hasła oraz przechowywanie informacji o zalogowanym użytkowniku.User(User.h,User.cpp): Klasa modelująca pojedynczego użytkownika, przechowująca jego ID, login, hasło, imię i nazwisko.UsersFile(UsersFile.h,UsersFile.cpp): Klasa odpowiedzialna za operacje plikowe związane z danymi użytkowników. Zapisuje i odczytuje informacje o użytkownikach z plikuusers.xml. Dziedziczy po klasieXmlFile.FinanceManager(FinanceManager.h,FinanceManager.cpp): Zarządza operacjami finansowymi – dodawaniem przychodów i wydatków oraz obliczaniem i wyświetlaniem bilansów.Operation(Operation.h,Operation.cpp): Reprezentuje pojedynczą operację finansową (przychód lub wydatek), przechowując jej ID, ID użytkownika, datę, opis (item) oraz kwotę.FinancesFile(FinancesFile.h,FinancesFile.cpp): Odpowiada za zapis i odczyt operacji finansowych (przychodów i wydatków) do odpowiednich plików XML (incomes.xmlioutcomes.xml).DatesSupportingMethods(DatesSupportingMethods.h,DatesSupportingMethods.cpp): Zestaw statycznych metod pomocniczych do obsługi dat, np. pobierania bieżącej daty, konwersji daty między formatem string a int, sprawdzania poprawności daty czy obliczania liczby dni w miesiącu.SupportingMethods(SupportingMethods.h,SupportingMethods.cpp): Klasa zawierająca ogólne metody pomocnicze, takie jak konwersje między typami (int na string, string na double), wczytywanie linii czy pojedynczych znaków z konsoli.XmlFile(XmlFile.h,XmlFile.cpp): Klasa bazowa dla klas obsługujących pliki XML, dostarczająca podstawową funkcjonalność związaną z nazwą pliku.Markup(Markup.h,Markup.cpp): Zewnętrzna biblioteka służąca do parsowania i manipulowania plikami XML. Jest to kluczowy element umożliwiający trwałe przechowywanie danych.main.cpp: Plik zawierający funkcjęmain, która inicjalizuje obiektPersonalFinancesAplicationi uruchamia główne menu aplikacji.
Dane użytkowników są przechowywane w pliku users.xml, natomiast transakcje finansowe w incomes.xml i outcomes.xml. Format daty używany w aplikacji to RRRR-MM-DD, a wewnętrznie daty są często reprezentowane jako liczby całkowite (np. 20230515).


Kluczowe fragmenty kodu:
Główna pętla aplikacji znajdująca się w metodzie menu klasy PersonalFinancesAplication steruje przepływem programu w zależności od statusu zalogowania użytkownika i jego wyborów.
// PersonalFinancesAplication.cpp
void PersonalFinancesAplication::menu() {
char choice;
while(true) {
if(!isUserLoggedIn()) { // Sprawdzenie, czy użytkownik jest zalogowany
choice = chooseOptionFromMainMenu(); // Wybór opcji z menu głównego
switch(choice) {
case '1' : userRegistration(); break;
case '2' : userLogin(); break;
case '9' : exit(0); break;
default:
std::cout << std::endl << "Incorrect choice, try again." << std::endl << std::endl;
system("pause");
break;
}
} else { // Menu dla zalogowanego użytkownika
choice = chooseOptionFromUserMenu();
switch(choice) {
case '1' : addIncome(); break;
case '2' : addOutcome(); break;
case '3' : currentMonthBalance(); break;
case '4' : previousMonthBalance(); break;
case '5' : anyPeriodBalance(); break;
case '6' : passwordChange(); break;
case '7' : userLogOut(); break;
default:
std::cout << std::endl << "Incorrect choice, try again." << std::endl << std::endl;
system("pause");
break;
}
}
}
}Rejestracja nowego użytkownika w UserManager::userRegistration obejmuje zebranie danych i zapisanie ich do pliku XML.
// UserManager.cpp
void UserManager::userRegistration() {
User user = enterNewUserData(); // Pobranie danych nowego użytkownika
users.push_back(user); // Dodanie użytkownika do wektora w pamięci
usersFile.addUserToFile(user); // Zapisanie użytkownika do pliku XML
std::cout << std::endl << "Account has been succesfully created" << std::endl << std::endl;
system("pause");
}Dodawanie operacji finansowej (przychodu lub wydatku) w FinanceManager polega na zebraniu szczegółów transakcji i zapisaniu jej do odpowiedniego pliku XML. Metoda setOperationData jest tutaj kluczowa.
// FinanceManager.cpp
void FinanceManager::addIncome() {
Operation income;
int newIncomeId = financesFile.getLastIncomeId() + 1; // Pobranie ostatniego ID i inkrementacja
std::cout << "Add Income with todays date - press '1' " << std::endl;
std::cout << "" << std::endl;
std::cout << "Add Income with different date - press '2' " << std::endl;
char choice;
choice = SupportingMethods::readChar();
switch(choice) {
case '1' :
income = setOperationData(DatesSupportingMethods::getCurrentDate(), newIncomeId); // Użycie bieżącej daty
break;
case '2' :
income = setOperationData(DatesSupportingMethods::getDifferentDate(), newIncomeId); // Użycie innej daty
break;
default :
std::cout << "Invalid choice" << std::endl;
// Dodano return aby uniknąć błędu kompilacji, jeśli wybór jest nieprawidłowy
return;
}
financesFile.addOperationToFile(income, "incomes.xml"); // Zapis do pliku
std::cout << "Income has been added succesfully... " << std::endl;
system("pause");
}
Operation FinanceManager::setOperationData(int date, int newId) {
Operation operation;
operation.setOperationId(newId);
operation.setUserId(LOGGED_IN_USER_ID); // ID zalogowanego użytkownika
operation.setDate(date); // Ustawienie daty
std::cout << "Enter item: ";
operation.setItem(SupportingMethods::readLine()); // Wczytanie opisu
std::string amount;
do {
std::cout << "Enter amount: ";
amount = SupportingMethods::readLine(); // Wczytanie kwoty
if (!SupportingMethods::doesHaveOnlyDigits(amount)) { // Podstawowa walidacja kwoty
std::cout << "It is not number, try again" << std::endl;
}
} while(!SupportingMethods::doesHaveOnlyDigits(amount));
operation.setAmount(SupportingMethods::stringToDouble(amount)); // Konwersja kwoty na double
return operation;
}Obliczanie i wyświetlanie bilansu w FinanceManager::balance demonstruje pobieranie operacji z zadanego okresu, sortowanie ich i prezentację użytkownikowi.
// FinanceManager.cpp
void FinanceManager::balance(std::string startDate, std::string endDate) {
std::vector <Operation> balanceIncomes;
std::vector <Operation> balanceOutcomes;
double incomesSum = 0.00;
double outcomesSum = 0.00;
double balance = 0.00;
// ... (pominięto potwierdzenie dat przez użytkownika) ...
// Pobranie operacji z plików XML dla danego okresu i użytkownika
balanceIncomes = financesFile.getOperationsFromPeriod("incomes.xml", startDate, endDate, LOGGED_IN_USER_ID);
balanceOutcomes = financesFile.getOperationsFromPeriod("outcomes.xml", startDate, endDate, LOGGED_IN_USER_ID);
system("cls");
if(!balanceIncomes.empty()) {
std::cout << "== Incomes == " << std::endl << std::endl;
incomesSum = displaySortedVector(balanceIncomes); // Wyświetlenie i zsumowanie przychodów
std::cout << std::endl << "Incomes sum = " << incomesSum << std::endl;
} else {
std::cout << "Logged in user has no incomes in this period " << std::endl;
}
if(!balanceOutcomes.empty()) {
std::cout << std::endl << std::endl << "== Outcomes == " << std::endl << std::endl;
outcomesSum = displaySortedVector(balanceOutcomes); // Wyświetlenie i zsumowanie wydatków
std::cout << std::endl << "Outcomes sum = " << outcomesSum << std::endl << std::endl;
} else {
std::cout << "Logged in user has no outcomes in this period " << std::endl;
}
balance = incomesSum - outcomesSum; // Obliczenie bilansu
if (incomesSum < outcomesSum) {
std::cout << "Balance: -" << -balance << std::endl;
} else {
std::cout << "Balance: " << balance << std::endl;
}
system("pause");
}
// Metoda pomocnicza do wyświetlania posortowanych operacji
double FinanceManager::displaySortedVector (std::vector <Operation> input) {
double sum = 0.00;
std::sort(input.begin(), input.end(), Operation::compareByDate); // Sortowanie operacji wg daty
for (auto operation : input) {
std::cout << "Date: " << DatesSupportingMethods::intDateToString(operation.getDate()) << ", "
<< "Item: " << operation.getItem() << ", "
<< "Amount: " << operation.getAmount() << std::endl;
sum += operation.getAmount();
}
return sum;
}Przykładem użycia biblioteki CMarkup do zapisu danych do pliku XML jest metoda addUserToFile w klasie UsersFile.
// UsersFile.cpp
void UsersFile::addUserToFile(User user) {
CMarkup xml; // Obiekt biblioteki CMarkup
bool fileExists = xml.Load(XmlFile::getFileName().c_str()); // Próba załadowania istniejącego pliku
if (!fileExists) { // Jeśli plik nie istnieje, tworzony jest główny element "USERS"
xml.AddElem("USERS");
xml.IntoElem();
} else { // Jeśli plik istnieje, następuje nawigacja do głównego elementu
xml.ResetPos();
if (!xml.FindElem("USERS")) {
std::cout << "Error during opening file" << std::endl;
return;
}
xml.IntoElem();
}
xml.AddElem("USER"); // Dodanie elementu "USER"
xml.IntoElem(); // Wejście do elementu "USER"
// Dodanie podelementów z danymi użytkownika
xml.AddElem("userId", SupportingMethods::intToStringConversion(user.getUserId()));
xml.AddElem("login", user.getLogin());
xml.AddElem("password", user.getPassword());
xml.AddElem("name", user.getName());
xml.AddElem("surname", user.getSurname());
xml.Save("users.xml"); // Zapisanie zmian do pliku
}Obsługa dat, w tym pobieranie aktualnej daty, jest realizowana w klasie DatesSupportingMethods.
// DatesSupportingMethods.cpp
int DatesSupportingMethods::getCurrentDate() {
std::string stringDate;
std::time_t t = std::time(0); // Pobranie aktualnego czasu
std::tm* now = std::localtime(&t); // Konwersja na strukturę tm
// Formatowanie daty do stringa RRRRMMDD
stringDate += std::to_string(now->tm_year + 1900);
stringDate += (now -> tm_mon + 1 < 10 ? "0" : "") + std::to_string(now -> tm_mon + 1);
stringDate += (now -> tm_mday < 10 ? "0" : "") + std::to_string(now -> tm_mday);
int currentDate = std::atoi(stringDate.c_str()); // Konwersja stringa na int
return currentDate;
}Aplikacja demonstruje podstawowe techniki programowania w C++ zorientowane obiektowo, obsługę plików XML oraz zarządzanie prostą bazą danych użytkowników i transakcji finansowych w środowisku konsolowym.