From 16663d830f003f0553f32af152100c0f9d147d45 Mon Sep 17 00:00:00 2001 From: Freezepop Date: Sun, 8 Dec 2024 23:25:01 +0300 Subject: [PATCH] add finance_management --- src/finance_management/DB_handler.java | 64 +++++ src/finance_management/Main.java | 16 ++ src/finance_management/authentication.java | 48 ++++ src/finance_management/processsing.java | 235 ++++++++++++++++++ .../processsing_handler.java | 198 +++++++++++++++ src/finance_management/user_handler.java | 36 +++ 6 files changed, 597 insertions(+) create mode 100644 src/finance_management/DB_handler.java create mode 100644 src/finance_management/Main.java create mode 100644 src/finance_management/authentication.java create mode 100644 src/finance_management/processsing.java create mode 100644 src/finance_management/processsing_handler.java create mode 100644 src/finance_management/user_handler.java diff --git a/src/finance_management/DB_handler.java b/src/finance_management/DB_handler.java new file mode 100644 index 0000000..7591b39 --- /dev/null +++ b/src/finance_management/DB_handler.java @@ -0,0 +1,64 @@ +package finance_management; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +public class DB_handler { + + private static final String DATABASE_PATH = "jdbc:sqlite:finance_management.db"; + + public static Connection connect() { + Connection cursor; + try { + cursor = DriverManager.getConnection(DATABASE_PATH); + } catch (SQLException error) { + throw new RuntimeException(error); + } + createTables(cursor); + return cursor; + } + + private static void createTables(Connection cursor) { + + String sql_users = """ + CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + uuid TEXT NOT NULL DEFAULT (lower(hex(randomblob(16)))), + name TEXT NOT NULL UNIQUE, + passwd TEXT NOT NULL, + wallet INT NOT NULL DEFAULT 0 + ); + """; + + String sql_categories = """ + CREATE TABLE categories ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + uuid TEXT NOT NULL DEFAULT (lower(hex(randomblob(16)))), + user_uuid TEXT NOT NULL, + name TEXT NOT NULL, + limit_value INT, + FOREIGN KEY (user_uuid) REFERENCES users (uuid) + ); + """; + + String sql_transactions = """ + CREATE TABLE transactions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + category_uuid TEXT NOT NULL, + value INT NOT NULL, + income BOOLEAN NOT NULL, + FOREIGN KEY (category_uuid) REFERENCES users (uuid) + ); + """; + + try (Statement stmt = cursor.createStatement()) { + stmt.execute(sql_users); + stmt.execute(sql_categories); + stmt.execute(sql_transactions); + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + } +} diff --git a/src/finance_management/Main.java b/src/finance_management/Main.java new file mode 100644 index 0000000..6130a43 --- /dev/null +++ b/src/finance_management/Main.java @@ -0,0 +1,16 @@ +package finance_management; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Scanner; + +public class Main { + public static void main(String[] args) throws SQLException { + + Connection cursor = DB_handler.connect(); + Scanner scanner = new Scanner(System.in); + + new authentication().run(scanner, cursor); + + } +} diff --git a/src/finance_management/authentication.java b/src/finance_management/authentication.java new file mode 100644 index 0000000..ab03fc7 --- /dev/null +++ b/src/finance_management/authentication.java @@ -0,0 +1,48 @@ +package finance_management; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Scanner; + +public class authentication { + + boolean run = true; + + public void run(Scanner scanner, Connection cursor) throws SQLException { + while (this.run) { + System.out.println(""" + Вас приветствует программа управления личными финансами + У Вас уже есть учетная запись? + 1 - Да + 2 - Нет + exit - Выход из программы"""); + String answer = scanner.nextLine(); + + if (answer.equals("1")) { + System.out.println("Введите логин:\n"); + String name = scanner.nextLine(); + System.out.println("Введите пароль:\n"); + String passwd = scanner.nextLine(); + int auth_result = user_handler.getUser(cursor, name, passwd); + if (auth_result == 1) { + new processsing().run(scanner, cursor, name); + } + System.out.println(auth_result); + } + else if (answer.equals("2")) { + System.out.println("Введите логин:\n"); + String name = scanner.nextLine(); + System.out.println("Введите пароль:\n"); + String passwd = scanner.nextLine(); + System.out.printf("Вы зарегистрировались как пользователь %s. Теперь Вы можете авторизоваться выбрав 1.\n\n", name); + user_handler.createUser(cursor, name, passwd); + } + else if (answer.equals("exit")) { + run = false; + } + else { + System.out.println("Требуется ввести 1, 2 или exit для выхода."); + } + } + } +} diff --git a/src/finance_management/processsing.java b/src/finance_management/processsing.java new file mode 100644 index 0000000..2a11160 --- /dev/null +++ b/src/finance_management/processsing.java @@ -0,0 +1,235 @@ +package finance_management; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import java.util.Scanner; + +public class processsing { + + boolean run = true; + + public void run(Scanner scanner, Connection cursor, String name) throws SQLException { + + System.out.printf("\n%s, Вы успешно авторизовались.\n\n", name); + String user_uuid = processsing_handler.getUserUUID(cursor, name); + processsing_handler.calculateWallet(cursor, user_uuid); + + String menu = (""" + \nВыберите операцию: + 1 - Записать доход + 2 - Записать расход + 3 - Создать категорию и задать ее бюджет + 4 - Выполнить расчет бюджета + 5 - Подкрутить бюджет для категории + 6 - СБП по имени + 7 - Выход из аккаунта, возврат к меню авторизации + """); + + while (run) { + + + + System.out.println(menu); + + String answer = scanner.nextLine(); + + if (answer.equals("1")) { + System.out.println("Укажите категорию и значение в формате \"Еда: 300\"."); + String[] income_value = scanner.nextLine().split(":"); + String category = income_value[0]; + String value = income_value[1].strip(); + String category_uuid = processsing_handler.checkCategory(cursor, category, user_uuid); + if (category_uuid != null) { + processsing_handler.writeIncome(cursor, category_uuid, value); + System.out.printf("Вы успешно записали доход %s по категории %s.\n", value, category); + } + else { + System.out.println("Операция не удалась, такой категории не существует. Создайте ее выбрав соответствующий пункт меню.\n"); + } + processsing_handler.calculateWallet(cursor, user_uuid); + } + + else if (answer.equals("2")) { + System.out.println("Укажите категорию и значение в формате \"Еда: 300\"."); + String[] income_value = scanner.nextLine().split(":"); + String category = income_value[0]; + String value = income_value[1].strip(); + String category_uuid = processsing_handler.checkCategory(cursor, category, user_uuid); + if (category_uuid != null) { + processsing_handler.writeExpense(cursor, category_uuid, value); + System.out.printf("Вы успешно записали расход %s по категории %s.\n", value, category); + } + else { + System.out.println("Операция не удалась, такой категории не существует. Создайте ее выбрав соответствующий пункт меню.\n"); + } + processsing_handler.calculateWallet(cursor, user_uuid); + } + + else if (answer.equals("3")) { + System.out.println("Укажите имя категории и ее бюджет, например, \"Еда: 30000\".\nЕсли это категория доходов, то можно просто написать \"Бонус\" или \"Зарплата\"."); + String create_category = scanner.nextLine(); + if (create_category.contains(":")){ + String[] create_category_with_limit = create_category.split(":"); + String category_name = create_category_with_limit[0]; + String limit_value = create_category_with_limit[1].strip(); + String category_uuid = processsing_handler.checkCategory(cursor, category_name, user_uuid); + if (category_uuid != null) { + System.out.println("Операция не удалась, такая категория уже существует. Используйте ее для записи расходов и доходов.\n"); + } + else { + processsing_handler.createCategory(cursor, user_uuid, category_name, limit_value); + System.out.printf("Вы успешно создали категорию %s. Теперь Вы можете записывать доходы и расходы по ней.\n", category_name); + } + } + else { + String category_uuid = processsing_handler.checkCategory(cursor, create_category.strip(), user_uuid); + if (category_uuid != null) { + System.out.println("Операция не удалась, такая категория уже существует. Используйте ее для записи расходов и доходов.\n"); + } + else { + processsing_handler.createCategory(cursor, user_uuid, create_category.strip(), "-1"); + System.out.printf("Вы успешно создали категорию %s. Теперь Вы можете записывать доходы и расходы по ней.\n", create_category.strip()); + } + } + } + + else if (answer.equals("4")) { + processsing_handler.calculateWallet(cursor, user_uuid); + int total_income = processsing_handler.calculateIncomeTotal(cursor, user_uuid); + int total_expense = processsing_handler.calculateExpenseTotal(cursor, user_uuid); + + List results_income; + try { + results_income = processsing_handler.calculateIncomeTotalByCategory(cursor, user_uuid); + } catch (SQLException e) { + throw new RuntimeException(e); + } + + if (results_income != null) { + StringBuilder calcIncomeBuilder = new StringBuilder(); + for (Object[] result : results_income) { + String category_name = (String) result[0]; + int category_income = (Integer) result[1]; + + calcIncomeBuilder.append(String.format( + " %s: %d\n", + category_name, category_income + )); + } + String calcIncome = calcIncomeBuilder.toString(); + System.out.printf("\nОбщий доход: %d\n", total_income); + System.out.printf("Доходы по категориям:\n%s", calcIncome); + } + + List results_expense; + try { + results_expense = processsing_handler.calculateExpenseTotalByCategory(cursor, user_uuid); + } catch (SQLException e) { + throw new RuntimeException(e); + } + + if (results_expense != null) { + StringBuilder calcExpenseBuilder = new StringBuilder(); + for (Object[] result : results_expense) { + String category_name = (String) result[0]; + int category_expense = (Integer) result[1]; + int limit_value = (Integer) result[2]; + if (limit_value != -1){ + int remaining_budget = limit_value - category_expense; + if (remaining_budget < 0) { + + calcExpenseBuilder.append(String.format( + " %s: %d, Оставшийся бюджет: %d (Внимание, лимиты бюджета превышены!)\n", + category_name, category_expense, remaining_budget + )); + } + else { + calcExpenseBuilder.append(String.format( + " %s: %d, Оставшийся бюджет: %d\n", + category_name, category_expense, remaining_budget + )); + } + } + else { + String remaining_budget = "Бюджет для данной категории не установлен"; + + calcExpenseBuilder.append(String.format( + " %s: %d, Оставшийся бюджет: %s\n", + category_name, category_expense, remaining_budget + )); + } + + } + String calcExpense = calcExpenseBuilder.toString(); + System.out.printf("\nОбщие расходы: %d\n", total_expense); + System.out.printf("Бюджет по категориям:\n%s", calcExpense); + + if(total_income < total_expense){ + System.out.printf("\nВнимание! Ваши общие расходы превысили общие доходы: %d\n", total_income - total_expense); + } + else { + System.out.printf("\nВаш баланс: %d\n", total_income - total_expense); + } + + } + } + else if (answer.equals("5")) { + System.out.println("Укажите имя категории и ее бюджет, например, \"Еда: 30000\".\nЕсли Вы хоите обрать категорию из рассчетов, то можно написать \"Бонус\" или \"Зарплата\" без указания лимитов или назначить лимит -1, например, \"Бонус: -1\"."); + String create_category = scanner.nextLine(); + if (create_category.contains(":")){ + String[] create_category_with_limit = create_category.split(":"); + String category_name = create_category_with_limit[0]; + String limit_value = create_category_with_limit[1].strip(); + String category_uuid = processsing_handler.checkCategory(cursor, category_name, user_uuid); + if (category_uuid != null) { + processsing_handler.updateCategory(cursor, user_uuid, category_name, limit_value); + System.out.printf("Вы успешно установили лимит для категории %s.\n", category_name); + } + else { + System.out.println("Операция не удалась, такой категории не существует. Используйте соответствующий пункт меню для категории.\n"); + } + } + else { + String category_uuid = processsing_handler.checkCategory(cursor, create_category.strip(), user_uuid); + if (category_uuid != null) { + processsing_handler.updateCategory(cursor, user_uuid, create_category.strip(), "-1"); + System.out.printf("Вы успешно установили лимит для категории %s.\n", create_category.strip()); + } + else { + System.out.println("Операция не удалась, такой категории не существует. Используйте соответствующий пункт меню для категории.\n"); + } + } + } + else if (answer.equals("6")) { + System.out.println("Здравствуйте, введите имя друга в нашей системе и сумму, которую хотите перевести в форме \"Иван: 10000\".\n"); + String friend_input = scanner.nextLine(); + if (friend_input.contains(":")){ + String[] friend_input_data = friend_input.split(":"); + String friend_name = friend_input_data[0]; + String value = friend_input_data[1].strip(); + try { + Integer.parseInt(value); + int result = processsing_handler.sbp(cursor, user_uuid, friend_name, value); + if (result == 1) { + System.out.println("Операция прошла успешно.\n"); + } + else { + System.out.println("Операция завершилась неудачно, попробуйте еще раз позднее.\n"); + } + + } catch (NumberFormatException e) { + System.out.println("Извините, но формат ввода неправильный.\n"); + } + + } + processsing_handler.calculateWallet(cursor, user_uuid); + + } + + else if (answer.equals("7")) { + run = false; + } + } + } +} \ No newline at end of file diff --git a/src/finance_management/processsing_handler.java b/src/finance_management/processsing_handler.java new file mode 100644 index 0000000..3ddc27e --- /dev/null +++ b/src/finance_management/processsing_handler.java @@ -0,0 +1,198 @@ +package finance_management; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + + +public interface processsing_handler { + + static String getUserUUID(Connection cursor, String name) throws SQLException { + + String insertSQL = "SELECT uuid FROM users WHERE name = ?"; + + try (PreparedStatement pstmt = cursor.prepareStatement(insertSQL)) { + pstmt.setString(1, name); + ResultSet rs = pstmt.executeQuery(); + return rs.getString("uuid"); + } + } + + static void writeIncome(Connection cursor, String category_uuid, String value) throws SQLException { + + String sql = "INSERT INTO transactions (category_uuid, value, income) VALUES (?, ?, true)"; + + try (PreparedStatement insert_pstmt = cursor.prepareStatement(sql)) { + insert_pstmt.setString(1, category_uuid); + insert_pstmt.setString(2, value); + insert_pstmt.executeUpdate(); + } + } + + static void writeExpense(Connection cursor, String category_uuid, String value) throws SQLException { + + String sql = "INSERT INTO transactions (category_uuid, value, income) VALUES (?, ?, false)"; + + try (PreparedStatement insert_pstmt = cursor.prepareStatement(sql)) { + insert_pstmt.setString(1, category_uuid); + insert_pstmt.setString(2, value); + insert_pstmt.executeUpdate(); + } + } + + static String checkCategory(Connection cursor, String category, String user_uuid) throws SQLException { + + String insertSQL = "SELECT uuid FROM categories WHERE name = ? AND user_uuid = ?"; + + try (PreparedStatement pstmt = cursor.prepareStatement(insertSQL)) { + pstmt.setString(1, category); + pstmt.setString(2, user_uuid); + ResultSet rs = pstmt.executeQuery(); + return rs.getString("uuid"); + } + } + + static void createCategory(Connection cursor, String user_uuid, String category_name, String limit_value) throws SQLException { + + String sql = "INSERT INTO categories (user_uuid, name, limit_value) VALUES (?, ?, ?)"; + + try (PreparedStatement insert_pstmt = cursor.prepareStatement(sql)) { + insert_pstmt.setString(1, user_uuid); + insert_pstmt.setString(2, category_name); + insert_pstmt.setInt(3, Integer.parseInt(limit_value)); + insert_pstmt.executeUpdate(); + } + } + + static void updateCategory(Connection cursor, String user_uuid, String category_name, String limit_value) throws SQLException { + + String sql_update = "UPDATE categories SET limit_value = ? WHERE user_uuid = ? AND name = ?"; + + try (PreparedStatement update_pstmt = cursor.prepareStatement(sql_update)) { + update_pstmt.setInt(1, Integer.parseInt(limit_value)); + update_pstmt.setString(2, user_uuid); + update_pstmt.setString(3, category_name); + update_pstmt.executeUpdate(); + } + } + + static void calculateWallet(Connection cursor, String user_uuid) throws SQLException { + + String sql = "SELECT SUM(CASE WHEN t.income THEN t.value ELSE -t.value END) AS balance FROM users u LEFT JOIN categories c ON u.uuid = c.user_uuid LEFT JOIN transactions t ON c.uuid = t.category_uuid WHERE u.uuid = ?"; + + try (PreparedStatement pstmt = cursor.prepareStatement(sql)) { + pstmt.setString(1, user_uuid); + ResultSet rs = pstmt.executeQuery(); + int balance = rs.getInt("balance"); + + String sql_update = "UPDATE users SET wallet = ? WHERE uuid = ?"; + + try (PreparedStatement update_pstmt = cursor.prepareStatement(sql_update)) { + update_pstmt.setInt(1, balance); + update_pstmt.setString(2, user_uuid); + update_pstmt.executeUpdate(); + } + } + } + + static int calculateIncomeTotal(Connection cursor, String user_uuid) throws SQLException { + + String sql = "SELECT SUM(t.value) AS total_income FROM transactions t JOIN categories c ON t.category_uuid = c.uuid JOIN users u ON c.user_uuid = u.uuid WHERE t.income = TRUE AND u.uuid = ?"; + + try (PreparedStatement pstmt = cursor.prepareStatement(sql)) { + pstmt.setString(1, user_uuid); + ResultSet rs = pstmt.executeQuery(); + return rs.getInt("total_income"); + } + } + + static List calculateIncomeTotalByCategory(Connection cursor, String user_uuid) throws SQLException { + + String sql = "SELECT c.name AS category_name, SUM(t.value) AS category_income FROM transactions t JOIN categories c ON t.category_uuid = c.uuid JOIN users u ON c.user_uuid = u.uuid WHERE t.income = TRUE AND u.uuid = ? GROUP BY c.name"; + + List result = new ArrayList<>(); + try (PreparedStatement pstmt = cursor.prepareStatement(sql)) { + pstmt.setString(1, user_uuid); + try (ResultSet rs = pstmt.executeQuery()) { + while (rs.next()) { + result.add(new Object[]{ + rs.getString("category_name"), + rs.getInt("category_income")}); + } + } + } + if (!result.isEmpty()) { + return result; + } + else { + return null; + } + } + + static int calculateExpenseTotal(Connection cursor, String user_uuid) throws SQLException { + + String sql = "SELECT SUM(t.value) AS total_expense FROM transactions t JOIN categories c ON t.category_uuid = c.uuid JOIN users u ON c.user_uuid = u.uuid WHERE t.income = FALSE AND u.uuid = ?"; + + try (PreparedStatement pstmt = cursor.prepareStatement(sql)) { + pstmt.setString(1, user_uuid); + ResultSet rs = pstmt.executeQuery(); + return rs.getInt("total_expense"); + } + } + + static List calculateExpenseTotalByCategory(Connection cursor, String user_uuid) throws SQLException { + + String sql = "SELECT c.name AS category_name, SUM(t.value) AS category_expense, c.limit_value FROM transactions t JOIN categories c ON t.category_uuid = c.uuid JOIN users u ON c.user_uuid = u.uuid WHERE t.income = FALSE AND u.uuid = ? GROUP BY c.name, c.limit_value"; + + List result = new ArrayList<>(); + try (PreparedStatement pstmt = cursor.prepareStatement(sql)) { + pstmt.setString(1, user_uuid); + try (ResultSet rs = pstmt.executeQuery()) { + while (rs.next()) { + result.add(new Object[]{ + rs.getString("category_name"), + rs.getInt("category_expense"), + rs.getInt("limit_value")}); + } + } + } + if (!result.isEmpty()) { + return result; + } + else { + return null; + } + } + + static int sbp(Connection cursor, String user_uuid, String friend_name, String value) throws SQLException { + + String sql = "SELECT wallet FROM users WHERE uuid = ?"; + + try (PreparedStatement pstmt = cursor.prepareStatement(sql)) { + pstmt.setString(1, user_uuid); + ResultSet rs = pstmt.executeQuery(); + int current_balance = rs.getInt("wallet"); + if (current_balance >= Integer.parseInt(value)){ + String get_friend_sql = "SELECT uuid FROM users WHERE name = ?"; + try (PreparedStatement pstmt_get_friend = cursor.prepareStatement(get_friend_sql)) { + pstmt_get_friend.setString(1, friend_name); + ResultSet rs_get_friend = pstmt_get_friend.executeQuery(); + String friend_uuid = rs_get_friend.getString("uuid"); + createCategory(cursor, friend_uuid, "bro_bonus", "-1"); + createCategory(cursor, user_uuid, "bro_bonus", "-1"); + String category_friend_uuid = checkCategory(cursor, "bro_bonus", friend_uuid); + String category_my_uuid = checkCategory(cursor, "bro_bonus", user_uuid); + writeIncome(cursor, category_friend_uuid, value); + writeExpense(cursor, category_my_uuid, value); + return 1; + } + } + else { + return 0; + } + } + } +} diff --git a/src/finance_management/user_handler.java b/src/finance_management/user_handler.java new file mode 100644 index 0000000..2ada189 --- /dev/null +++ b/src/finance_management/user_handler.java @@ -0,0 +1,36 @@ +package finance_management; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Objects; + +public interface user_handler { + static void createUser(Connection cursor, String name, String passwd) throws SQLException { + String insertSQL = "INSERT INTO users (name, passwd) VALUES (?, ?)"; + + try (PreparedStatement pstmt = cursor.prepareStatement(insertSQL)) { + pstmt.setString(1, name); + pstmt.setString(2, passwd); + pstmt.executeUpdate(); + } + } + static int getUser(Connection cursor, String name, String passwd) throws SQLException { + String insertSQL = "SELECT name, passwd FROM users WHERE name = ?"; + + try (PreparedStatement pstmt = cursor.prepareStatement(insertSQL)) { + pstmt.setString(1, name); + ResultSet rs = pstmt.executeQuery(); + if (rs.getString("name") == null) { + return 0; + } + else if (Objects.equals(rs.getString("name"), name) && Objects.equals(rs.getString("passwd"), passwd)) { + return 1; + } + else { + return 2; + } + } + } +}