mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Add Account-list options.
This commit is contained in:
@@ -29,6 +29,23 @@ extern "C"
|
||||
#include "gnc-accounting-period.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
|
||||
|
||||
@@ -28,6 +28,7 @@ extern "C"
|
||||
{
|
||||
#include <config.h>
|
||||
#include <qof.h>
|
||||
#include <Account.h>
|
||||
#include <gnc-budget.h>
|
||||
#include <gnc-commodity.h>
|
||||
}
|
||||
@@ -393,6 +394,82 @@ private:
|
||||
GncMultiChoiceOptionChoices m_choices;
|
||||
};
|
||||
|
||||
/** Account options
|
||||
*
|
||||
* Set one or more accounts on which to report, optionally restricted to certain
|
||||
* account types. Many calls to make-account-list-option will pass a get-default
|
||||
* function that retrieves all of the accounts of a list of types.
|
||||
*
|
||||
* Some reports (examples/daily-reports.scm and standard/ account-piechart.scm,
|
||||
* advanced-portfolio.scm, category-barchart.scm, net-charts.scm, and
|
||||
* portfolio.scm) also provide a validator that rejects accounts that don't meet
|
||||
* an account-type criterion.
|
||||
*
|
||||
* There are two types of option, account-list which permits more than one
|
||||
* account selection and account-sel, which doesn't.
|
||||
*
|
||||
|
||||
*/
|
||||
|
||||
using GncOptionAccountList = std::vector<const Account*>;
|
||||
using GncOptionAccountTypeList = std::vector<GNCAccountType>;
|
||||
|
||||
class GncOptionAccountValue :
|
||||
public OptionClassifier, public OptionUIItem
|
||||
{
|
||||
public:
|
||||
GncOptionAccountValue(const char* section, const char* name,
|
||||
const char* key, const char* doc_string,
|
||||
GncOptionUIType ui_type) :
|
||||
OptionClassifier{section, name, key, doc_string},
|
||||
OptionUIItem(ui_type), m_value{}, m_default_value{}, m_allowed{} {}
|
||||
|
||||
GncOptionAccountValue(const char* section, const char* name,
|
||||
const char* key, const char* doc_string,
|
||||
GncOptionUIType ui_type,
|
||||
const GncOptionAccountList& value) :
|
||||
OptionClassifier{section, name, key, doc_string},
|
||||
OptionUIItem(ui_type),
|
||||
m_value{value},
|
||||
m_default_value{std::move(value)}, m_allowed{} {}
|
||||
GncOptionAccountValue(const char* section, const char* name,
|
||||
const char* key, const char* doc_string,
|
||||
GncOptionUIType ui_type,
|
||||
GncOptionAccountTypeList&& allowed) :
|
||||
OptionClassifier{section, name, key, doc_string},
|
||||
OptionUIItem(ui_type),
|
||||
m_value{},
|
||||
m_default_value{}, m_allowed{std::move(allowed)} {}
|
||||
GncOptionAccountValue(const char* section, const char* name,
|
||||
const char* key, const char* doc_string,
|
||||
GncOptionUIType ui_type,
|
||||
const GncOptionAccountList& value,
|
||||
GncOptionAccountTypeList&& allowed) :
|
||||
OptionClassifier{section, name, key, doc_string},
|
||||
OptionUIItem(ui_type),
|
||||
m_value{},
|
||||
m_default_value{}, m_allowed{std::move(allowed)} {
|
||||
if (!validate(value))
|
||||
throw std::invalid_argument("Supplied Value not in allowed set.");
|
||||
m_value = value;
|
||||
m_default_value = std::move(value);
|
||||
}
|
||||
|
||||
const GncOptionAccountList& get_value() const { return m_value; }
|
||||
const GncOptionAccountList& get_default_value() const { return m_default_value; }
|
||||
bool validate (const GncOptionAccountList& values) const;
|
||||
void set_value (const GncOptionAccountList& values) {
|
||||
if (validate(values))
|
||||
//throw!
|
||||
m_value = values;
|
||||
}
|
||||
|
||||
private:
|
||||
GncOptionAccountList m_value;
|
||||
GncOptionAccountList m_default_value;
|
||||
GncOptionAccountTypeList m_allowed;
|
||||
};
|
||||
|
||||
/** Date options
|
||||
* A legal date value is a pair of either and a RelativeDatePeriod, the absolute flag and a time64, or for legacy purposes the absolute flag and a timespec.
|
||||
* The original design allowed custom RelativeDatePeriods, but that facility is unused so we'll go with compiled-in enums.
|
||||
@@ -457,6 +534,7 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
|
||||
GncOptionValue<QofInstance*>,
|
||||
GncOptionValue<QofQuery*>,
|
||||
GncOptionValue<std::vector<GncGUID>>,
|
||||
GncOptionAccountValue,
|
||||
GncOptionMultichoiceValue,
|
||||
GncOptionRangeValue<int>,
|
||||
GncOptionRangeValue<double>,
|
||||
|
||||
@@ -231,7 +231,7 @@ gnc_register_color_option(const GncOptionDBPtr& db, const char* section,
|
||||
const char* doc_string, std::string value)
|
||||
{
|
||||
GncOption option{section, name, key, doc_string, value,
|
||||
GncOptionUIType::FONT};
|
||||
GncOptionUIType::COLOR};
|
||||
db->register_option(section, std::move(option));
|
||||
}
|
||||
|
||||
@@ -278,36 +278,80 @@ gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section,
|
||||
}
|
||||
|
||||
void
|
||||
gnc_register_account_liat_option(const GncOptionDBPtr& db, const char* section,
|
||||
gnc_register_account_list_option(const GncOptionDBPtr& db, const char* section,
|
||||
const char* name, const char* key,
|
||||
const char* doc_string,
|
||||
std::vector<GncGUID> value)
|
||||
const GncOptionAccountList& value)
|
||||
{
|
||||
GncOption option{section, name, key, doc_string, value,
|
||||
GncOptionUIType::ACCOUNT_LIST};
|
||||
GncOption option{GncOptionAccountValue{section, name, key, doc_string,
|
||||
GncOptionUIType::ACCOUNT_LIST, value}};
|
||||
db->register_option(section, std::move(option));
|
||||
}
|
||||
|
||||
void
|
||||
gnc_register_acount_list_limited_option(const GncOptionDBPtr& db,
|
||||
const char* section, const char* name,
|
||||
const char* key, const char* doc_string,
|
||||
std::vector<GncGUID> value)
|
||||
gnc_register_account_list_limited_option(const GncOptionDBPtr& db,
|
||||
const char* section, const char* name,
|
||||
const char* key,
|
||||
const char* doc_string,
|
||||
const GncOptionAccountList& value,
|
||||
GncOptionAccountTypeList&& allowed)
|
||||
{
|
||||
GncOption option{section, name, key, doc_string, value,
|
||||
GncOptionUIType::ACCOUNT_LIST};
|
||||
db->register_option(section, std::move(option));
|
||||
try
|
||||
{
|
||||
GncOption option{GncOptionAccountValue{section, name, key, doc_string,
|
||||
GncOptionUIType::ACCOUNT_LIST, value, std::move(allowed)}};
|
||||
db->register_option(section, std::move(option));
|
||||
}
|
||||
catch (const std::invalid_argument& err)
|
||||
{
|
||||
std::cerr << "Account List Limited Option, value failed validation, option not registerd.\n";
|
||||
}
|
||||
}
|
||||
|
||||
using AccountPair = std::pair<GncOptionAccountList&,
|
||||
const GncOptionAccountTypeList&>;
|
||||
static void
|
||||
find_children(Account* account, void* data)
|
||||
{
|
||||
auto datapair =
|
||||
(AccountPair*)data;
|
||||
GncOptionAccountList& list = datapair->first;
|
||||
const GncOptionAccountTypeList& types = datapair->second;
|
||||
if (std::find(types.begin(), types.end(),
|
||||
xaccAccountGetType(account)) != types.end())
|
||||
list.push_back(account);
|
||||
}
|
||||
|
||||
GncOptionAccountList
|
||||
gnc_account_list_from_types(QofBook *book,
|
||||
const GncOptionAccountTypeList& types)
|
||||
{
|
||||
GncOptionAccountList list;
|
||||
AccountPair funcdata{list, types};
|
||||
Account* base_acct = gnc_book_get_root_account(book);
|
||||
gnc_account_foreach_descendant(base_acct, (AccountCb)find_children,
|
||||
&funcdata);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
|
||||
const char* section, const char* name,
|
||||
const char* key, const char* doc_string,
|
||||
std::vector<GncGUID> value)
|
||||
const GncOptionAccountList& value,
|
||||
GncOptionAccountTypeList&& allowed)
|
||||
{
|
||||
GncOption option{section, name, key, doc_string, value,
|
||||
GncOptionUIType::ACCOUNT_SEL};
|
||||
try
|
||||
{
|
||||
GncOption option{GncOptionAccountValue{section, name, key, doc_string,
|
||||
GncOptionUIType::ACCOUNT_SEL, value, std::move(allowed)}};
|
||||
db->register_option(section, std::move(option));
|
||||
}
|
||||
catch (const std::invalid_argument& err)
|
||||
{
|
||||
std::cerr <<"Account Sel Limited Option, value failed validation, option not registerd.\n";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -93,6 +93,26 @@ private:
|
||||
std::function<void(GncOptionUIItem*)> m_set_ui_value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract a list of accounts in the book having one of the GNCAccountTypes in
|
||||
* types.
|
||||
*
|
||||
* Note that in Scheme it's important to use this function and not to create a
|
||||
* list of accounts using gnc-get-descendants-sorted because the latter method
|
||||
* produces a SWIGTYPE for the accounts that's incompatible with the SWIGTYPE
|
||||
* used in this module.
|
||||
*
|
||||
* @param book The book whose accounts to search
|
||||
* @param types A std::vector of GNCAccountType containing the Account types to
|
||||
* include in ther result
|
||||
* @return A std::vector<const Account*> of all accounts in the book having the
|
||||
* Account types in the types parameter.
|
||||
*/
|
||||
GncOptionAccountList
|
||||
gnc_account_list_from_types(QofBook *book,
|
||||
const GncOptionAccountTypeList& types);
|
||||
|
||||
|
||||
using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
|
||||
/**
|
||||
* Create an empty option database.
|
||||
@@ -160,20 +180,21 @@ void gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section,
|
||||
* of guids. Externally, both guids and account pointers may be used to set the
|
||||
* value of the option. The option always returns a list of account pointers.
|
||||
*/
|
||||
void gnc_register_acount_list_limited_option(const GncOptionDBPtr& db,
|
||||
void gnc_register_account_list_limited_option(const GncOptionDBPtr& db,
|
||||
const char* section,
|
||||
const char* name, const char* key,
|
||||
const char* doc_string,
|
||||
std::vector<GncGUID> value);
|
||||
const GncOptionAccountList& value,
|
||||
GncOptionAccountTypeList&& allowed);
|
||||
|
||||
/* Just like gnc:make-account-list-limited-option except it does not limit the
|
||||
* types of accounts that are available to the user.
|
||||
*/
|
||||
void gnc_register_account_liat_option(const GncOptionDBPtr& db,
|
||||
void gnc_register_account_list_option(const GncOptionDBPtr& db,
|
||||
const char* section,
|
||||
const char* name, const char* key,
|
||||
const char* doc_string,
|
||||
std::vector<GncGUID> value);
|
||||
const GncOptionAccountList& value);
|
||||
|
||||
/* account-sel options use the option-data as a pair; the car is ignored, the
|
||||
* cdr is a list of account-types. If the cdr is an empty list, then all account
|
||||
@@ -185,7 +206,8 @@ void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
|
||||
const char* section,
|
||||
const char* name, const char* key,
|
||||
const char* doc_string,
|
||||
std::vector<GncGUID> value);
|
||||
const GncOptionAccountList& value,
|
||||
GncOptionAccountTypeList&& allowed);
|
||||
|
||||
/* Multichoice options use the option-data as a list of vectors. Each vector
|
||||
* contains a permissible value (scheme symbol), a name, and a description
|
||||
|
||||
@@ -57,10 +57,27 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
|
||||
*/
|
||||
%inline %{
|
||||
template <typename ValueType> inline SCM
|
||||
scm_from_value(ValueType value)
|
||||
{
|
||||
scm_from_value(ValueType value);
|
||||
/*{
|
||||
return SCM_BOOL_F;
|
||||
}*/
|
||||
template <> inline SCM
|
||||
scm_from_value<QofQuery*>(QofQuery* value)
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
template <> inline SCM
|
||||
scm_from_value<QofInstance*>(QofInstance* value)
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
template <> inline SCM
|
||||
scm_from_value<std::vector<GncGUID>>(std::vector<GncGUID> value)
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
template <> inline SCM
|
||||
scm_from_value<std::string>(std::string value)
|
||||
{
|
||||
@@ -103,24 +120,30 @@ scm_from_value<const QofInstance*>(const QofInstance* value)
|
||||
|
||||
/* Not needed now, the default template will do this
|
||||
template <> inline SCM
|
||||
scm_from_value<QofQuer*>(const QofQuery* value)
|
||||
scm_from_value<QofQuery*>(const QofQuery* value)
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
*/
|
||||
|
||||
/* Account is actually a typedef for struct account_s and SWIG insists on using
|
||||
* the struct name (i.e. account_s) in C++ and the alias (i.e. Account) in
|
||||
* C. Oddly the compiler's type resolution also fails to consider them the same
|
||||
* so we have to use the struct name here to get the template to resolve
|
||||
* correctly.
|
||||
*/
|
||||
using GncOptionAccount_sList = std::vector<const account_s*>;
|
||||
|
||||
template <>inline SCM
|
||||
scm_from_value<const std::vector<GncGUID>&>(const std::vector<GncGUID>& value)
|
||||
scm_from_value<GncOptionAccount_sList>(GncOptionAccount_sList value)
|
||||
{
|
||||
SCM s_list;
|
||||
for (auto guid : value)
|
||||
SCM s_list = SCM_EOL;
|
||||
for (auto acct : value)
|
||||
{
|
||||
auto guid_s = guid_to_string(qof_instance_get_guid(&guid));
|
||||
auto scm_guid = scm_from_utf8_string(guid_s);
|
||||
auto scm_guid_list1 = scm_list_1(scm_guid);
|
||||
s_list = scm_append(scm_list_2(s_list, scm_guid_list1));
|
||||
g_free(guid_s);
|
||||
SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0));
|
||||
s_list = scm_append(scm_list_2(s_list, elem));
|
||||
}
|
||||
|
||||
return s_list;
|
||||
}
|
||||
/* default template
|
||||
@@ -131,6 +154,7 @@ template <>inline SCM
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
template <typename ValueType> inline ValueType
|
||||
scm_to_value(SCM new_value)
|
||||
{
|
||||
@@ -167,8 +191,21 @@ scm_to_value<int64_t>(SCM new_value)
|
||||
return scm_to_int64(new_value);
|
||||
}
|
||||
|
||||
QofBook*
|
||||
qof_book_new()
|
||||
{
|
||||
return static_cast<QofBook*>(g_object_new(QOF_TYPE_BOOK, nullptr));
|
||||
}
|
||||
|
||||
void
|
||||
qof_book_destroy(QofBook* book)
|
||||
{
|
||||
g_object_unref(book);
|
||||
}
|
||||
|
||||
using Account = struct account_s;
|
||||
%}
|
||||
|
||||
%ignore OptionClassifier;
|
||||
%ignore OptionUIItem;
|
||||
%nodefaultctor GncOption;
|
||||
@@ -197,7 +234,77 @@ scm_to_value<int64_t>(SCM new_value)
|
||||
$1 = &choices;
|
||||
}
|
||||
|
||||
%typemap(in) GncOptionAccountTypeList& (GncOptionAccountTypeList types)
|
||||
{
|
||||
auto len = scm_to_size_t(scm_length($input));
|
||||
for (std::size_t i = 0; i < len; ++i)
|
||||
{
|
||||
SCM s_type = scm_list_ref($input, scm_from_size_t(i));
|
||||
GNCAccountType type = (GNCAccountType)scm_to_int(s_type);
|
||||
types.push_back(type);
|
||||
}
|
||||
$1 = &types;
|
||||
}
|
||||
|
||||
%typemap(in) GncOptionAccountTypeList&& (GncOptionAccountTypeList types)
|
||||
{
|
||||
auto len = scm_to_size_t(scm_length($input));
|
||||
for (std::size_t i = 0; i < len; ++i)
|
||||
{
|
||||
SCM s_type = scm_list_ref($input, scm_from_size_t(i));
|
||||
GNCAccountType type = (GNCAccountType)scm_to_int(s_type);
|
||||
types.push_back(type);
|
||||
}
|
||||
$1 = &types;
|
||||
}
|
||||
|
||||
%typemap(in) GncOptionAccountList
|
||||
{
|
||||
auto len = scm_to_size_t(scm_length($input));
|
||||
for (std::size_t i = 0; i < len; ++i)
|
||||
{
|
||||
SCM s_account = scm_list_ref($input, scm_from_size_t(i));
|
||||
Account* acct = (Account*)SWIG_MustGetPtr(s_account,
|
||||
SWIGTYPE_p_account_s, 1, 0);
|
||||
$1.push_back(acct);
|
||||
}
|
||||
}
|
||||
|
||||
%typemap(in) GncOptionAccountList& (GncOptionAccountList acclist)
|
||||
{
|
||||
auto len = scm_to_size_t(scm_length($input));
|
||||
for (std::size_t i = 0; i < len; ++i)
|
||||
{
|
||||
SCM s_account = scm_list_ref($input, scm_from_size_t(i));
|
||||
Account* acct = (Account*)SWIG_MustGetPtr(s_account,
|
||||
SWIGTYPE_p_account_s, 1, 0);
|
||||
acclist.push_back(acct);
|
||||
}
|
||||
$1 = &acclist;
|
||||
}
|
||||
|
||||
%typemap(out) GncOptionAccountList
|
||||
{
|
||||
$result = SCM_EOL;
|
||||
for (auto acct : $1)
|
||||
{
|
||||
SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0));
|
||||
$result = scm_append(scm_list_2($result, elem));
|
||||
}
|
||||
}
|
||||
|
||||
%typemap(out) GncOptionAccountList&
|
||||
{
|
||||
$result = SCM_EOL;
|
||||
for (auto acct : *$1)
|
||||
{
|
||||
SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0));
|
||||
$result = scm_append(scm_list_2($result, elem));
|
||||
}
|
||||
}
|
||||
|
||||
wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
|
||||
|
||||
%include "gnc-option.hpp"
|
||||
%include "gnc-optiondb.hpp"
|
||||
|
||||
|
||||
@@ -246,6 +246,149 @@ TEST_F(GncRangeOption, test_setter)
|
||||
EXPECT_EQ(1.5, m_doubleoption.get_default_value());
|
||||
}
|
||||
|
||||
using AccountPair = std::pair<GncOptionAccountList&,
|
||||
const GncOptionAccountTypeList&>;
|
||||
static void
|
||||
find_children(Account* account, void* data)
|
||||
{
|
||||
auto datapair =
|
||||
(AccountPair*)data;
|
||||
GncOptionAccountList& list = datapair->first;
|
||||
const GncOptionAccountTypeList& types = datapair->second;
|
||||
if (std::find(types.begin(), types.end(),
|
||||
xaccAccountGetType(account)) != types.end())
|
||||
list.push_back(account);
|
||||
}
|
||||
|
||||
class GncOptionAccountTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GncOptionAccountTest() :
|
||||
m_book{qof_book_new()}, m_root{gnc_account_create_root(m_book)}
|
||||
{
|
||||
auto create_account = [this](Account* parent, GNCAccountType type,
|
||||
const char* name)->Account* {
|
||||
auto account = xaccMallocAccount(this->m_book);
|
||||
xaccAccountBeginEdit(account);
|
||||
xaccAccountSetType(account, type);
|
||||
xaccAccountSetName(account, name);
|
||||
xaccAccountBeginEdit(parent);
|
||||
gnc_account_append_child(parent, account);
|
||||
xaccAccountCommitEdit(parent);
|
||||
xaccAccountCommitEdit(account);
|
||||
return account;
|
||||
};
|
||||
auto assets = create_account(m_root, ACCT_TYPE_ASSET, "Assets");
|
||||
auto liabilities = create_account(m_root, ACCT_TYPE_LIABILITY, "Liabilities");
|
||||
auto expenses = create_account(m_root, ACCT_TYPE_EXPENSE, "Expenses");
|
||||
create_account(assets, ACCT_TYPE_BANK, "Bank");
|
||||
auto broker = create_account(assets, ACCT_TYPE_ASSET, "Broker");
|
||||
auto stocks = create_account(broker, ACCT_TYPE_STOCK, "Stocks");
|
||||
create_account(stocks, ACCT_TYPE_STOCK, "AAPL");
|
||||
create_account(stocks, ACCT_TYPE_STOCK, "MSFT");
|
||||
create_account(stocks, ACCT_TYPE_STOCK, "HPE");
|
||||
create_account(broker, ACCT_TYPE_BANK, "Cash Management");
|
||||
create_account(expenses, ACCT_TYPE_EXPENSE, "Food");
|
||||
create_account(expenses, ACCT_TYPE_EXPENSE, "Gas");
|
||||
create_account(expenses, ACCT_TYPE_EXPENSE, "Rent");
|
||||
}
|
||||
~GncOptionAccountTest()
|
||||
{
|
||||
xaccAccountBeginEdit(m_root);
|
||||
xaccAccountDestroy(m_root); //It does the commit
|
||||
qof_book_destroy(m_book);
|
||||
}
|
||||
GncOptionAccountList list_of_types(const GncOptionAccountTypeList& types)
|
||||
{
|
||||
GncOptionAccountList list;
|
||||
AccountPair funcdata{list, types};
|
||||
gnc_account_foreach_descendant(m_root, (AccountCb)find_children,
|
||||
&funcdata);
|
||||
return list;
|
||||
}
|
||||
|
||||
QofBook* m_book;
|
||||
Account* m_root;
|
||||
};
|
||||
|
||||
TEST_F(GncOptionAccountTest, test_test_constructor_and_destructor)
|
||||
{
|
||||
EXPECT_TRUE(m_book != NULL);
|
||||
EXPECT_TRUE(QOF_IS_BOOK(m_book));
|
||||
EXPECT_TRUE(m_root != NULL);
|
||||
EXPECT_TRUE(GNC_IS_ACCOUNT(m_root));
|
||||
GncOptionAccountList list{list_of_types({ACCT_TYPE_BANK})};
|
||||
EXPECT_EQ(2, list.size());
|
||||
list = list_of_types({ACCT_TYPE_ASSET, ACCT_TYPE_STOCK});
|
||||
EXPECT_EQ(6, list.size());
|
||||
}
|
||||
|
||||
TEST_F(GncOptionAccountTest, test_option_no_value_constructor)
|
||||
{
|
||||
GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
|
||||
GncOptionUIType::ACCOUNT_LIST};
|
||||
EXPECT_TRUE(option.get_value().empty());
|
||||
EXPECT_TRUE(option.get_default_value().empty());
|
||||
}
|
||||
|
||||
TEST_F(GncOptionAccountTest, test_option_value_constructor)
|
||||
{
|
||||
GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
|
||||
GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
|
||||
GncOptionUIType::ACCOUNT_LIST, acclist};
|
||||
EXPECT_EQ(2, option.get_value().size());
|
||||
EXPECT_EQ(2, option.get_default_value().size());
|
||||
EXPECT_EQ(acclist[0], option.get_value()[0]);
|
||||
}
|
||||
|
||||
TEST_F(GncOptionAccountTest, test_option_no_value_limited_constructor)
|
||||
{
|
||||
GncOptionAccountList acclistgood{list_of_types({ACCT_TYPE_BANK})};
|
||||
GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
|
||||
GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
|
||||
GncOptionUIType::ACCOUNT_LIST, {ACCT_TYPE_BANK}};
|
||||
EXPECT_TRUE(option.get_value().empty());
|
||||
EXPECT_TRUE(option.get_default_value().empty());
|
||||
EXPECT_EQ(true, option.validate(acclistgood));
|
||||
EXPECT_EQ(false, option.validate(acclistbad));
|
||||
}
|
||||
|
||||
TEST_F(GncOptionAccountTest, test_option_value_limited_constructor)
|
||||
{
|
||||
GncOptionAccountList acclistgood{list_of_types({ACCT_TYPE_BANK})};
|
||||
GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
|
||||
EXPECT_THROW({
|
||||
GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
|
||||
GncOptionUIType::ACCOUNT_LIST,
|
||||
acclistbad, {ACCT_TYPE_BANK});
|
||||
}, std::invalid_argument);
|
||||
|
||||
EXPECT_THROW({
|
||||
GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
|
||||
GncOptionUIType::ACCOUNT_SEL,
|
||||
acclistgood, {ACCT_TYPE_BANK});
|
||||
}, std::invalid_argument);
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
|
||||
GncOptionUIType::ACCOUNT_LIST,
|
||||
acclistgood, {ACCT_TYPE_BANK});
|
||||
});
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
GncOptionAccountList accsel{acclistgood[0]};
|
||||
GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
|
||||
GncOptionUIType::ACCOUNT_LIST,
|
||||
accsel, {ACCT_TYPE_BANK});
|
||||
});
|
||||
GncOptionAccountValue option {"foo", "bar", "baz", "Bogus Option",
|
||||
GncOptionUIType::ACCOUNT_LIST, acclistgood, {ACCT_TYPE_BANK}};
|
||||
EXPECT_FALSE(option.get_value().empty());
|
||||
EXPECT_FALSE(option.get_default_value().empty());
|
||||
EXPECT_EQ(true, option.validate(acclistgood));
|
||||
EXPECT_EQ(false, option.validate(acclistbad));
|
||||
}
|
||||
|
||||
|
||||
class GncOptionMultichoiceTest : public ::testing::Test
|
||||
{
|
||||
|
||||
@@ -69,6 +69,100 @@ TEST_F(GncOptionDBTest, test_register_string_option)
|
||||
EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str());
|
||||
}
|
||||
|
||||
/* Note: The following test-fixture code is also present in slightly different
|
||||
* form in gtest-gnc-option.cpp.
|
||||
*/
|
||||
|
||||
|
||||
struct AccountTestBook
|
||||
{
|
||||
AccountTestBook() :
|
||||
m_book{qof_book_new()}, m_root{gnc_account_create_root(m_book)}
|
||||
{
|
||||
auto create_account = [this](Account* parent, GNCAccountType type,
|
||||
const char* name)->Account* {
|
||||
auto account = xaccMallocAccount(this->m_book);
|
||||
xaccAccountBeginEdit(account);
|
||||
xaccAccountSetType(account, type);
|
||||
xaccAccountSetName(account, name);
|
||||
xaccAccountBeginEdit(parent);
|
||||
gnc_account_append_child(parent, account);
|
||||
xaccAccountCommitEdit(parent);
|
||||
xaccAccountCommitEdit(account);
|
||||
return account;
|
||||
};
|
||||
auto assets = create_account(m_root, ACCT_TYPE_ASSET, "Assets");
|
||||
auto liabilities = create_account(m_root, ACCT_TYPE_LIABILITY, "Liabilities");
|
||||
auto expenses = create_account(m_root, ACCT_TYPE_EXPENSE, "Expenses");
|
||||
create_account(assets, ACCT_TYPE_BANK, "Bank");
|
||||
auto broker = create_account(assets, ACCT_TYPE_ASSET, "Broker");
|
||||
auto stocks = create_account(broker, ACCT_TYPE_STOCK, "Stocks");
|
||||
create_account(stocks, ACCT_TYPE_STOCK, "AAPL");
|
||||
create_account(stocks, ACCT_TYPE_STOCK, "MSFT");
|
||||
create_account(stocks, ACCT_TYPE_STOCK, "HPE");
|
||||
create_account(broker, ACCT_TYPE_BANK, "Cash Management");
|
||||
create_account(expenses, ACCT_TYPE_EXPENSE, "Food");
|
||||
create_account(expenses, ACCT_TYPE_EXPENSE, "Gas");
|
||||
create_account(expenses, ACCT_TYPE_EXPENSE, "Rent");
|
||||
}
|
||||
~AccountTestBook()
|
||||
{
|
||||
xaccAccountBeginEdit(m_root);
|
||||
xaccAccountDestroy(m_root); //It does the commit
|
||||
qof_book_destroy(m_book);
|
||||
}
|
||||
|
||||
QofBook* m_book;
|
||||
Account* m_root;
|
||||
};
|
||||
|
||||
TEST_F(GncOptionDBTest, test_register_account_list_option)
|
||||
{
|
||||
AccountTestBook book;
|
||||
auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
|
||||
gnc_register_account_list_option(m_db, "foo", "bar", "baz", "Phony Option",
|
||||
acclist);
|
||||
EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
|
||||
EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(3));
|
||||
}
|
||||
|
||||
TEST_F(GncOptionDBTest, test_register_account_list_limited_option)
|
||||
{
|
||||
AccountTestBook book;
|
||||
auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
|
||||
gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
|
||||
"Phony Option", acclist,
|
||||
{ACCT_TYPE_STOCK});
|
||||
EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
|
||||
EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(3));
|
||||
}
|
||||
|
||||
TEST_F(GncOptionDBTest, test_register_account_sel_limited_option)
|
||||
{
|
||||
AccountTestBook book;
|
||||
auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
|
||||
GncOptionAccountList accsel{acclist[2]};
|
||||
gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
|
||||
"Phony Option", accsel,
|
||||
{ACCT_TYPE_STOCK});
|
||||
EXPECT_EQ(1, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
|
||||
EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(0));
|
||||
}
|
||||
|
||||
TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct)
|
||||
{
|
||||
AccountTestBook book;
|
||||
auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
|
||||
GncOptionAccountList accsel{acclist[2]};
|
||||
gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option",
|
||||
accsel, {ACCT_TYPE_BANK});
|
||||
EXPECT_FALSE(m_db->find_option("foo", "bar"));
|
||||
gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
|
||||
"Phony Option", acclist,
|
||||
{ACCT_TYPE_BANK});
|
||||
EXPECT_FALSE(m_db->find_option("foo", "bar"));
|
||||
}
|
||||
|
||||
TEST_F(GncOptionDBTest, test_register_multichoice_option)
|
||||
{
|
||||
GncMultiChoiceOptionChoices choices{
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
(test-runner-factory gnc:test-runner)
|
||||
(test-begin "test-gnc-optiondb-scheme")
|
||||
(test-gnc-make-text-option)
|
||||
(test-gnc-make-account-list-options)
|
||||
(test-gnc-make-multichoice-option)
|
||||
(test-gnc-make-list-option)
|
||||
(test-gnc-make-date-option)
|
||||
@@ -52,6 +53,86 @@
|
||||
(test-equal "pepper" (gnc-option-value option-db "foo" "bar")))
|
||||
(test-end "test-gnc-make-string-option"))
|
||||
|
||||
(define (test-gnc-make-account-list-options)
|
||||
(define (create-account book parent type name)
|
||||
(let ((account (xaccMallocAccount book)))
|
||||
(xaccAccountBeginEdit account)
|
||||
(xaccAccountSetType account type)
|
||||
(xaccAccountSetName account name)
|
||||
(xaccAccountBeginEdit parent)
|
||||
(gnc-account-append-child parent account)
|
||||
(xaccAccountCommitEdit parent)
|
||||
(xaccAccountCommitEdit account)
|
||||
account))
|
||||
|
||||
(define (make-account-tree book root)
|
||||
(let* ((assets (create-account book root ACCT-TYPE-ASSET "Assets"))
|
||||
(liabilities (create-account book root ACCT-TYPE-LIABILITY "Liabilities"))
|
||||
(equity (create-account book root ACCT-TYPE-EQUITY "Equity"))
|
||||
(expenses (create-account book root ACCT-TYPE-EXPENSE "Expenses"))
|
||||
(equity (create-account book root ACCT-TYPE-INCOME "Income"))
|
||||
(broker (create-account book assets ACCT-TYPE-EQUITY "broker"))
|
||||
(stocks (create-account book broker ACCT-TYPE-STOCK "Stocks")))
|
||||
(create-account book assets ACCT-TYPE-BANK "Bank")
|
||||
(create-account book stocks ACCT-TYPE-STOCK "AAPL")
|
||||
(create-account book stocks ACCT-TYPE-STOCK "MSFT")
|
||||
(create-account book stocks ACCT-TYPE-STOCK "HPE")
|
||||
(create-account book broker ACCT-TYPE-BANK "Cash Management")
|
||||
(create-account book expenses ACCT-TYPE-EXPENSE "Food")
|
||||
(create-account book expenses ACCT-TYPE-EXPENSE "Gas")
|
||||
(create-account book expenses ACCT-TYPE-EXPENSE "Rent")))
|
||||
|
||||
(define (cleanup book root)
|
||||
(xaccAccountBeginEdit root)
|
||||
(xaccAccountDestroy root)
|
||||
(qof-book-destroy book))
|
||||
|
||||
(define (test-make-account-list-option book)
|
||||
(test-group "test-make-account-list-option"
|
||||
(let ((optiondb (gnc-option-db-new))
|
||||
(acctlist (gnc-account-list-from-types book
|
||||
(list ACCT-TYPE-STOCK))))
|
||||
(gnc-register-account-list-option optiondb "foo" "bar" "baz"
|
||||
"Phony Option" acctlist)
|
||||
(let ((acct-list (gnc-option-value optiondb "foo" "bar")))
|
||||
(test-equal (length acctlist) (length acct-list))
|
||||
(test-equal (car acctlist) (car acct-list))) )))
|
||||
|
||||
(define (test-make-account-list-limited-option book)
|
||||
(test-group "test-make-account-list-option"
|
||||
(let ((optiondb (gnc-option-db-new))
|
||||
(acctlist (gnc-account-list-from-types book
|
||||
(list ACCT-TYPE-STOCK))))
|
||||
(gnc-register-account-list-limited-option optiondb "foo" "bar" "baz"
|
||||
"Phony Option" acctlist (list ACCT-TYPE-STOCK))
|
||||
(let ((acct-list (gnc-option-value optiondb "foo" "bar")))
|
||||
(test-equal (length acctlist) (length acct-list))
|
||||
(test-equal (cadr acctlist) (cadr acct-list)))
|
||||
(gnc-register-account-list-limited-option optiondb "waldo" "pepper" "baz"
|
||||
"Phony Option" acctlist (list ACCT-TYPE-BANK))
|
||||
(let ((acct-list (gnc-option-value optiondb "waldo" "pepper")))
|
||||
(test-equal #f (length acct-list))))))
|
||||
|
||||
(define (test-make-account-sel-limited-option book)
|
||||
(test-group "test-make-account-list-option"
|
||||
(let ((optiondb (gnc-option-db-new))
|
||||
(acctlist (gnc-account-list-from-types book
|
||||
(list ACCT-TYPE-STOCK))))
|
||||
(gnc-register-account-sel-limited-option optiondb "salt" "pork" "baz"
|
||||
"Phony Option" (list (cadr acctlist)) (list ACCT-TYPE-STOCK))
|
||||
(let ((acct (gnc-option-value optiondb "salt" "pork")))
|
||||
(test-equal (list (cadr acctlist)) acct)))))
|
||||
|
||||
(let* ((book (qof-book-new))
|
||||
(root-account (gnc-account-create-root book)))
|
||||
(test-group-with-cleanup "test-gnc-make-account-list-options"
|
||||
(make-account-tree book root-account)
|
||||
(test-make-account-list-option book)
|
||||
(test-make-account-list-limited-option book)
|
||||
(test-make-account-sel-limited-option book)
|
||||
(cleanup book root-account))))
|
||||
|
||||
|
||||
(define (test-gnc-make-multichoice-option)
|
||||
|
||||
(define (keylist->vectorlist keylist)
|
||||
|
||||
Reference in New Issue
Block a user