mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
These add or parse the textual noise needed to replicate the Scheme options' serialization technique of saving scheme forms for saving report options and then evaluating those forms to restore the option values. Required for backward saved-reports compatibility.
285 lines
9.4 KiB
C++
285 lines
9.4 KiB
C++
/********************************************************************\
|
|
* gnc-option.cpp -- Application options system *
|
|
* Copyright (C) 2019 John Ralls <jralls@ceridwen.us> *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU General Public License as *
|
|
* published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* *
|
|
* 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, contact: *
|
|
* *
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
|
* *
|
|
\********************************************************************/
|
|
|
|
//#include "options.h"
|
|
#include "gnc-option.hpp"
|
|
#include <gnc-datetime.hpp>
|
|
#include <guid.hpp>
|
|
extern "C"
|
|
{
|
|
#include "gnc-accounting-period.h"
|
|
#include "gnc-ui-util.h"
|
|
}
|
|
|
|
bool
|
|
GncOptionAccountValue::validate(const GncOptionAccountList& values) const
|
|
{
|
|
if (values.empty())
|
|
return false;
|
|
if (get_ui_type() == GncOptionUIType::ACCOUNT_SEL && values.size() != 1)
|
|
return false;
|
|
if (m_allowed.empty())
|
|
return true;
|
|
for(auto account : values) {
|
|
if (std::find(m_allowed.begin(), m_allowed.end(),
|
|
xaccAccountGetType(account)) == m_allowed.end())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
|
|
static void
|
|
normalize_month(struct tm& now)
|
|
{
|
|
if (now.tm_mon < 0)
|
|
{
|
|
now.tm_mon += 12;
|
|
--now.tm_year;
|
|
}
|
|
else if (now.tm_mon > 11)
|
|
{
|
|
now.tm_mon -= 12;
|
|
++now.tm_year;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_day_and_time(struct tm& now, bool starting)
|
|
{
|
|
if (starting)
|
|
{
|
|
now.tm_hour = now.tm_min = now.tm_sec = 0;
|
|
now.tm_mday = 1;
|
|
}
|
|
else
|
|
{
|
|
now.tm_min = now.tm_sec = 59;
|
|
now.tm_hour = 23;
|
|
now.tm_mday = days_in_month[now.tm_mon];
|
|
// Check for Februrary in a leap year
|
|
if (int year = now.tm_year + 1900; now.tm_mon == 1 &&
|
|
year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
|
|
++now.tm_mday;
|
|
}
|
|
};
|
|
|
|
time64
|
|
GncOptionDateValue::get_value() const
|
|
{
|
|
if (m_period == RelativeDatePeriod::ABSOLUTE)
|
|
return m_date;
|
|
if (m_period == RelativeDatePeriod::TODAY)
|
|
return static_cast<time64>(GncDateTime());
|
|
if (m_period == RelativeDatePeriod::START_ACCOUNTING_PERIOD)
|
|
return gnc_accounting_period_fiscal_start();
|
|
if (m_period == RelativeDatePeriod::END_ACCOUNTING_PERIOD)
|
|
return gnc_accounting_period_fiscal_end();
|
|
|
|
GncDateTime now_t;
|
|
if (m_period == RelativeDatePeriod::TODAY)
|
|
return static_cast<time64>(now_t);
|
|
struct tm now{static_cast<tm>(now_t)};
|
|
struct tm period{static_cast<tm>(GncDateTime(gnc_accounting_period_fiscal_start()))};
|
|
bool starting = m_period == RelativeDatePeriod::START_PREV_MONTH ||
|
|
m_period == RelativeDatePeriod::START_THIS_MONTH ||
|
|
m_period == RelativeDatePeriod::START_CAL_YEAR ||
|
|
m_period == RelativeDatePeriod::START_PREV_YEAR ||
|
|
m_period == RelativeDatePeriod::START_CURRENT_QUARTER ||
|
|
m_period == RelativeDatePeriod::START_PREV_QUARTER;
|
|
|
|
bool prev = m_period == RelativeDatePeriod::START_PREV_YEAR ||
|
|
m_period == RelativeDatePeriod::END_PREV_YEAR ||
|
|
m_period == RelativeDatePeriod::START_PREV_QUARTER ||
|
|
m_period == RelativeDatePeriod::END_PREV_QUARTER;
|
|
|
|
if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday)
|
|
{
|
|
//No set accounting period, use the calendar year
|
|
period.tm_mon = 0;
|
|
period.tm_mday = 0;
|
|
}
|
|
|
|
if (m_period == RelativeDatePeriod::START_CAL_YEAR ||
|
|
m_period == RelativeDatePeriod::END_CAL_YEAR ||
|
|
m_period == RelativeDatePeriod::START_PREV_YEAR ||
|
|
m_period == RelativeDatePeriod::END_PREV_YEAR)
|
|
{
|
|
|
|
if (prev)
|
|
--now.tm_year;
|
|
now.tm_mon = starting ? 0 : 11;
|
|
}
|
|
else if (m_period == RelativeDatePeriod::START_PREV_QUARTER ||
|
|
m_period == RelativeDatePeriod::END_PREV_QUARTER ||
|
|
m_period == RelativeDatePeriod::START_CURRENT_QUARTER ||
|
|
m_period == RelativeDatePeriod::END_CURRENT_QUARTER)
|
|
{
|
|
auto offset = (now.tm_mon > period.tm_mon ? now.tm_mon - period.tm_mon :
|
|
period.tm_mon - now.tm_mon) % 3;
|
|
now.tm_mon = now.tm_mon - offset;
|
|
if (prev)
|
|
now.tm_mon -= 3;
|
|
if (!starting)
|
|
now.tm_mon += 2;
|
|
}
|
|
else if (m_period == RelativeDatePeriod::START_PREV_MONTH ||
|
|
m_period == RelativeDatePeriod::END_PREV_MONTH)
|
|
--now.tm_mon;
|
|
normalize_month(now);
|
|
set_day_and_time(now, starting);
|
|
return static_cast<time64>(GncDateTime(now));
|
|
}
|
|
static const char* date_type_str[] {"absolute", "relative"};
|
|
static const std::array<const char*, 15> date_period_str
|
|
{
|
|
"today",
|
|
"start-this-month", "end-this-month",
|
|
"start-prev-month", "end-prev-month",
|
|
"start-current-quarter", "end-current-quarter",
|
|
"start-prev-quarter", "end-prev-quarter",
|
|
"start-cal-year", "end-cal-year",
|
|
"start-prev-year", "end-prev-year",
|
|
"start-prev-fin-year", "end-prev-fin-year"
|
|
};
|
|
|
|
|
|
std::ostream&
|
|
GncOptionDateValue::out_stream(std::ostream& oss) const noexcept
|
|
{
|
|
if (m_period == RelativeDatePeriod::ABSOLUTE)
|
|
oss << date_type_str[0] << " . " << m_date;
|
|
else
|
|
oss << date_type_str[1] << " . " <<
|
|
date_period_str[static_cast<int>(m_period)];
|
|
return oss;
|
|
}
|
|
|
|
std::istream&
|
|
GncOptionDateValue::in_stream(std::istream& iss)
|
|
{
|
|
std::string type_str;
|
|
std::getline(iss, type_str, '.');
|
|
if (type_str == "absolute ")
|
|
{
|
|
time64 time;
|
|
iss >> time;
|
|
set_value(time);
|
|
if (iss.get() != ')')
|
|
iss.unget();
|
|
}
|
|
else if (type_str == "relative ")
|
|
{
|
|
std::string period_str;
|
|
iss >> period_str;
|
|
if (period_str.back() == ')')
|
|
period_str.pop_back();
|
|
auto period = std::find(date_period_str.begin(), date_period_str.end(),
|
|
period_str);
|
|
if (period == date_period_str.end())
|
|
{
|
|
std::string err{"Unknown period string in date option: '"};
|
|
err += period_str;
|
|
err += "'";
|
|
throw std::invalid_argument(err);
|
|
}
|
|
|
|
int64_t index = period - date_period_str.begin();
|
|
set_value(static_cast<RelativeDatePeriod>(index));
|
|
}
|
|
else
|
|
{
|
|
std::string err{"Unknown date type string in date option: '"};
|
|
err += type_str;
|
|
err += "'";
|
|
throw std::invalid_argument{err};
|
|
}
|
|
return iss;
|
|
}
|
|
|
|
QofInstance*
|
|
qof_instance_from_string(const std::string& str, GncOptionUIType type)
|
|
{
|
|
QofIdType qof_type;
|
|
bool commodity_type{false};
|
|
switch(type)
|
|
{
|
|
case GncOptionUIType::CURRENCY:
|
|
case GncOptionUIType::COMMODITY:
|
|
qof_type = "Commodity";
|
|
commodity_type = true;
|
|
break;
|
|
case GncOptionUIType::BUDGET:
|
|
qof_type = "Budget";
|
|
break;
|
|
case GncOptionUIType::OWNER:
|
|
qof_type = "gncOwner";
|
|
break;
|
|
case GncOptionUIType::CUSTOMER:
|
|
qof_type = "gncCustomer";
|
|
break;
|
|
case GncOptionUIType::VENDOR:
|
|
qof_type = "gncVendor";
|
|
break;
|
|
case GncOptionUIType::EMPLOYEE:
|
|
qof_type = "gncEmployee";
|
|
break;
|
|
case GncOptionUIType::INVOICE:
|
|
qof_type = "gncInvoice";
|
|
break;
|
|
case GncOptionUIType::TAX_TABLE:
|
|
qof_type = "gncTaxtable";
|
|
break;
|
|
case GncOptionUIType::QUERY:
|
|
qof_type = "gncQuery";
|
|
break;
|
|
case GncOptionUIType::ACCOUNT_LIST:
|
|
case GncOptionUIType::ACCOUNT_SEL:
|
|
default:
|
|
qof_type = "Account";
|
|
break;
|
|
}
|
|
auto book{gnc_get_current_book()};
|
|
if (commodity_type)
|
|
{
|
|
auto sep{str.find(":")};
|
|
auto name_space{str.substr(0, sep)};
|
|
auto mnemonic{str.substr(sep + 1, -1)};
|
|
auto table = gnc_commodity_table_get_table(book);
|
|
return QOF_INSTANCE(gnc_commodity_table_lookup(table,
|
|
name_space.c_str(),
|
|
mnemonic.c_str()));
|
|
}
|
|
auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
|
|
auto col{qof_book_get_collection(book, qof_type)};
|
|
return QOF_INSTANCE(qof_collection_lookup_entity(col, &guid));
|
|
}
|
|
|
|
std::string
|
|
qof_instance_to_string(const QofInstance* inst)
|
|
{
|
|
gnc::GUID guid{*qof_instance_get_guid(inst)};
|
|
return guid.to_string();
|
|
}
|