Welcome to lark3ri.com

Financial management program with c++

28.11.2020

Financial management program which I program for more detailed financial management. Includes only the features I actually need so I can keep it simple to manage my finances. Includes enough features, as example, working command and command line parameters are:

./program.exe database-path=data start-date="15.11.2020 02:00:00" end-date="15.11.2020 02:00:03" credit-first no-color delete=1 it deletes the first result between those dates.

I use Cygwin to run this program on Windows. I have not tested functionality on any other platform. Default database path is data. You can find more default settings to be set in std::map<std::string, std::string> sett, line 57. Here is a picture of the program:


main.cpp
/*
	Copyright(C) 2020 Lari Varjonen <[email protected]>

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	version 2 as published by the Free Software Foundation.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110 - 1301, USA.

	Version: 1.2
*/

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <iomanip>

#ifdef _WIN32
#include <windows.h>
#pragma execution_character_set("utf-8")
#endif

//#define _DEBUG



class SETT
{
	const std::string settings_fp = "settings";
	const std::string translation_dp = "translations/";

	bool veccmp(const std::vector<std::string> &arr, const std::string &val)
	{
		for (std::vector<std::string>::const_iterator it = arr.begin(); it != arr.end(); it++)
		{
			if (it->compare(val) == 0)
			{
				return true;
			}
		}
		return false;
	}

	std::vector<std::string> cmd_arg = {
		"SV_NEW",
		"SV_DELETE"
	};

	std::map<std::string, std::string> sett = {
		{"SV_DATABASE_PATH",    "data"},
		{"SV_LANGUAGE",         "en"},
		{"SV_TIMEZONE",         "+0"},
		{"SV_DATE_ORDER",       "DMYhms"},
		{"SV_CREDIT_COLOR",     "1;32"},
		{"SV_DEBIT_COLOR",      "1;35"},
		{"SV_ORDER_BY_DATE",    "0"},
		{"SV_DEBIT_FIRST",      "0"},
		{"SV_CREDIT_FIRST",     "0"},
		{"SV_ONLY_DEBIT",       "0"},
		{"SV_ONLY_CREDIT",      "0"},
		{"SV_NO_COLOR",         "0"},
		{"SV_NEW",              "0"},
		{"SV_DELETE",           ""},
		{"SV_START_DATE",       ""},
		{"SV_END_DATE",         ""},
		{"SV_FORMAT",           "%d.%m.%Y %H:%M:%S"},
		{"SV_ORDER",            "cod-dat-nam-amo-ref-des-tzo"},
		{"SV_CURRENCY",         "$"},
		{"SV_CURRENCY_SIDE",    "1"},

		{"T_CREDIT_OR_DEBIT",   "Credit or Debit?(credit/debit): "},
		{"T_CREDIT_OR_DEBIT_A", "credit"},
		{"T_DAY",               "Day:\t"},
		{"T_MONTH",             "Month:\t"},
		{"T_YEAR",              "Year:\t"},
		{"T_HOURS",             "Hours:\t"},
		{"T_MINUTES",           "Minutes:\t"},
		{"T_SECONDS",           "Seconds:\t"},
		{"T_NAME",              "Name:\t\t"},
		{"T_AMOUNT",            "Amount:\t\t"},
		{"T_REF",               "Ref:\t\t"},
		{"T_DESCRIPTION",       "Description:\t"},
		{"T_TIMEZONE",          "Timezone:\t"},
		{"T_DATE",              "Date:\t\t"},
		{"T_BALANCE",           "Balance: "},
		{"T_CREDIT",            "CREDIT"},
		{"T_DEBIT",             "DEBIT"}
	};

	public:
		SETT();
		std::string get(const std::string &key);
		bool getb(const std::string& key);
		bool add(std::string key, const std::string &val);
};

