Personal Finances Application

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:

  1. 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.
  2. 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.
  3. User (User.h, User.cpp): Klasa modelująca pojedynczego użytkownika, przechowująca jego ID, login, hasło, imię i nazwisko.
  4. 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 pliku users.xml. Dziedziczy po klasie XmlFile.
  5. FinanceManager (FinanceManager.h, FinanceManager.cpp): Zarządza operacjami finansowymi – dodawaniem przychodów i wydatków oraz obliczaniem i wyświetlaniem bilansów.
  6. 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ę.
  7. FinancesFile (FinancesFile.h, FinancesFile.cpp): Odpowiada za zapis i odczyt operacji finansowych (przychodów i wydatków) do odpowiednich plików XML (incomes.xml i outcomes.xml).
  8. 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.
  9. 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.
  10. XmlFile (XmlFile.h, XmlFile.cpp): Klasa bazowa dla klas obsługujących pliki XML, dostarczająca podstawową funkcjonalność związaną z nazwą pliku.
  11. 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.
  12. main.cpp: Plik zawierający funkcję main, która inicjalizuje obiekt PersonalFinancesAplication i 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.

C++
// 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.

C++
// 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.

C++
// 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.

C++
// 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.

C++
// 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.

C++
// 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.

GITHUB

https://github.com/szymonMCS/PersonalFinancesAplication.git

Udostępnij jeśli spodobał Ci się mój projekt