SETT::SETT()
{
	// Read settings file
	std::ifstream file(settings_fp);
	if (!file.good())
	{
		return;
	}
	if (file.peek() == std::ifstream::traits_type::eof())
	{
		return;
	}

	std::string current_line;
	while (std::getline(file, current_line))
	{
		if (current_line.empty()) continue;

		std::string key;
		std::string val;

		int index = current_line.find("=");
		if (index != -1)
		{
			key = current_line.substr(0, index);
			val = current_line.substr(index + 1, current_line.size() - 1);
		}
		else
		{
			key = current_line;
			val = "1";
		}

		for (unsigned int i = 0; i < key.size(); i++)
		{
			char c = key.at(i);
			// abc --> ABC
			if (c > 96 && c < 123)
			{
				c -= 32;
				key.at(i) = c;
			}
		}

		if (!veccmp(cmd_arg, key) && key.at(0) == 'S' && key.at(1) == 'V')
		{
			std::map<std::string, std::string>::iterator it = sett.find(key);
			if (it != sett.end())
			{
				it->second = val;
			}
		}
	}

	// Read translations file
	std::string langauge_fn = get("SV_LANGUAGE");
	if (!langauge_fn.empty())
	{
		std::ifstream file0(translation_dp + langauge_fn);
		if (!file0.good())
		{
			return;
		}
		if (file0.peek() == std::ifstream::traits_type::eof())
		{
			return;
		}

		while (std::getline(file0, current_line))
		{
			if (current_line.empty()) continue;

			std::string key;
			std::string val;

			int index = current_line.find("=");
			if (index != -1)
			{
				key = current_line.substr(0, index);
				val = current_line.substr(index + 1, current_line.size() - 1);
			}
			else
			{
				key = current_line;
				val = "1";
			}

			for (unsigned int i = 0; i < key.size(); i++)
			{
				char c = key.at(i);
				// abc --> ABC
				if (c > 96 && c < 123)
				{
					c -= 32;
					key.at(i) = c;
				}
			}

			if (!veccmp(cmd_arg, key) && key.at(0) == 'T')
			{
				std::map<std::string, std::string>::iterator it = sett.find(key);
				if (it != sett.end())
				{
					it->second = val;
				}
			}
		}
	}

#ifdef _DEBUG
	for (std::map<std::string, std::string>::iterator it = sett.begin(); it != sett.end(); it++)
	{
		std::cout << it->first << "=" << it->second << std::endl;
	}
#endif
}

std::string SETT::get(const std::string &key)
{
	std::map<std::string, std::string>::iterator it = sett.find(key);
	if (it != sett.end())
	{
		return it->second;
	}
	return std::string("");
}

bool SETT::getb(const std::string& key)
{
	std::map<std::string, std::string>::iterator it = sett.find(key);
	if (it != sett.end())
	{
		return std::stoi(it->second) > 0;
	}
	return false;
}

bool SETT::add(std::string key, const std::string &val)
{
	for (unsigned int i = 0; i < key.size(); i++)
	{
		char c = key.at(i);
		if (c == '-')
		{
			key.at(i) = '_';
			continue;
		}
		// abc --> ABC
		if (c > 96 && c < 123)
		{
			c -= 32;
			key.at(i) = c;
		}
	}
	key = std::string("SV_") + key;
	std::map<std::string, std::string>::iterator it = sett.find(key);
	if (it != sett.end())
	{
		it->second = val;
		return false;
	}
	return true;
}



struct transaction {
	time_t date;
	time_t date_current;
	std::string reference_num;
	int timezone;
	bool credit_or_debit;
	double amount;
	std::string name;
	std::string description;
	unsigned int index;
};
std::vector<transaction> data; // All data.
std::vector<transaction> data0; // Data visible.

void read_database(const std::string &fn);
bool save_database(const std::string &fn);
int set_timezone(const std::string &tz);
std::string get_timezone(int timezone);

time_t date_to_seconds(const std::string& date)
{
	int yy, month, dd, hh, mm, ss;
	const char* datec = date.c_str();
	sscanf(datec, "%d.%d.%d %d:%d:%d", &dd, &month, &yy, &hh, &mm, &ss);
	
	struct tm tm;
	tm.tm_year = yy - 1900;
	tm.tm_mon = month - 1;
	tm.tm_mday = dd;
	tm.tm_hour = hh;
	tm.tm_min = mm;
	tm.tm_sec = ss;
	tm.tm_isdst = -1;

	time_t t = mktime(&tm);
	return t;
}

time_t date_to_seconds(const std::string& date, std::string format)
{
	char u[6] = {0};
	int i = 0;
	for (std::string::iterator it = format.begin(); it != format.end(); it++)
	{
		if (*it == '%')
		{
			u[i] = *(it + 1);
			*(it + 1) = 'd';
			i++;
		}
	}

	int y[6] = {0};
	const char* datec = date.c_str();
	sscanf(datec, format.c_str(), &y[0], &y[1], &y[2], &y[3], &y[4], &y[5]);

	int yy, month, dd, hh, mm, ss;
	for (i = 0; i < 6; i++)
	{
		if (u[i] == 'd')
		{
			dd = y[i];
			continue;
		}
		if (u[i] == 'm')
		{
			month = y[i];
			continue;
		}
		if (u[i] == 'Y')
		{
			yy = y[i];
			continue;
		}
		if (u[i] == 'H')
		{
			hh = y[i];
			continue;
		}
		if (u[i] == 'M')
		{
			mm = y[i];
			continue;
		}
		if (u[i] == 'S')
		{
			ss = y[i];
		}
	}

	struct tm tm;
	tm.tm_year = yy - 1900;
	tm.tm_mon = month - 1;
	tm.tm_mday = dd;
	tm.tm_hour = hh;
	tm.tm_min = mm;
	tm.tm_sec = ss;
	tm.tm_isdst = -1;

	time_t t = mktime(&tm);
	return t;
}

void get_word(std::string *val)
{
	std::string usr_input;
	std::getline(std::cin, usr_input);
	if (usr_input.empty())
	{
		*val = "";
		return;
	}
	int find_pos = usr_input.find(" ");
	if (find_pos != -1)
	{
		usr_input = usr_input.substr(0, find_pos);
	}
	*val = usr_input;
}



int main(int argc, char *argv[])
{
#ifdef _WIN32
	SetConsoleOutputCP(65001);
#endif

	std::string fp;
	std::vector<std::string> cmd_arg;
	SETT sett;

	if (argc > 1)
	{
		cmd_arg = std::vector<std::string>(argv, argv + argc);
		cmd_arg.erase(cmd_arg.begin());
		cmd_arg.shrink_to_fit();

		for (std::vector<std::string>::iterator it = cmd_arg.begin(); it != cmd_arg.end(); it++)
		{
			std::string lhs;
			std::string rhs;
			int index = it->find('=');
			if (index != -1)
			{
				lhs = it->substr(0, index);
				rhs = it->substr(index + 1, it->size());
			}
			else
			{
				static bool prevent_double = false;
				if (!prevent_double)
				{
					prevent_double = true;
					std::vector<std::string>::iterator it0 = it;
					for (; it0 != cmd_arg.end(); it0++)
					{
						int index0 = it0->find('=');
						if (index0 != -1)
							return 0;
					}
				}
				lhs = *it;
				rhs = "1";
			}
			sett.add(lhs, rhs);
		}
	}

	int timezone = set_timezone(sett.get("SV_TIMEZONE"));
	const std::string format = sett.get("SV_FORMAT");
	const std::string order = sett.get("SV_ORDER");
	
	fp = sett.get("SV_DATABASE_PATH");
	read_database(fp);

	// Order by date
	if (!data.empty())
	{
		for (unsigned int i = 0; i < data.size() - 1; i++)
		{
			if (data.at(i).date > data.at(i + 1).date)
			{
				for (unsigned int u = i; u < data.size() - 1; u++)
				{
					if (data.at(u).date < data.at(u + 1).date) break;

					std::vector<transaction>::iterator it = data.begin() + u;
					data.insert(data.begin() + u + 2, *it);
					data.erase(data.begin() + u);
				}
				if (i > 0) i -= 2;
				else i--;
			}
		}
		data0 = data;
	}

	if (argc > 1)
	{
		// SV_NEW - cmd arg
		if (sett.getb("SV_NEW"))
		{
			transaction new_data;
			for (unsigned int i = 0; i < 7; i++)
			{
				std::string usr_input;

				char current[4];
				current[0] = order.at(4 * i);
				current[1] = order.at(4 * i + 1);
				current[2] = order.at(4 * i + 2);
				current[3] = '\0';

				// credit or debit
				if (strcmp(current, "cod") == 0)
				{
					std::cout << sett.get("T_CREDIT_OR_DEBIT");
					get_word(&usr_input);
					if (usr_input.empty())
						return 0;
					if (strcmp(usr_input.c_str(), sett.get("T_CREDIT_OR_DEBIT_A").c_str()) == 0)
					{
						usr_input = "1";
					}
					else
					{
						usr_input = "0";
					}
					new_data.credit_or_debit = std::stoi(usr_input.c_str()) > 0;
				}
				// date
				if (strcmp(current, "dat") == 0)
				{
					std::string dmy = sett.get("SV_DATE_ORDER");
					std::string dd, month, yy, mm, hh, ss;
					for (unsigned int i = 0; i < 6; i++)
					{
						if (dmy.at(i) == 'D')
						{
							std::cout << sett.get("T_DAY");
							get_word(&dd);
							if (dd.empty())
								return 0;
							continue;
						}
						if (dmy.at(i) == 'M')
						{
							std::cout << sett.get("T_MONTH");
							get_word(&month);
							if (month.empty())
								return 0;
							continue;
						}
						if (dmy.at(i) == 'Y')
						{
							std::cout << sett.get("T_YEAR");
							get_word(&yy);
							if (yy.empty())
								return 0;
							continue;
						}
						if (dmy.at(i) == 'h')
						{
							std::cout << sett.get("T_HOURS");
							get_word(&hh);
							if (hh.empty())
								hh = "0";
							continue;
						}
						if (dmy.at(i) == 'm')
						{
							std::cout << sett.get("T_MINUTES");
							get_word(&mm);
							if (mm.empty())
								mm = "0";
							continue;
						}
						if (dmy.at(i) == 's')
						{
							std::cout << sett.get("T_SECONDS");
							get_word(&ss);
							if (ss.empty())
								ss = "0";
							continue;
						}
					}
					std::string date = dd;
					date += ".";
					date += month;
					date += ".";
					date += yy;
					date += " ";
					date += hh;
					date += ":";
					date += mm;
					date += ":";
					date += ss;
					new_data.date = date_to_seconds(date);
				}
				// name
				if (strcmp(current, "nam") == 0)
				{
					std::cout << sett.get("T_NAME");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.name = usr_input;
				}
				// amount
				if (strcmp(current, "amo") == 0)
				{
					std::cout << sett.get("T_AMOUNT");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.amount = std::atof(usr_input.c_str());
				}
				// reference
				if (strcmp(current, "ref") == 0)
				{
					std::cout << sett.get("T_REF");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.reference_num = usr_input;
				}
				// description
				if (strcmp(current, "des") == 0)
				{
					std::cout << sett.get("T_DESCRIPTION");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.description = usr_input;
				}
				// timezone
				if (strcmp(current, "tzo") == 0)
				{
					std::cout << sett.get("T_TIMEZONE");
					get_word(&usr_input);
					if (usr_input.empty())
					{
						new_data.timezone = timezone;
					}
					else
					{
						new_data.timezone = set_timezone(usr_input);
					}
					new_data.date -= new_data.timezone;
				}
				std::cin.clear();
				fflush(stdin);
			}
			new_data.date_current = std::time(nullptr);
#ifdef _DEBUG
			std::cout << std::endl;
			std::cout << "----- NEW DATA -----" << std::endl;
			std::cout << "date:            " << new_data.date << std::endl;
			std::cout << "date_current:    " << new_data.date_current << std::endl;
			std::cout << "reference_num:   " << new_data.reference_num << std::endl;
			std::cout << "timezone:        " << new_data.timezone << std::endl;
			std::cout << "credit_or_debit: " << new_data.credit_or_debit << std::endl;
			std::cout << "amount:          " << new_data.amount << std::endl;
			std::cout << "name:            " << new_data.name << std::endl;
			std::cout << "description:     " << new_data.description << std::endl;
			time_t tt = new_data.date_current + timezone;
			std::cout << "ct: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl;
			tt = new_data.date + new_data.timezone;
			std::cout << "dt: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl;
			return 0;
#endif
			data.push_back(new_data);

			save_database(fp);
			return 0;
		}

		if (data0.empty())
		{
			return 0;
		}

		// SV_START_DATE - cmd arg
		std::string start_date = sett.get("SV_START_DATE");
		if (!start_date.empty())
		{
			time_t date = date_to_seconds(start_date, format.c_str());
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->date + it->timezone < date)
				{
					data0.erase(it);
					it--;
				}
			}
		}
		// SV_END_DATE - cmd arg
		std::string end_date = sett.get("SV_END_DATE");
		if (!end_date.empty())
		{
			time_t date = date_to_seconds(end_date, format.c_str());
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->date + it->timezone > date)
				{
					data0.erase(it);
					it--;
				}
			}
		}

		// SV_ORDER_BY_DATE - cmd arg
		if (sett.getb("SV_ORDER_BY_DATE"))
		{
			for (unsigned int i = 0; i < data0.size() - 1; i++)
			{
				if (data0.at(i).date_current > data0.at(i + 1).date_current)
				{
					for (unsigned int u = i; u < data0.size() - 1; u++)
					{
						if (data0.at(u).date_current < data0.at(u + 1).date_current) break;

						std::vector<transaction>::iterator it = data0.begin() + u;
						data0.insert(data0.begin() + u + 2, *it);
						data0.erase(data0.begin() + u);
					}
					if (i > 0) i -= 2;
					else i--;
				}
			}
		}

		// SV_DEBIT_FIRST - cmd arg
		if (sett.getb("SV_DEBIT_FIRST"))
		{
			std::vector<transaction> temp;
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 0)
				{
					transaction tra = *it;
					data0.erase(it);
					temp.push_back(tra);
					it--;
				}
			}
			data0.insert(data0.end(), temp.begin(), temp.end());
		}
		// SV_CREDIT_FIRST - cmd arg
		else if (sett.getb("SV_CREDIT_FIRST"))
		{
			std::vector<transaction> temp;
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 1)
				{
					transaction tra = *it;
					data0.erase(it);
					temp.push_back(tra);
					it--;
				}
			}
			data0.insert(data0.end(), temp.begin(), temp.end());
		}
		// SV_ONLY_DEBIT - cmd arg
		else if (sett.getb("SV_ONLY_DEBIT"))
		{
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 1)
				{
					data0.erase(it);
					it--;
				}
			}
		}
		// SV_ONLY_CREDIT - cmd arg
		else if (sett.getb("SV_ONLY_CREDIT"))
		{
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 0)
				{
					data0.erase(it);
					it--;
				}
			}
		}

		// SV_DELETE - cmd arg
		std::string delete_cmd = sett.get("SV_DELETE");
		if (!delete_cmd.empty())
		{
			if (data0.empty())
			{
				return 0;
			}
			for (std::string::iterator it = delete_cmd.begin(); it != delete_cmd.end(); it++)
			{
				if (*it > 57 || *it < 48)
				{
					return 0;
				}
			}
			unsigned int index = std::stoi(delete_cmd);
			if (index < 1 || index > data0.size())
			{
				return 0;
			}

			transaction tra = data0.at(data0.size() - index);

			std::vector<transaction>::const_iterator it;
			it = data.begin() + tra.index;
			data.erase(it);

			save_database(fp);
			return 0;
		}
	}

	if (data0.empty())
	{
		return 0;
	}

	const std::string currency = sett.get("SV_CURRENCY");
	double balance = 0;
	bool currency_side = sett.getb("SV_CURRENCY_SIDE");

	std::cout << std::fixed << std::setprecision(2);

	unsigned int index = 0;
	for (std::vector<transaction>::const_iterator it = data0.begin(); it != data0.end(); it++)
	{
		std::cout << "\n[ " << data0.size() - index++  << " ]" << std::endl;
		for (unsigned int i = 0; i < 7; i++)
		{
			char current[4];
			current[0] = order.at(4 * i);
			current[1] = order.at(4 * i + 1);
			current[2] = order.at(4 * i + 2);
			current[3] = '\0';

			// timezone
			if (strcmp(current, "tzo") == 0)
			{
				continue;
			}

			// credit or debit
			if (strcmp(current, "cod") == 0)
			{
				bool colored_text = sett.getb("SV_NO_COLOR");
				if (it->credit_or_debit)
				{
					std::string text = sett.get("T_CREDIT");
					if (colored_text)
					{
						std::cout << text << std::endl;
					}
					else
					{
						std::string color = "\033[";
						color += sett.get("SV_CREDIT_COLOR");
						color += "m";
						std::cout << color << text << "\033[0m" << std::endl;
					}
				}
				else
				{
					std::string text = sett.get("T_DEBIT");
					if (colored_text)
					{
						std::cout << text << std::endl;
					}
					else
					{
						std::string color = "\033[";
						color += sett.get("SV_DEBIT_COLOR");
						color += "m";
						std::cout << color << text << "\033[0m" << std::endl;
					}
				}
				continue;
			}
			// date
			if (strcmp(current, "dat") == 0)
			{
				std::cout << sett.get("T_DATE");
				time_t date = it->date + it->timezone;
				std::cout << std::put_time(gmtime(&date), format.c_str()) << std::endl;
				if (it->credit_or_debit)
				{
					balance += it->amount;
				}
				else
				{
					balance -= it->amount;
				}
				continue;
			}
			// name
			if (strcmp(current, "nam") == 0)
			{
				std::cout << sett.get("T_NAME") << it->name << std::endl;
				continue;
			}
			// amount
			if (strcmp(current, "amo") == 0)
			{
				std::cout << sett.get("T_AMOUNT");
				if (currency_side)
				{
					std::cout << currency << it->amount << std::endl;
				}
				else
				{
					std::cout << it->amount << currency << std::endl;
				}
				continue;
			}
			// reference
			if (strcmp(current, "ref") == 0)
			{
				std::cout << sett.get("T_REF");
				std::cout << it->reference_num << std::endl;
				continue;
			}
			// description
			if (strcmp(current, "des") == 0)
			{
				std::cout << sett.get("T_DESCRIPTION") << it->description << std::endl;
			}
		}
	}

	std::cout << "\n" << sett.get("T_BALANCE");
	if (currency_side)
	{
		std::cout << currency << balance << std::endl;
	}
	else
	{
		std::cout << balance << currency << std::endl;
	}

	return 0;
}

void read_database(const std::string &fn)
{
	std::ifstream file(fn);

	if (!file.good())
	{
		return;
	}
	if (file.peek() == std::ifstream::traits_type::eof())
	{
		return;
	}

	std::string current_line;
	unsigned int index = 0;
	while (std::getline(file, current_line))
	{
		data.push_back(transaction());
		data.back().date = std::stoi(current_line.c_str());

		std::getline(file, current_line);
		data.back().date_current = std::stoi(current_line.c_str());

		std::getline(file, current_line);
		data.back().name = current_line;

		std::getline(file, current_line);
		data.back().amount = std::atof(current_line.c_str());

		std::getline(file, current_line);
		data.back().reference_num = current_line;

		std::getline(file, current_line);
		data.back().description = current_line;
		
		std::getline(file, current_line);
		int timezone = set_timezone(current_line);
		data.back().timezone = timezone;

		std::getline(file, current_line);
		data.back().credit_or_debit = std::stoi(current_line.c_str()) > 0;

		data.back().index = index;
		index++;
	}
}

bool save_database(const std::string &fn)
{
	std::ofstream file(fn);
	if (!file.good())
	{
		std::cout << "\n\nERROR! The database can not be saved." << std::endl;
		return true;
	}
	for (std::vector<transaction>::const_iterator it = data.begin(); it != data.end(); it++)
	{
		file << it->date << "\n";
		file << it->date_current << "\n";
		file << it->name << "\n";
		file << it->amount << "\n";
		file << it->reference_num << "\n";
		file << it->description << "\n";
		file << get_timezone(it->timezone) << "\n";
		file << it->credit_or_debit << "\n";
	}
	return false;
}

int set_timezone(const std::string& tz)
{
	std::string sn(1, tz.at(1));
	if (tz.size() > 2)
	{
		std::string c(1, tz.at(2));
		sn += c;
	}
	
	int n = std::stoi(sn.c_str());
	if (n < -12) n = -12;
	else if (n > 12) n = 12;
	
	int timezone;
	timezone = 3600 * n;
	if (tz.at(0) == '-')
	{
		timezone *= -1;
	}
	return timezone;
}

std::string get_timezone(int timezone)
{
	std::string return_str;
	if (timezone < 0)
	{
		timezone *= -1;
		return_str = "-";
	}
	else
	{
		return_str = "+";
	}
	int n = timezone / 3600;
	return_str += std::to_string(n);
	return return_str;
}

Supports also translations into a different languages. Translated into Finnish, the file may look like this for example:

translations/fi
T_DATE=Päivämäärä:	
T_AMOUNT=Summa:		
T_NAME=Nimi:		
T_DESCRIPTION=Kuvaus:		
T_BALANCE=Yhteensä: 
T_REF=Viitenumero:	
T_CREDIT_OR_DEBIT=Tulo vai meno?(tulo/meno): 
T_CREDIT_OR_DEBIT_A=tulo
T_DAY=Päivä: 		
T_MONTH=Kuukausi: 	
T_YEAR=Vuosi: 		
T_MINUTES=Minuutit: 	
T_HOURS=Tunnit: 	
T_SECONDS=Sekunnit: 	
T_TIMEZONE=Aikavyöhyke:	
T_DEBIT=MENO
T_CREDIT=TULO

The Settings file may look like this for example:

settings
SV_LANGUAGE=fi
SV_TIMEZONE=+2
SV_FORMAT=%d.%m.%Y %H:%M:%S
SV_CURRENCY=€
SV_CURRENCY_SIDE=0
« Back | ↑ Top