From 5fdf14f8cbd024a4e7ff8f7800ffd5b44fb5449c Mon Sep 17 00:00:00 2001 From: Alex Aycinena Date: Sat, 27 Jun 2015 16:06:56 -0700 Subject: [PATCH] Book-Currency Feature Step 3 Add an option to the File->Properties dialog to select a Currency Accounting Method, and to specify the book-currency and a default gains/loss policy if the 'book-currency' method is selected. Revert some changes made in prior commits. Other than storing and retrieving the book-currency and policy in book KVPs, the behavior of gnucash has not been affected by these changes. The changes made are: engine/policy.c & .h - define valid policies and their descriptions and hints; define functions gnc_get_valid_policy_list and gnc_valid_policy engine/engine.i - include policy.h; define 'CURRENCY-ACCOUNTING', 'BOOK-CURRENCY', and 'DEFAULT-GAINS-POLICY' as option names engine/test/utest-Split.cpp - revert prior commit that modified test function for trading accounts engine/test/utest-Transaction.c - revert prior commit that modified test function for trading accounts libqof/qof/qofbookslots.h - revert prior commit that defined Currency Accounting Method; modify definitions of Currency Accounting and Book Currency; add definition of default gains policy; keep the previous Trading Accounts definition libqof/qof/qofbook.cpp & .h - reverted function to determine currency accounting method; add function to get book-currency, revert previously committed changes to function to determine if book uses trading accounts; add function to get gains policy; modify gobject properties accordingly libqof/qof/test/test-qofbook.c - revert prior modification to test function for trading accounts; define tests for book-currency and default gains policies app-utils/options.scm - define make-currency-accounting-option and required support functions app-utils/options-util.c & .h - make support functions of make-currency-accounting-option available in c code app-utils/test/test-options-util.c - include tests for using book-currency app-utils/gnc-ui-util.c & .h - define gnc_book_use_book_currency, gnc_book_get_book_currency and gnc_book_get_default_gains_policy functions app-utils/test/test-gnc-ui-util.c - set up tests for new gnc-ui-util functions app-utils/test/test-app-utils.c - include test-gnc-ui-util.c app-utils/test/Makefile.am - include test-gnc-ui-util.c app-utils/app-utils.scm - define and export book options for Currency Accounting, Book-Currency and Default Gains Policy; export required currency-accounting-option support functions app-utils/business-prefs.scm - replace the Trading Accounts boolean option with a currency-accounting-option and rearrange the order of the options gnome-utils/dialog-options.c - define functions for set-ui-widget, set-ui-value, and get-ui-value for 'currency-accounting' option and various other support functions --- src/app-utils/app-utils.scm | 12 + src/app-utils/business-prefs.scm | 31 +- src/app-utils/gnc-ui-util.c | 63 +++ src/app-utils/gnc-ui-util.h | 17 + src/app-utils/option-util.c | 153 ++++++- src/app-utils/option-util.h | 9 + src/app-utils/options.scm | 211 ++++++++++ src/app-utils/test/Makefile.am | 4 +- src/app-utils/test/test-app-utils.c | 2 + src/app-utils/test/test-gnc-ui-util.c | 216 ++++++++++ src/app-utils/test/test-option-util.c | 132 +++++- src/engine/engine.i | 5 + src/engine/policy.c | 77 ++++ src/engine/policy.h | 17 + src/engine/test/utest-Split.cpp | 2 +- src/engine/test/utest-Transaction.c | 4 +- src/gnome-utils/dialog-options.c | 561 ++++++++++++++++++++++++++ src/libqof/qof/qofbook.cpp | 224 ++++------ src/libqof/qof/qofbook.h | 16 +- src/libqof/qof/qofbookslots.h | 6 +- src/libqof/qof/test/test-qofbook.c | 127 +++--- 21 files changed, 1671 insertions(+), 218 deletions(-) create mode 100644 src/app-utils/test/test-gnc-ui-util.c diff --git a/src/app-utils/app-utils.scm b/src/app-utils/app-utils.scm index c62e93ac7b..ccd78f9300 100644 --- a/src/app-utils/app-utils.scm +++ b/src/app-utils/app-utils.scm @@ -114,6 +114,13 @@ (export gnc:make-color-option) (export gnc:make-dateformat-option) (export gnc:dateformat-get-format) +(export gnc:currency-accounting-option-get-curr-doc-string) +(export gnc:currency-accounting-option-get-default-curr) +(export gnc:currency-accounting-option-get-policy-doc-string) +(export gnc:currency-accounting-option-get-default-policy) +(export gnc:currency-accounting-option-selected-method) +(export gnc:currency-accounting-option-selected-currency) +(export gnc:currency-accounting-option-selected-policy) (export gnc:color->html) (export gnc:color-option->html) @@ -322,10 +329,15 @@ (define gnc:*option-section-accounts* OPTION-SECTION-ACCOUNTS) (define gnc:*option-name-trading-accounts* OPTION-NAME-TRADING-ACCOUNTS) +(define gnc:*option-name-currency-accounting* OPTION-NAME-CURRENCY-ACCOUNTING) +(define gnc:*option-name-book-currency* OPTION-NAME-BOOK-CURRENCY) +(define gnc:*option-name-default-gains-policy* OPTION-NAME-DEFAULT-GAINS-POLICY) (define gnc:*option-name-auto-readonly-days* OPTION-NAME-AUTO-READONLY-DAYS) (define gnc:*option-name-num-field-source* OPTION-NAME-NUM-FIELD-SOURCE) (export gnc:*option-section-accounts* gnc:*option-name-trading-accounts* + gnc:*option-name-currency-accounting* + gnc:*option-name-book-currency* gnc:*option-name-default-gains-policy* gnc:*option-name-auto-readonly-days* gnc:*option-name-num-field-source*) (define gnc:*option-section-budgeting* OPTION-SECTION-BUDGETING) diff --git a/src/app-utils/business-prefs.scm b/src/app-utils/business-prefs.scm index a49ab435cd..d37b4dc8e5 100644 --- a/src/app-utils/business-prefs.scm +++ b/src/app-utils/business-prefs.scm @@ -127,16 +127,10 @@ ;; Accounts tab - (reg-option - (gnc:make-simple-boolean-option - gnc:*option-section-accounts* gnc:*option-name-trading-accounts* - "a" (N_ "Check to have trading accounts used for transactions involving more than one currency or commodity.") - #f)) - (reg-option (gnc:make-number-range-option gnc:*option-section-accounts* gnc:*option-name-auto-readonly-days* - "b" (N_ "Choose the number of days after which transactions will be read-only and cannot be edited anymore. This threshold is marked by a red line in the account register windows. If zero, all transactions can be edited and none are read-only.") + "a" (N_ "Choose the number of days after which transactions will be read-only and cannot be edited anymore. This threshold is marked by a red line in the account register windows. If zero, all transactions can be edited and none are read-only.") 0 ;; default 0 ;; lower bound 3650 ;; upper bound @@ -147,9 +141,30 @@ (reg-option (gnc:make-simple-boolean-option gnc:*option-section-accounts* gnc:*option-name-num-field-source* - "c" (N_ "Check to have split action field used in registers for 'Num' field in place of transaction number; transaction number shown as 'T-Num' on second line of register. Has corresponding effect on business features, reporting and imports/exports.") + "b" (N_ "Check to have split action field used in registers for 'Num' field in place of transaction number; transaction number shown as 'T-Num' on second line of register. Has corresponding effect on business features, reporting and imports/exports.") #f)) + (reg-option + (gnc:make-currency-accounting-option + gnc:*option-section-accounts* gnc:*option-name-currency-accounting* + "c" + (N_ "Select the currency accounting method to use for transactions involving more than one currency or commodity.") + 'neither + (list + (vector 'trading + (N_ "Use Trading Accounts") + (N_ "Check to have trading accounts used for transactions involving more than one currency or commodity.")) + (vector 'book-currency + (N_ "Use a Book-Currency") + (N_ "Check to use a book-currency for transactions involving more than one currency or commodity.")) + (vector 'neither + (N_ "Use neither Trading Accounts nor a Book-Currency") + (N_ "Check to use neither trading accounts nor a book-currency for transactions involving more than one currency or commodity."))) + (N_ "Select the book-currency which is to be used to track costs of transactions involving currencies or commodities other than the book-currency.") + (gnc-default-currency) + (N_ "Select the default gains policy; this policy will be used unless over-ridden at the account level.") + 'fifo)) + ;; Budgeting Tab (reg-option diff --git a/src/app-utils/gnc-ui-util.c b/src/app-utils/gnc-ui-util.c index 73f3bd85fb..72d8c51fa5 100644 --- a/src/app-utils/gnc-ui-util.c +++ b/src/app-utils/gnc-ui-util.c @@ -266,6 +266,69 @@ gnc_book_option_num_field_source_change_cb (gboolean num_action) gnc_gui_refresh_all (); } +/** Returns TRUE if both book-currency and default gain/loss policy KVPs exist + * and are valid and trading accounts are not used. */ +gboolean +gnc_book_use_book_currency (QofBook *book) +{ + const gchar *policy; + const gchar *currency; + + if (!book) return FALSE; + + policy = qof_book_get_default_gains_policy (book); + currency = qof_book_get_book_currency (book); + + /* If either a default gain/loss policy or a book-currency does not exist, + book-currency accounting method not valid */ + if (!policy || !currency) + return FALSE; + + /* If both exist, both must be valid */ + if (!gnc_valid_policy (policy) || !gnc_commodity_table_lookup + (gnc_commodity_table_get_table + (gnc_get_current_book()), + GNC_COMMODITY_NS_CURRENCY, + currency)) + return FALSE; + + /* If both exist and are valid, there must be no trading accounts flag */ + if (qof_book_use_trading_accounts (book)) + return FALSE; + + return TRUE; +} + +/** Returns pointer to Book Currency name for book or NULL; determines + * that both book-currency and default gain/loss policy KVPs exist and that + * both are valid, a requirement for the 'book-currency' currency accounting + * method to apply. */ +const gchar * +gnc_book_get_book_currency (QofBook *book) +{ + if (!book) return NULL; + + if (gnc_book_use_book_currency (book)) + return qof_book_get_book_currency (book); + + return NULL; +} + +/** Returns pointer to default gain/loss policy for book or NULL; determines + * that both book-currency and default gain/loss policy KVPs exist and that + * both are valid, a requirement for the 'book-currency' currency accounting + * method to apply. */ +const gchar * +gnc_book_get_default_gains_policy (QofBook *book) +{ + if (!book) return NULL; + + if (gnc_book_use_book_currency (book)) + return qof_book_get_default_gains_policy (book); + + return NULL; +} + Account * gnc_get_current_root_account (void) { diff --git a/src/app-utils/gnc-ui-util.h b/src/app-utils/gnc-ui-util.h index 281ffc69e5..13701ae763 100644 --- a/src/app-utils/gnc-ui-util.h +++ b/src/app-utils/gnc-ui-util.h @@ -80,6 +80,23 @@ const gchar * gnc_get_current_book_tax_type (void); * callbacks when num_field_source book option changes so that * registers/reports can update themselves; sets feature flag */ void gnc_book_option_num_field_source_change_cb (gboolean num_action); + +/** Returns TRUE if both book-currency and default gain/loss policy KVPs exist + * and are valid and trading accounts are not used */ +gboolean gnc_book_use_book_currency (QofBook *book); + +/** Returns pointer to Book Currency name for book or NULL; determines + * that both book-currency and default gain/loss policy KVPs exist and that + * both are valid, a requirement for the 'book-currency' currency accounting + * method to apply. */ +const gchar * gnc_book_get_book_currency (QofBook *book); + +/** Returns pointer to default gain/loss policy for book or NULL; determines + * that both book-currency and default gain/loss policy KVPs exist and that + * both are valid, a requirement for the 'book-currency' currency accounting + * method to apply. */ +const gchar * gnc_book_get_default_gains_policy (QofBook *book); + Account * gnc_get_current_root_account (void); gnc_commodity_table * gnc_get_current_commodities (void); diff --git a/src/app-utils/option-util.c b/src/app-utils/option-util.c index a1f03d6051..829749e682 100644 --- a/src/app-utils/option-util.c +++ b/src/app-utils/option-util.c @@ -31,7 +31,6 @@ #include "Account.h" #include "option-util.h" -#include "engine-helpers-guile.h" #include "glib-helpers.h" #include "gnc-guile-utils.h" #include "qof.h" @@ -109,6 +108,13 @@ struct _Getters SCM date_option_value_type; SCM date_option_value_absolute; SCM date_option_value_relative; + SCM currency_accounting_option_currency_doc_string; + SCM currency_accounting_option_default_currency; + SCM currency_accounting_option_policy_doc_string; + SCM currency_accounting_option_default_policy; + SCM currency_accounting_option_method; + SCM currency_accounting_option_book_currency; + SCM currency_accounting_option_selected_default_policy; }; @@ -577,6 +583,20 @@ initialize_getters(void) scm_c_eval_string("gnc:date-option-absolute-time"); getters.date_option_value_relative = scm_c_eval_string("gnc:date-option-relative-time"); + getters.currency_accounting_option_currency_doc_string = + scm_c_eval_string("gnc:currency-accounting-option-get-curr-doc-string"); + getters.currency_accounting_option_default_currency = + scm_c_eval_string("gnc:currency-accounting-option-get-default-curr"); + getters.currency_accounting_option_policy_doc_string = + scm_c_eval_string("gnc:currency-accounting-option-get-policy-doc-string"); + getters.currency_accounting_option_default_policy = + scm_c_eval_string("gnc:currency-accounting-option-get-default-policy"); + getters.currency_accounting_option_method = + scm_c_eval_string("gnc:currency-accounting-option-selected-method"); + getters.currency_accounting_option_book_currency = + scm_c_eval_string("gnc:currency-accounting-option-selected-currency"); + getters.currency_accounting_option_selected_default_policy = + scm_c_eval_string("gnc:currency-accounting-option-selected-policy"); getters_initialized = TRUE; } @@ -653,8 +673,8 @@ gnc_option_sort_tag(GNCOption *option) /********************************************************************\ * gnc_option_documentation * - * returns the malloc'ed sort tag of the option, or NULL * - * if it can't be retrieved. * + * returns the malloc'ed documentation string of the option, or * + * NULL if it can't be retrieved. * * * * Args: option - the GNCOption * * Returns: malloc'ed char * or NULL * @@ -2634,6 +2654,133 @@ gnc_date_option_value_get_relative (SCM option_value) return scm_call_1 (getters.date_option_value_relative, option_value); } +/********************************************************************\ + * gnc_currency_accounting_option_currency_documentation * + * returns the malloc'ed documentation string for currency * + * selector of the currency-accounting option, or NULL if it * + * can't be retrieved. * + * * + * Args: option - the GNCOption * + * Returns: malloc'ed char * or NULL * +\********************************************************************/ +char * +gnc_currency_accounting_option_currency_documentation(GNCOption *option) +{ + initialize_getters(); + + return gnc_scm_call_1_to_string + (getters.currency_accounting_option_currency_doc_string, + option->guile_option); +} + + +/********************************************************************\ + * gnc_currency_accounting_option_get_default_currency * + * returns the SCM value for the currency-accounting option * + * default currency. * + * * + * Args: option - the GNCOption * + * Returns: SCM value * +\********************************************************************/ +SCM +gnc_currency_accounting_option_get_default_currency(GNCOption *option) +{ + initialize_getters(); + + return scm_call_1 + (getters.currency_accounting_option_default_currency, + option->guile_option); +} + + +/********************************************************************\ + * gnc_currency_accounting_option_policy_documentation * + * returns the malloc'ed documentation string for policy * + * selector of the currency-accounting option, or NULL if it * + * can't be retrieved. * + * * + * Args: option - the GNCOption * + * Returns: malloc'ed char * or NULL * +\********************************************************************/ +char * +gnc_currency_accounting_option_policy_documentation(GNCOption *option) +{ + initialize_getters(); + + return gnc_scm_call_1_to_string + (getters.currency_accounting_option_policy_doc_string, + option->guile_option); +} + + +/********************************************************************\ + * gnc_currency_accounting_option_get_default_policy * + * returns the SCM value for the currency-accounting option * + * default policy. * + * * + * Args: option - the GNCOption * + * Returns: SCM value * +\********************************************************************/ +SCM +gnc_currency_accounting_option_get_default_policy(GNCOption *option) +{ + initialize_getters(); + + return scm_call_1 + (getters.currency_accounting_option_default_policy, + option->guile_option); +} + + +/*******************************************************************\ + * gnc_currency_accounting_option_value_get_method * + * get the currency accounting method of the option as a symbol * + * * + * Args: option_value - option value to get method of * + * Return: SCM value * +\*******************************************************************/ +SCM +gnc_currency_accounting_option_value_get_method (SCM option_value) +{ + initialize_getters(); + + return scm_call_1 (getters.currency_accounting_option_method, option_value); +} + +/*******************************************************************\ + * gnc_currency_accounting_option_value_get_book_currency * + * get the book-currency if that is the currency accounting * + * method of the option as a symbol * + * * + * Args: option_value - option value to get method of * + * Return: SCM value * +\*******************************************************************/ +SCM +gnc_currency_accounting_option_value_get_book_currency (SCM option_value) +{ + initialize_getters(); + + return scm_call_1 (getters.currency_accounting_option_book_currency, option_value); +} + +/*******************************************************************\ + * gnc_currency_accounting_option_value_get_default_policy * + * get the default policy if book-currency is the currency * + * accounting method of the option as a symbol * + * * + * Args: option_value - option value to get method of * + * Return: SCM value * +\*******************************************************************/ +SCM +gnc_currency_accounting_option_value_get_default_policy (SCM option_value) +{ + initialize_getters(); + + return scm_call_1 + (getters.currency_accounting_option_selected_default_policy, + option_value); +} + /*******************************************************************\ * gnc_option_db_set_option_selectable_by_name * * set the sensitivity of the option widget * diff --git a/src/app-utils/option-util.h b/src/app-utils/option-util.h index b3b29b94f0..5b16c7d2a8 100644 --- a/src/app-utils/option-util.h +++ b/src/app-utils/option-util.h @@ -29,6 +29,7 @@ #include "guile-mappings.h" #include "gnc-commodity.h" +#include "engine-helpers-guile.h" #include "qof.h" typedef struct gnc_option GNCOption; @@ -251,6 +252,14 @@ char * gnc_date_option_value_get_type (SCM option_value); Timespec gnc_date_option_value_get_absolute (SCM option_value); SCM gnc_date_option_value_get_relative (SCM option_value); +char * gnc_currency_accounting_option_currency_documentation(GNCOption *option); +SCM gnc_currency_accounting_option_get_default_currency(GNCOption *option); +char * gnc_currency_accounting_option_policy_documentation(GNCOption *option); +SCM gnc_currency_accounting_option_get_default_policy(GNCOption *option); +SCM gnc_currency_accounting_option_value_get_method (SCM option_value); +SCM gnc_currency_accounting_option_value_get_book_currency (SCM option_value); +SCM gnc_currency_accounting_option_value_get_default_policy (SCM option_value); + void gnc_option_db_set_option_selectable_by_name(SCM guile_options, const char *section, const char *name, diff --git a/src/app-utils/options.scm b/src/app-utils/options.scm index 1019dfd406..1ed4dda7cc 100644 --- a/src/app-utils/options.scm +++ b/src/app-utils/options.scm @@ -1313,6 +1313,217 @@ (define (gnc:dateformat-get-format v) (cadddr v)) +(define (gnc:make-currency-accounting-option + section + name + sort-tag + radiobutton-documentation-string + default-radiobutton-value + ok-radiobutton-values + book-currency-documentation-string + default-book-currency-value + default-cap-gains-policy-documentation-string + default-cap-gains-policy-value + ) + (define (legal-val val p-vals) + (cond ((null? p-vals) #f) + ((not (symbol? val)) #f) + ((eq? val (vector-ref (car p-vals) 0)) #t) + (else (legal-val val (cdr p-vals))))) + + (define (currency-lookup currency-string) + (if (string? currency-string) + (gnc-commodity-table-lookup + (gnc-commodity-table-get-table (gnc-get-current-book)) + GNC_COMMODITY_NS_CURRENCY currency-string) + #f)) + + (define (currency? val) + (gnc-commodity-is-currency (currency-lookup val))) + + (define (vector-strings p-vals) + (if (null? p-vals) + '() + (cons (vector-ref (car p-vals) 1) + (cons (vector-ref (car p-vals) 2) + (vector-strings (cdr p-vals)))))) + + (define (currency->scm currency) + (if (string? currency) + currency + (gnc-commodity-get-mnemonic currency))) + + (define (scm->currency currency) + (currency-lookup currency)) + + (let* ((value (if (eq? 'book-currency default-radiobutton-value) + (cons default-radiobutton-value + (cons default-book-currency-value + (cons default-cap-gains-policy-value '()))) + (cons default-radiobutton-value '()))) + (value->string (lambda () + (string-append "'" (gnc:value->string + (car value))))) + (trading-accounts-path (list (car gnc:*kvp-option-path*) + gnc:*option-section-accounts* + gnc:*option-name-trading-accounts*)) + (book-currency-path (list (car gnc:*kvp-option-path*) + gnc:*option-section-accounts* + gnc:*option-name-book-currency*)) + (gains-policy-path (list (car gnc:*kvp-option-path*) + gnc:*option-section-accounts* + gnc:*option-name-default-gains-policy*))) + (gnc:make-option + section name sort-tag 'currency-accounting + radiobutton-documentation-string + (lambda () value) + (lambda (x) + (if (legal-val (car x) ok-radiobutton-values) + (set! value x) + (gnc:error "Illegal Radiobutton option set"))) + (lambda () (if (eq? 'book-currency default-radiobutton-value) + (cons default-radiobutton-value + (cons default-book-currency-value + (cons default-cap-gains-policy-value '()))) + (cons default-radiobutton-value '()))) + (gnc:restore-form-generator value->string) + (lambda (f p) + (if (eq? 'book-currency (car value)) + (begin + ;; Currency = selected currency + (kvp-frame-set-slot-path-gslist + f + (currency->scm (cadr value)) + book-currency-path) + ;; Default Gains Policy = selected policy + (kvp-frame-set-slot-path-gslist + f + (symbol->string (caddr value)) + gains-policy-path)) + (if (eq? 'trading (car value)) + ;; Use Trading Accounts = "t" + (kvp-frame-set-slot-path-gslist f "t" trading-accounts-path)))) + (lambda (f p) + (let* ((trading-option-path-kvp? + (kvp-frame-get-slot-path-gslist + f trading-accounts-path)) + (trading? (if (and trading-option-path-kvp? + (string=? "t" trading-option-path-kvp?)) + #t + #f)) + (book-currency #f) + (cap-gains-policy #f) + (v (if trading? + 'trading + (let* ((book-currency-option-path-kvp? + (kvp-frame-get-slot-path-gslist + f book-currency-path)) + (gains-policy-option-path-kvp? + (kvp-frame-get-slot-path-gslist + f gains-policy-path)) + (book-currency? + (if (and book-currency-option-path-kvp? + gains-policy-option-path-kvp? + (string? + book-currency-option-path-kvp?) + (string? + gains-policy-option-path-kvp?) + (if book-currency-option-path-kvp? + (currency? + book-currency-option-path-kvp?)) + (if gains-policy-option-path-kvp? + (gnc-valid-policy + gains-policy-option-path-kvp?))) + (begin + (set! book-currency + book-currency-option-path-kvp?) + (set! cap-gains-policy + gains-policy-option-path-kvp?) + #t) + #f))) + (if book-currency? + 'book-currency + 'neither))))) + (if (and v (symbol? v) (legal-val v ok-radiobutton-values)) + (set! value (cons v (if (eq? 'book-currency v) + (list (scm->currency book-currency) + (string->symbol cap-gains-policy)) + '()))) + (set! value (cons 'neither '()))))) + (lambda (x) + (if (list? x) + (if (legal-val (car x) ok-radiobutton-values) + (if (eq? 'book-currency (car x)) + (if (currency? (currency->scm (cadr x))) + (if (gnc-valid-policy (symbol->string (caddr x))) + (list #t x) + (list #f "cap-gains-policy-option: illegal value")) + (list #f "currency-option: illegal value")) + (list #t x)) + (list #f "radiobutton-option: illegal choice")) + (list #f "value not a list"))) + (vector book-currency-documentation-string + default-book-currency-value + default-cap-gains-policy-documentation-string + default-cap-gains-policy-value) + (vector (lambda () (length ok-radiobutton-values)) + (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 0)) + (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 1)) + (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 2)) + (lambda (x) + (gnc:multichoice-list-lookup ok-radiobutton-values x))) + (lambda () (vector-strings ok-radiobutton-values)) + #f))) + +(define (gnc:get-currency-accounting-option-data-curr-doc-string option-data) + (vector-ref option-data 0)) + +(define (gnc:get-currency-accounting-option-data-default-curr option-data) + (vector-ref option-data 1)) + +(define (gnc:get-currency-accounting-option-data-policy-doc-string option-data) + (vector-ref option-data 2)) + +(define (gnc:get-currency-accounting-option-data-policy-default option-data) + (vector-ref option-data 3)) + +(define (gnc:currency-accounting-option-get-curr-doc-string option) + (if (eq? (gnc:option-type option) 'currency-accounting) + (gnc:get-currency-accounting-option-data-curr-doc-string + (gnc:option-data option)) + (gnc:error "Not a currency accounting option"))) + +(define (gnc:currency-accounting-option-get-default-curr option) + (if (eq? (gnc:option-type option) 'currency-accounting) + (gnc:get-currency-accounting-option-data-default-curr + (gnc:option-data option)) + (gnc:error "Not a currency accounting option"))) + +(define (gnc:currency-accounting-option-get-policy-doc-string option) + (if (eq? (gnc:option-type option) 'currency-accounting) + (gnc:get-currency-accounting-option-data-policy-doc-string + (gnc:option-data option)) + (gnc:error "Not a currency accounting option"))) + +(define (gnc:currency-accounting-option-get-default-policy option) + (if (eq? (gnc:option-type option) 'currency-accounting) + (gnc:get-currency-accounting-option-data-policy-default + (gnc:option-data option)) + (gnc:error "Not a currency accounting option"))) + +(define (gnc:currency-accounting-option-selected-method option-value) + (car option-value)) + +(define (gnc:currency-accounting-option-selected-currency option-value) + (if (eq? (car option-value) 'book-currency) + (cadr option-value) + #f)) + +(define (gnc:currency-accounting-option-selected-policy option-value) + (if (eq? (car option-value) 'book-currency) + (caddr option-value) + #f)) + ;; Create a new options database (define (gnc:new-options) (define option-hash (make-hash-table 23)) diff --git a/src/app-utils/test/Makefile.am b/src/app-utils/test/Makefile.am index ab7822b918..4000490d74 100644 --- a/src/app-utils/test/Makefile.am +++ b/src/app-utils/test/Makefile.am @@ -67,7 +67,9 @@ AM_CPPFLAGS = \ test_app_utils_SOURCES = \ test-app-utils.c \ - test-option-util.c + test-option-util.c \ + test-gnc-ui-util.c + test_app_utils_CFLAGS = \ ${DEFAULT_INCLUDES} \ diff --git a/src/app-utils/test/test-app-utils.c b/src/app-utils/test/test-app-utils.c index 123494e5c3..03ea193621 100644 --- a/src/app-utils/test/test-app-utils.c +++ b/src/app-utils/test/test-app-utils.c @@ -27,6 +27,7 @@ #include extern void test_suite_option_util (void); +extern void test_suite_gnc_ui_util (void); static void guile_main (void *closure, int argc, char **argv) @@ -37,6 +38,7 @@ guile_main (void *closure, int argc, char **argv) mod = gnc_module_load ("gnucash/app-utils", 0); test_suite_option_util (); + test_suite_gnc_ui_util (); retval = g_test_run (); exit (retval); diff --git a/src/app-utils/test/test-gnc-ui-util.c b/src/app-utils/test/test-gnc-ui-util.c new file mode 100644 index 0000000000..a6a84cb755 --- /dev/null +++ b/src/app-utils/test/test-gnc-ui-util.c @@ -0,0 +1,216 @@ +/******************************************************************** + * test-gnc-ui-util.c: GLib g_test test suite for gnc-ui-util.c. * + * Copyright 2015 Alex Aycinena * + * * + * 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, you can retrieve it from * + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * + * or 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 +#include +#include +#include +/*#include */ + +#include "../gnc-ui-util.h" + +static const gchar *suitename = "/app-utils/gnc-ui-util"; +void test_suite_gnc_ui_util (void); + +typedef struct +{ + QofBook *book; + GSList *hdlrs; +} Fixture; + +/* Expose a mostly-private QofInstance function to load options into + * the Book. + */ +/*extern KvpFrame *qof_instance_get_slots (const QofInstance*); */ + +static void +setup (Fixture *fixture, gconstpointer pData) +{ + fixture->book = qof_book_new (); + fixture->hdlrs = NULL; +} + +static void +teardown (Fixture *fixture, gconstpointer pData) +{ + qof_book_destroy (fixture->book); + g_slist_free_full (fixture->hdlrs, test_free_log_handler); + test_clear_error_list(); +} + +static void +test_book_use_book_currency( Fixture *fixture, gconstpointer pData ) +{ + const gchar *cur; + const gchar *pol; + + g_test_message( "Testing with no currency accounting method selected" ); + cur = gnc_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , NULL ); + pol = gnc_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , NULL ); + g_assert( !gnc_book_use_book_currency ( fixture-> book )); + + g_test_message( "Testing with trading accounts set to true - t" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "trading-accts", "t", + NULL); + cur = gnc_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , NULL ); + pol = gnc_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , NULL ); + g_assert( !gnc_book_use_book_currency ( fixture-> book )); + qof_book_commit_edit (fixture->book); + + qof_book_destroy( fixture->book ); + fixture->book = qof_book_new(); + + g_test_message( "Testing with valid book-currency but default-gains-policy set to nonsense" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "book-currency", "USD", + NULL); + qof_instance_set (QOF_INSTANCE (fixture->book), + "default-gains-policy", "random", + NULL); + cur = gnc_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , NULL ); + pol = gnc_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , NULL ); + g_assert( !gnc_book_use_book_currency ( fixture-> book )); + qof_book_commit_edit (fixture->book); + + qof_book_destroy( fixture->book ); + fixture->book = qof_book_new(); + + g_test_message( "Testing with valid default-gains-policy but book-currency set to nonsense" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "book-currency", "myMoney", + NULL); + qof_instance_set (QOF_INSTANCE (fixture->book), + "default-gains-policy", "fifo", + NULL); + cur = gnc_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , NULL ); + pol = gnc_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , NULL ); + g_assert( !gnc_book_use_book_currency ( fixture-> book )); + qof_book_commit_edit (fixture->book); + + qof_book_destroy( fixture->book ); + fixture->book = qof_book_new(); + + g_test_message( "Testing with book-currency and default-gains-policy set to nonsense" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "book-currency", "myMoney", + NULL); + qof_instance_set (QOF_INSTANCE (fixture->book), + "default-gains-policy", "random", + NULL); + cur = gnc_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , NULL ); + pol = gnc_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , NULL ); + g_assert( !gnc_book_use_book_currency ( fixture-> book )); + qof_book_commit_edit (fixture->book); + + qof_book_destroy( fixture->book ); + fixture->book = qof_book_new(); + + g_test_message( "Testing with book-currency set and no default-gains-policy" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "book-currency", "USD", + NULL); + cur = gnc_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , NULL ); + pol = gnc_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , NULL ); + g_assert( !gnc_book_use_book_currency ( fixture-> book )); + qof_book_commit_edit (fixture->book); + + qof_book_destroy( fixture->book ); + fixture->book = qof_book_new(); + + g_test_message( "Testing with default-gains-policy set and no book-currency" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "default-gains-policy", "fifo", + NULL); + cur = gnc_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , NULL ); + pol = gnc_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , NULL ); + g_assert( !gnc_book_use_book_currency ( fixture-> book )); + qof_book_commit_edit (fixture->book); + + qof_book_destroy( fixture->book ); + fixture->book = qof_book_new(); + + g_test_message( "Testing with book-currency and default-gains-policy set to valid values and with trading accounts set to true - t" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "trading-accts", "t", + NULL); + qof_instance_set (QOF_INSTANCE (fixture->book), + "book-currency", "USD", + NULL); + qof_instance_set (QOF_INSTANCE (fixture->book), + "default-gains-policy", "fifo", + NULL); + cur = gnc_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , NULL ); + pol = gnc_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , NULL ); + g_assert( !gnc_book_use_book_currency ( fixture-> book )); + qof_book_commit_edit (fixture->book); + + qof_book_destroy( fixture->book ); + fixture->book = qof_book_new(); + + g_test_message( "Testing with book-currency and default-gains-policy set to valid values and no trading accounts flag" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "book-currency", "USD", + NULL); + qof_instance_set (QOF_INSTANCE (fixture->book), + "default-gains-policy", "fifo", + NULL); + cur = gnc_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , "USD" ); + pol = gnc_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , "fifo" ); + g_assert( gnc_book_use_book_currency ( fixture-> book )); + qof_book_commit_edit (fixture->book); +} + +void +test_suite_gnc_ui_util (void) +{ + GNC_TEST_ADD( suitename, "use book-currency", Fixture, NULL, setup, test_book_use_book_currency, teardown ); + +} diff --git a/src/app-utils/test/test-option-util.c b/src/app-utils/test/test-option-util.c index 8fef4c0d9b..9a0a1883d8 100644 --- a/src/app-utils/test/test-option-util.c +++ b/src/app-utils/test/test-option-util.c @@ -60,16 +60,30 @@ setup_kvp (Fixture *fixture, gconstpointer pData) slots = qof_instance_get_slots (QOF_INSTANCE (book)); qof_begin_edit (QOF_INSTANCE (book)); qof_instance_set (QOF_INSTANCE (book), - "trading-accts", "t", - "split-action-num-field", "t", - "autoreadonly-days", (double)21, - NULL); + "trading-accts", "t", + "split-action-num-field", "t", + "autoreadonly-days", (double)21, + NULL); kvp_frame_set_string (slots, "options/Business/Company Name", "Bogus Company"); qof_commit_edit (QOF_INSTANCE (book)); } +static void +setup_kvp_book_currency (Fixture *fixture, gconstpointer pData) +{ + QofBook *book; + setup (fixture, pData); + book = fixture->book; + qof_begin_edit (QOF_INSTANCE (book)); + qof_instance_set (QOF_INSTANCE (book), + "book-currency", "GTQ", + "default-gains-policy", "fifo", + NULL); + qof_commit_edit (QOF_INSTANCE (book)); +} + static void teardown (Fixture *fixture, gconstpointer pData) { @@ -81,18 +95,96 @@ teardown (Fixture *fixture, gconstpointer pData) static void test_option_load_from_kvp (Fixture *fixture, gconstpointer pData) { + gchar *str = NULL; + SCM symbol_value; QofBook *book = fixture->book; GNCOptionDB *odb = gnc_option_db_new_for_type (QOF_ID_BOOK); qof_book_load_options (book, gnc_option_db_load_from_kvp, odb); - g_assert (gnc_option_db_lookup_boolean_option (odb, OPTION_SECTION_ACCOUNTS, - OPTION_NAME_TRADING_ACCOUNTS, FALSE)); + symbol_value = gnc_currency_accounting_option_value_get_method ( + gnc_option_db_lookup_option (odb, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_CURRENCY_ACCOUNTING, + SCM_BOOL_F)); + if (scm_is_symbol(symbol_value)) + { + SCM string_value = scm_symbol_to_string (symbol_value); + if (scm_is_string (string_value)) + { + str = scm_to_utf8_string (string_value); + } + } + g_assert_cmpstr (str, ==, "trading"); + if (str) + g_free (str); + g_assert (gnc_option_db_lookup_boolean_option (odb, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_NUM_FIELD_SOURCE, FALSE)); g_assert_cmpstr (gnc_option_db_lookup_string_option (odb, "Business", "Company Name", FALSE), ==, "Bogus Company"); g_assert_cmpfloat (gnc_option_db_lookup_number_option (odb, OPTION_SECTION_ACCOUNTS, OPTION_NAME_AUTO_READONLY_DAYS, FALSE), ==, 21); gnc_option_db_destroy (odb); } +static void +test_option_load_from_kvp_book_currency (Fixture *fixture, gconstpointer pData) +{ + gchar *str = NULL; + SCM symbol_value; + const gchar *curr = NULL; + SCM curr_scm; + gnc_commodity *commodity; + QofBook *book = fixture->book; + GNCOptionDB *odb = gnc_option_db_new_for_type (QOF_ID_BOOK); + + qof_book_load_options (book, gnc_option_db_load_from_kvp, odb); + symbol_value = gnc_currency_accounting_option_value_get_method ( + gnc_option_db_lookup_option (odb, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_CURRENCY_ACCOUNTING, + SCM_BOOL_F)); + if (scm_is_symbol(symbol_value)) + { + SCM string_value = scm_symbol_to_string (symbol_value); + if (scm_is_string (string_value)) + { + str = scm_to_utf8_string (string_value); + } + } + g_assert_cmpstr (str, ==, "book-currency"); + if (str) + g_free (str); + symbol_value = gnc_currency_accounting_option_value_get_default_policy ( + gnc_option_db_lookup_option (odb, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_CURRENCY_ACCOUNTING, + SCM_BOOL_F)); + if (scm_is_symbol(symbol_value)) + { + SCM string_value = scm_symbol_to_string (symbol_value); + if (scm_is_string (string_value)) + { + str = scm_to_utf8_string (string_value); + } + } + g_assert_cmpstr (str, ==, "fifo"); + if (str) + g_free (str); + curr_scm = gnc_currency_accounting_option_value_get_book_currency( + gnc_option_db_lookup_option(odb, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_CURRENCY_ACCOUNTING, + SCM_BOOL_F)); + commodity = gnc_scm_to_commodity (curr_scm); + if (commodity) + { + curr = gnc_commodity_get_mnemonic (commodity); + } + g_assert_cmpstr (curr, ==, "GTQ"); + + gnc_option_db_destroy (odb); +} + static void test_option_save_to_kvp (Fixture *fixture, gconstpointer pData) { @@ -100,9 +192,12 @@ test_option_save_to_kvp (Fixture *fixture, gconstpointer pData) GNCOptionDB *odb = gnc_option_db_new_for_type (QOF_ID_BOOK); KvpFrame *slots = qof_instance_get_slots (QOF_INSTANCE (book)); + g_assert (gnc_option_db_set_option (odb, OPTION_SECTION_ACCOUNTS, + OPTION_NAME_CURRENCY_ACCOUNTING, + scm_cons (scm_from_locale_symbol("trading"), SCM_EOL))); g_assert (gnc_option_db_set_boolean_option (odb, OPTION_SECTION_ACCOUNTS, - OPTION_NAME_TRADING_ACCOUNTS, - TRUE)); + OPTION_NAME_NUM_FIELD_SOURCE, + TRUE)); g_assert (gnc_option_db_set_string_option (odb, "Business", "Company Name", "Bogus Company")); g_assert (gnc_option_db_set_number_option (odb, OPTION_SECTION_ACCOUNTS, @@ -110,17 +205,38 @@ test_option_save_to_kvp (Fixture *fixture, gconstpointer pData) 17)); qof_book_save_options (book, gnc_option_db_save_to_kvp, odb, TRUE); g_assert_cmpstr (kvp_frame_get_string (slots, "options/Accounts/Use Trading Accounts"), == , "t"); + g_assert_cmpstr (kvp_frame_get_string (slots, "options/Accounts/Use Split Action Field for Number"), == , "t"); g_assert_cmpstr (kvp_frame_get_string (slots, "options/Business/Company Name"), ==, "Bogus Company"); g_assert_cmpfloat (kvp_frame_get_double (slots, "options/Accounts/Day Threshold for Read-Only Transactions (red line)"), ==, 17); gnc_option_db_destroy (odb); } +static void +test_option_save_to_kvp_book_currency (Fixture *fixture, gconstpointer pData) +{ + QofBook *book = fixture->book; + GNCOptionDB *odb = gnc_option_db_new_for_type (QOF_ID_BOOK); + KvpFrame *slots = qof_instance_get_slots (QOF_INSTANCE (book)); + + g_assert (gnc_option_db_set_option (odb, OPTION_SECTION_ACCOUNTS, + OPTION_NAME_CURRENCY_ACCOUNTING, + scm_cons (scm_from_locale_symbol("book-currency"), + scm_cons (scm_from_utf8_string("GTQ"), + scm_cons (scm_from_locale_symbol("fifo"), SCM_EOL))))); + qof_book_save_options (book, gnc_option_db_save_to_kvp, odb, TRUE); + g_assert_cmpstr (kvp_frame_get_string (slots, "options/Accounts/Book Currency"), == , "GTQ"); + g_assert_cmpstr (kvp_frame_get_string (slots, "options/Accounts/Default Gains Policy"), == , "fifo"); + + gnc_option_db_destroy (odb); +} void test_suite_option_util (void) { GNC_TEST_ADD (suitename, "Option DB Load from KVP", Fixture, NULL, setup_kvp, test_option_load_from_kvp, teardown); + GNC_TEST_ADD (suitename, "Option DB Load from KVP - Book Currency", Fixture, NULL, setup_kvp_book_currency, test_option_load_from_kvp_book_currency, teardown); GNC_TEST_ADD (suitename, "Option DB Save to KVP", Fixture, NULL, setup, test_option_save_to_kvp, teardown); + GNC_TEST_ADD (suitename, "Option DB Save to KVP - Book Currency", Fixture, NULL, setup, test_option_save_to_kvp_book_currency, teardown); } diff --git a/src/engine/engine.i b/src/engine/engine.i index 41ce57ca74..99d1c5a6db 100644 --- a/src/engine/engine.i +++ b/src/engine/engine.i @@ -14,6 +14,7 @@ #include "gnc-hooks-scm.h" #include "engine-helpers.h" #include "engine-helpers-guile.h" +#include "policy.h" #include "SX-book.h" #include "kvp-scm.h" #include "glib-helpers.h" @@ -136,6 +137,7 @@ functions. */ %typemap(in) char * num; %typemap(in) char * action; +%include %include QofSession * qof_session_new (void); @@ -348,6 +350,9 @@ KvpValue * kvp_frame_get_slot_path_gslist (KvpFrame *frame, GSList *key_path); SET_ENUM("OPTION-SECTION-ACCOUNTS"); SET_ENUM("OPTION-NAME-TRADING-ACCOUNTS"); + SET_ENUM("OPTION-NAME-CURRENCY-ACCOUNTING"); + SET_ENUM("OPTION-NAME-BOOK-CURRENCY"); + SET_ENUM("OPTION-NAME-DEFAULT-GAINS-POLICY"); SET_ENUM("OPTION-NAME-AUTO-READONLY-DAYS"); SET_ENUM("OPTION-NAME-NUM-FIELD-SOURCE"); diff --git a/src/engine/policy.c b/src/engine/policy.c index fbb382c404..38da2058bf 100644 --- a/src/engine/policy.c +++ b/src/engine/policy.c @@ -42,8 +42,85 @@ #include "policy.h" #include "policy-p.h" +#ifndef SWIG /* swig doesn't see N_() as a string constant */ +#include +#else +#define N_(string) string +#endif + +#define FIFO_POLICY "fifo" +#define FIFO_POLICY_DESC N_("First In, First Out") +#define FIFO_POLICY_HINT N_("Use oldest lots first.") +#define LIFO_POLICY "lifo" +#define LIFO_POLICY_DESC N_("Last In, First Out") +#define LIFO_POLICY_HINT N_("Use newest lots first.") +#define AVERAGE_POLICY "average" +#define AVERAGE_POLICY_DESC N_("Average") +#define AVERAGE_POLICY_HINT N_("Average cost of open lots.") +#define MANUAL_POLICY "manual" +#define MANUAL_POLICY_DESC N_("Manual") +#define MANUAL_POLICY_HINT N_("Manually select lots.") + //static QofLogModule log_module = GNC_MOD_LOT; +GList * +gnc_get_valid_policy_list (void) +{ + GList *return_list = NULL; + GList *policy_list1 = NULL; + GList *policy_list2 = NULL; + GList *policy_list3 = NULL; + GList *policy_list4 = NULL; + + policy_list1 = g_list_prepend (policy_list1, MANUAL_POLICY_HINT); + policy_list1 = g_list_prepend (policy_list1, MANUAL_POLICY_DESC); + policy_list1 = g_list_prepend (policy_list1, MANUAL_POLICY); + return_list = g_list_prepend (return_list, policy_list1); + policy_list2 = g_list_prepend (policy_list2, AVERAGE_POLICY_HINT); + policy_list2 = g_list_prepend (policy_list2, AVERAGE_POLICY_DESC); + policy_list2 = g_list_prepend (policy_list2, AVERAGE_POLICY); + return_list = g_list_prepend (return_list, policy_list2); + policy_list3 = g_list_prepend (policy_list3, LIFO_POLICY_HINT); + policy_list3 = g_list_prepend (policy_list3, LIFO_POLICY_DESC); + policy_list3 = g_list_prepend (policy_list3, LIFO_POLICY); + return_list = g_list_prepend (return_list, policy_list3); + policy_list4 = g_list_prepend (policy_list4, FIFO_POLICY_HINT); + policy_list4 = g_list_prepend (policy_list4, FIFO_POLICY_DESC); + policy_list4 = g_list_prepend (policy_list4, FIFO_POLICY); + return_list = g_list_prepend (return_list, policy_list4); + + return return_list; +} + +gboolean +gnc_valid_policy (const gchar *name) +{ + GList *list_of_policies = NULL; + gboolean ret_val = FALSE; + + if (!name) + return ret_val; + + list_of_policies = gnc_get_valid_policy_list(); + if (!list_of_policies) + { + return ret_val; + } + else + { + GList *l = NULL; + for (l = list_of_policies; l != NULL; l = l->next) + { + GList *policy_list = l->data; + if (g_strcmp0(policy_list->data, name) == 0) + ret_val = TRUE; + g_list_free(policy_list); + } + g_list_free(list_of_policies); + return ret_val; + } +} + static Split * DirectionPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot, short reverse) { diff --git a/src/engine/policy.h b/src/engine/policy.h index f59acacd11..5c4ad8ca6c 100644 --- a/src/engine/policy.h +++ b/src/engine/policy.h @@ -39,6 +39,23 @@ typedef struct gncpolicy_s GNCPolicy; +/** Valid Policy List + * Provides a glist of glists for implemented policies. For each implemented + * policy, this glist contains: name, description, hint, as follows: + * glist( + * glist("fifo", "First In First Out", "Use oldest lots first.") + * glist("lifo", "Last In First Out", "Use newest lots first.") + * etc. + * ) + * Both levels of lists must be freed with g_list_free(). + */ +GList * gnc_get_valid_policy_list (void); + +/** Valid Policy + * Uses the Valid Policy List to determine if a policy name is valid. + */ +gboolean gnc_valid_policy (const gchar *name); + /** First-in, First-out Policy * This policy will create FIFO Lots. FIFO Lots have the following * properties: diff --git a/src/engine/test/utest-Split.cpp b/src/engine/test/utest-Split.cpp index 733e5fb9ab..4e3425df2d 100644 --- a/src/engine/test/utest-Split.cpp +++ b/src/engine/test/utest-Split.cpp @@ -1808,7 +1808,7 @@ test_xaccSplitGetOtherSplit (Fixture *fixture, gconstpointer pData) g_assert (kvp_frame_get_slot (split1->inst.kvp_data, "lot-split") == NULL); qof_book_begin_edit (book); qof_instance_set (QOF_INSTANCE (book), - "currency-accounting", "trading", + "trading-accts", "t", NULL); qof_book_commit_edit (book); g_assert (xaccTransUseTradingAccounts (txn)); diff --git a/src/engine/test/utest-Transaction.c b/src/engine/test/utest-Transaction.c index 8c3ee23d2c..7611c2c117 100644 --- a/src/engine/test/utest-Transaction.c +++ b/src/engine/test/utest-Transaction.c @@ -1053,7 +1053,7 @@ test_xaccTransGetImbalance_trading (Fixture *fixture, MonetaryList *mlist; qof_book_begin_edit (book); qof_instance_set (QOF_INSTANCE (book), - "currency-accounting", "trading", + "trading-accts", "t", NULL); qof_book_commit_edit (book); @@ -1140,7 +1140,7 @@ test_xaccTransIsBalanced_trading (Fixture *fixture, gconstpointer pData) qof_book_begin_edit (book); qof_instance_set (QOF_INSTANCE (book), - "currency-accounting", "trading", + "trading-accts", "t", NULL); qof_book_commit_edit (book); diff --git a/src/gnome-utils/dialog-options.c b/src/gnome-utils/dialog-options.c index 64aba22d0a..e590b902d3 100644 --- a/src/gnome-utils/dialog-options.c +++ b/src/gnome-utils/dialog-options.c @@ -125,6 +125,7 @@ void gnc_options_dialog_response_cb(GtkDialog *dialog, gint response, static void gnc_options_dialog_reset_cb(GtkWidget * w, gpointer data); void gnc_options_dialog_list_select_cb (GtkTreeSelection *selection, gpointer data); +GList * gnc_option_get_ui_widgets_currency_accounting(GtkWidget *widget); GtkWidget * gnc_option_get_gtk_widget (GNCOption *option) @@ -397,6 +398,69 @@ gnc_option_set_selectable_internal (GNCOption *option, gboolean selectable) gtk_widget_set_sensitive (widget, selectable); } +GList * +gnc_option_get_ui_widgets_currency_accounting (GtkWidget *widget) +{ + GList *list1, *list2, *list3; + GList *list = NULL; + GList *return_list = NULL; + GtkWidget *book_currency_widget= NULL; + GtkWidget *default_cost_policy_widget= NULL; + GtkWidget *book_currency_vbox_widget= NULL; + GtkWidget *default_cost_policy_vbox_widget= NULL; + + /* children of the frame, the 1st of which is 1 vbox1 */ + list1 = gtk_container_get_children (GTK_CONTAINER (widget)); + /* children of vbox1 which are 3 hbox's */ + list2 = gtk_container_get_children (GTK_CONTAINER (list1->data)); + g_list_free(list1); + /* create list of button widgets */ + for (list3 = list2; list3; list3 = list3->next) + { + GList *vbox2list = NULL; + /* children of each hbox, the 1st of which is a vbox2 */ + vbox2list = gtk_container_get_children + (GTK_CONTAINER (list3->data)); + /* children of 1st vbox2, the 1st of which is a button */ + list1 = gtk_container_get_children + (GTK_CONTAINER (vbox2list->data)); + list = g_list_append (list, list1->data); + g_list_free(vbox2list); + g_list_free(list1); + } + return_list = g_list_append (return_list, list); + /* point list2 to 2nd hbox, which is for book-currency */ + list2 = list2->next; + /* children of book-currency hbox which is 3 vbox2's */ + list1 = gtk_container_get_children (GTK_CONTAINER (list2->data)); + g_list_free(list2); + /* point list1 to 2nd vbox2, which is for book-currency widget*/ + list1 = list1->next; + book_currency_vbox_widget = list1->data; + /* children of book-currency vbox2 which is a label and the + book-currency widget */ + list2 = gtk_container_get_children (GTK_CONTAINER (list1->data)); + list2 = list2->next; + book_currency_widget = list2->data; + return_list = g_list_append (return_list, book_currency_widget); + g_list_free(list2); + /* point list1 to 3rd vbox2, which is for policy widget*/ + list1 = list1->next; + default_cost_policy_vbox_widget = list1->data; + /* children of policy vbox2 which is a label and the + policy multichoice widget */ + list2 = gtk_container_get_children (GTK_CONTAINER (list1->data)); + list2 = list2->next; + default_cost_policy_widget = list2->data; + return_list = g_list_append (return_list, default_cost_policy_widget); + return_list = g_list_append (return_list, book_currency_vbox_widget); + return_list = g_list_append (return_list, default_cost_policy_vbox_widget); + g_list_free(list2); + g_list_free(list1); + + return return_list; +} + static void gnc_option_default_cb(GtkWidget *widget, GNCOption *option) { @@ -450,6 +514,127 @@ gnc_option_radiobutton_cb(GtkWidget *w, gpointer data) gnc_option_changed_widget_cb(widget, option); } +static void +gnc_option_currency_accounting_set_sensitivity(GNCOption *option, + gboolean set_sensitivity) +{ + GtkWidget *option_widget; + GtkWidget *book_currency_widget; + GtkWidget *default_cost_policy_widget; + GtkWidget *book_currency_vbox_widget; + GtkWidget *default_cost_policy_vbox_widget; + GList *list = NULL; + GList *sub_widgets = NULL; + + option_widget = gnc_option_get_gtk_widget (option); + sub_widgets = gnc_option_get_ui_widgets_currency_accounting (option_widget); + list = sub_widgets->data; /* save this to be able to free it */ + sub_widgets = sub_widgets->next; + book_currency_widget = sub_widgets->data; + sub_widgets = sub_widgets->next; + default_cost_policy_widget = sub_widgets->data; + sub_widgets = sub_widgets->next; + book_currency_vbox_widget = sub_widgets->data; + sub_widgets = sub_widgets->next; + default_cost_policy_vbox_widget = sub_widgets->data; + g_list_free(sub_widgets); + g_list_free(list); + if (set_sensitivity) + { + SCM curr_scm; + SCM list_symbol; + gnc_commodity *commodity; + int index; + GList *list_of_policies = NULL; + + curr_scm = gnc_currency_accounting_option_get_default_currency(option); + commodity = gnc_scm_to_commodity (curr_scm); + if (commodity) + { + gnc_currency_edit_set_currency + (GNC_CURRENCY_EDIT(book_currency_widget), commodity); + } + else + { + gnc_currency_edit_set_currency + (GNC_CURRENCY_EDIT(book_currency_widget), gnc_default_currency()); + } + gtk_widget_set_sensitive(book_currency_vbox_widget, TRUE); + + list_of_policies = gnc_get_valid_policy_list(); + if (list_of_policies) + { + GList *l = NULL; + gint i = 0; + list_symbol = + gnc_currency_accounting_option_get_default_policy(option); + for (l = list_of_policies; l != NULL; l = l->next) + { + /* First item in policy_list is internal name of policy */ + GList *policy_list = l->data; + if (g_strcmp0(policy_list->data, + gnc_scm_symbol_to_locale_string(list_symbol)) + == 0) + { + /* GtkComboBox per-item tooltip changes needed below */ + gnc_combott_set_active( + GNC_COMBOTT(default_cost_policy_widget), i); + } + i++; + g_list_free(policy_list); + } + g_list_free(list_of_policies); + } + else + { + gnc_combott_set_active + (GNC_COMBOTT(default_cost_policy_widget), -1); + } + gtk_widget_set_sensitive(default_cost_policy_vbox_widget, TRUE); + } + else + { + GtkWidget *new_book_currency_widget = NULL; + + /* since there is no 'gnc_currency_edit_set_currency(widget, -1)' like + there is for 'gnc_combott_set_active', do this as a work around so + the dialog is cleared of currency when switched out of 'book- + currency' choice */ + gtk_widget_destroy (book_currency_widget); + new_book_currency_widget = gnc_currency_edit_new(); + g_signal_connect(G_OBJECT(new_book_currency_widget), + "changed", + G_CALLBACK(gnc_option_changed_widget_cb), + option); + gtk_box_pack_start (GTK_BOX (book_currency_vbox_widget), + new_book_currency_widget, FALSE, FALSE, 0); + gtk_widget_show_all(book_currency_vbox_widget); + gtk_widget_set_sensitive(book_currency_vbox_widget, FALSE); + gnc_combott_set_active(GNC_COMBOTT(default_cost_policy_widget), -1); + gtk_widget_set_sensitive(default_cost_policy_vbox_widget, FALSE); + } +} + +static void +gnc_option_currency_accounting_non_book_cb(GtkWidget *widget, gpointer data) +{ + /* widget is the radiobutton */ + GNCOption *option = data; + + gnc_option_currency_accounting_set_sensitivity (option, FALSE); + gnc_option_radiobutton_cb(widget, data); +} + +static void +gnc_option_currency_accounting_book_cb(GtkWidget *widget, gpointer data) +{ + /* widget is the radiobutton */ + GNCOption *option = data; + + gnc_option_currency_accounting_set_sensitivity (option, TRUE); + gnc_option_radiobutton_cb(widget, data); +} + static GtkWidget * gnc_option_create_date_widget (GNCOption *option) { @@ -678,6 +863,146 @@ gnc_option_create_radiobutton_widget(char *name, GNCOption *option) return frame; } +static GtkWidget * +gnc_option_create_currency_accounting_widget (char *name, GNCOption *option) +{ + GtkWidget *frame = NULL, *vbox1 = NULL; + GtkWidget *widget = NULL; + int num_values; + GList *list_of_policies = NULL; + int i; + + num_values = gnc_option_num_permissible_values(option); + + g_return_val_if_fail(num_values == 3, NULL); + + list_of_policies = gnc_get_valid_policy_list(); + + g_return_val_if_fail(g_list_length (list_of_policies) >= 0, NULL); + + /* Create our button frame */ + frame = gtk_frame_new (name); + + /* Create the verticle button box */ + vbox1 = gtk_vbox_new (FALSE, 5); + gtk_container_add (GTK_CONTAINER (frame), vbox1); + + /* Iterate over the three options and create a radio button for each one */ + for (i = 0; i < num_values; i++) + { + char *label; + char *tip; + GtkWidget *vbox2 = NULL, *hbox = NULL; + + label = gnc_option_permissible_value_name(option, i); + tip = gnc_option_permissible_value_description(option, i); + + widget = + gtk_radio_button_new_with_label_from_widget (widget ? + GTK_RADIO_BUTTON (widget) : + NULL, + label && *label ? _(label) : ""); + g_object_set_data (G_OBJECT (widget), "gnc_radiobutton_index", + GINT_TO_POINTER (i)); + gtk_widget_set_tooltip_text(widget, tip && *tip ? _(tip) : ""); + /* Use hbox & vbox2 for all buttons so they are all at the same level; + easier to get in set/get ui functions */ + hbox = gtk_hbox_new(FALSE, 5); + vbox2 = gtk_vbox_new(FALSE, 5); + gtk_box_pack_start (GTK_BOX (vbox2), widget, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0); + if (i == 1) /* book-currency */ + { + GtkWidget *widget_label; + GtkWidget *book_currency_widget = NULL, + *default_cost_policy_widget = NULL; + + g_signal_connect(G_OBJECT(widget), + "toggled", + G_CALLBACK(gnc_option_currency_accounting_book_cb), + option); + tip = gnc_currency_accounting_option_currency_documentation(option); + widget_label = gtk_label_new( _("Book Currency") ); + book_currency_widget = gnc_currency_edit_new(); + g_signal_connect(G_OBJECT(book_currency_widget), + "changed", + G_CALLBACK(gnc_option_changed_widget_cb), + option); + vbox2 = gtk_vbox_new(FALSE, 5); + gtk_widget_set_tooltip_text(vbox2, tip && *tip ? _(tip) : ""); + gtk_box_pack_start (GTK_BOX (vbox2), widget_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox2), + book_currency_widget, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0); + if (list_of_policies) + { + GtkListStore *store; + GtkTreeIter iter; + char *itemstring; + char *description; + GList *l = NULL; + + store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + /* Add values to the list store, entry and tooltip */ + for (l = list_of_policies; l != NULL; l = l->next) + { + GList *policy_list = l->data; + /* First item in policy_list is internal name of policy */ + policy_list = policy_list->next; + /* Second item in policy_list is policy description */ + itemstring = policy_list->data; + policy_list = policy_list->next; + /* Third item in policy_list is policy hint */ + description = policy_list->data; + gtk_list_store_append (store, &iter); + gtk_list_store_set + (store, + &iter, + 0, + (itemstring && *itemstring) ? _(itemstring) : "", + 1, + (description && *description) ? _(description) : "", + -1); + g_list_free(policy_list); + } + g_list_free(list_of_policies); + /* Create the new Combo with tooltip and add the store */ + default_cost_policy_widget = GTK_WIDGET(gnc_combott_new()); + g_object_set( G_OBJECT( default_cost_policy_widget ), + "model", + GTK_TREE_MODEL(store), + NULL ); + g_object_unref(store); + + g_signal_connect(G_OBJECT(default_cost_policy_widget), "changed", + G_CALLBACK(gnc_option_multichoice_cb), option); + } + tip = gnc_currency_accounting_option_policy_documentation(option); + widget_label = gtk_label_new( _("Default Gains Policy") ); + vbox2 = gtk_vbox_new(FALSE, 5); + gtk_widget_set_tooltip_text(vbox2, tip && *tip ? _(tip) : ""); + gtk_box_pack_start (GTK_BOX (vbox2), widget_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox2), + default_cost_policy_widget, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0); + } + else /* trading or neither */ + { + g_signal_connect(G_OBJECT(widget), + "toggled", + G_CALLBACK(gnc_option_currency_accounting_non_book_cb), + option); + } + gtk_box_pack_start (GTK_BOX (vbox1), hbox, FALSE, FALSE, 0); + + if (label) + free (label); + if (tip) + free (tip); + } + return frame; +} + static void gnc_option_account_cb(GtkTreeSelection *selection, gpointer data) { @@ -2167,6 +2492,27 @@ gnc_option_set_ui_widget_budget (GNCOption *option, GtkBox *page_box, return value; } +static GtkWidget * +gnc_option_set_ui_widget_currency_accounting (GNCOption *option, + GtkBox *page_box, + char *name, char *documentation, + /* Return values */ + GtkWidget **enclosing, + gboolean *packed) +{ + GtkWidget *value; + + *enclosing = gtk_hbox_new(FALSE, 5); + + value = gnc_option_create_currency_accounting_widget(name, option); + gnc_option_set_widget (option, value); + + gnc_option_set_ui_value(option, FALSE); + gtk_box_pack_start(GTK_BOX(*enclosing), value, FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + return value; +} + /************************* * SET VALUE * ************************* @@ -2632,6 +2978,144 @@ gnc_option_set_ui_value_dateformat (GNCOption *option, gboolean use_default, return FALSE; } +static gboolean +gnc_option_set_ui_value_currency_accounting (GNCOption *option, + gboolean use_default, + GtkWidget *widget, SCM value) +{ + if (scm_is_pair(value)) + { + SCM rb_symbol; + + rb_symbol = gnc_currency_accounting_option_value_get_method (value); + + if (rb_symbol) + { + int index; + + index = gnc_option_permissible_value_index(option, rb_symbol); + if (index < 0) + return TRUE; + else + { + GtkWidget *button; + int i; + gpointer val; + GList *list = NULL; + GList *sub_widgets = NULL; + + sub_widgets = + gnc_option_get_ui_widgets_currency_accounting (widget); + list = sub_widgets->data; + g_list_free(sub_widgets); + + /* stop when list of buttons is pointing at index */ + for (i = 0; i < index && list; i++) + list = list->next; + g_return_val_if_fail (list, TRUE); + + button = list->data; /* this is selected button */ + g_list_free(list); + val = g_object_get_data (G_OBJECT (button), + "gnc_radiobutton_index"); + g_return_val_if_fail (GPOINTER_TO_INT (val) == index, TRUE); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE); + /* when an unselected button in a group is clicked the clicked + button receives the “toggled” signal, as does the + previously selected button; however, if the first button + is active when the currency-accounting dialog is created, + that is, it's read from the option, the "toggled" handler + is not called while it is if any other button is active. + To get desired result, that is, to set sensitivity to + FALSE, explicitly call the handler here if first button */ + if (index == 0) + gnc_option_currency_accounting_non_book_cb(button, + (gpointer) option); + if (g_strcmp0(gnc_option_permissible_value_name(option, + index), + "Use a Book-Currency") == 0) + { + GtkWidget *book_currency_widget; + GtkWidget *default_cost_policy_widget; + SCM curr_scm; + SCM list_symbol; + gnc_commodity *commodity; + GList *list_of_policies = NULL; + + /* note that we have to call this function again here + because the callback routines called above, or + initiated by GTK, change the widget pointers, so we + can't keep and use those called above */ + sub_widgets = + gnc_option_get_ui_widgets_currency_accounting + (widget); + list = sub_widgets->data; // needed just to free button list + sub_widgets = sub_widgets->next; + book_currency_widget = sub_widgets->data; + sub_widgets = sub_widgets->next; + default_cost_policy_widget = sub_widgets->data; + g_list_free(sub_widgets); + g_list_free(list); + curr_scm = + gnc_currency_accounting_option_value_get_book_currency + (value); + commodity = gnc_scm_to_commodity (curr_scm); + if (commodity) + { + gnc_currency_edit_set_currency + (GNC_CURRENCY_EDIT(book_currency_widget), + commodity); + } + else + { + gnc_currency_edit_set_currency + (GNC_CURRENCY_EDIT(book_currency_widget), + gnc_default_currency()); + } + list_of_policies = gnc_get_valid_policy_list(); + if (list_of_policies) + { + GList *l = NULL; + gint i = 0; + list_symbol = + gnc_currency_accounting_option_value_get_default_policy + (value); + for (l = list_of_policies; l != NULL; l = l->next) + { + /* First item in policy_list is internal name of + policy */ + GList *policy_list = l->data; + if (g_strcmp0(policy_list->data, + gnc_scm_symbol_to_locale_string(list_symbol)) + == 0) + { + /* GtkComboBox per-item tooltip changes needed + below */ + gnc_combott_set_active( + GNC_COMBOTT(default_cost_policy_widget), + i); + } + i++; + g_list_free(policy_list); + } + g_list_free(list_of_policies); + } + else + { + gnc_combott_set_active + (GNC_COMBOTT(default_cost_policy_widget), -1); + } + } + return FALSE; + } + } + return TRUE; + } + return TRUE; +} + + /************************* * GET VALUE * ************************* @@ -2939,6 +3423,77 @@ gnc_option_get_ui_value_dateformat (GNCOption *option, GtkWidget *widget) return (gnc_dateformat_option_set_value(format, months, years, custom)); } +static SCM +gnc_option_get_ui_value_currency_accounting (GNCOption *option, GtkWidget *widget) +{ + gpointer _index; + int index; + SCM value = SCM_EOL; + + _index = g_object_get_data(G_OBJECT(widget), "gnc_radiobutton_index"); + index = GPOINTER_TO_INT(_index); + + /* build the return list in reverse order */ + if (g_strcmp0(gnc_option_permissible_value_name(option, index), + "Use a Book-Currency") == 0) + { + GtkWidget *book_currency_widget; + GtkWidget *default_cost_policy_widget; + GList *list = NULL; /* needed just to free the button list */ + GList *sub_widgets = NULL; + gnc_commodity *commodity; + int policy_index; + SCM val; + GList *list_of_policies = NULL; + const char *str = NULL; + + sub_widgets = gnc_option_get_ui_widgets_currency_accounting (widget); + list = sub_widgets->data; + sub_widgets = sub_widgets->next; + book_currency_widget = sub_widgets->data; + sub_widgets = sub_widgets->next; + default_cost_policy_widget = sub_widgets->data; + g_list_free(sub_widgets); + g_list_free(list); + + /* GtkComboBox per-item tooltip changes needed below */ + policy_index = + gnc_combott_get_active(GNC_COMBOTT(default_cost_policy_widget)); + list_of_policies = gnc_get_valid_policy_list(); + if (list_of_policies) + { + GList *l = NULL; + gint i = 0; + for (l = list_of_policies; l != NULL; l = l->next) + { + GList *policy_list = l->data; + if(i == policy_index) + str = policy_list->data; + i++; + g_list_free(policy_list); + } + g_list_free(list_of_policies); + } + if (str) + { + val = scm_from_locale_symbol(str); + } + else + { + val = SCM_EOL; + } + value = scm_cons(val, value); + + commodity = + gnc_currency_edit_get_currency( + GNC_CURRENCY_EDIT(book_currency_widget)); + val = gnc_commodity_to_scm(commodity); + value = scm_cons(val, value); + } + + return (scm_cons (gnc_option_permissible_value(option, index), value)); +} + /************************************/ /* INITIALIZATION */ /************************************/ @@ -3015,6 +3570,12 @@ static void gnc_options_initialize_options (void) "budget", gnc_option_set_ui_widget_budget, gnc_option_set_ui_value_budget, gnc_option_get_ui_value_budget }, + { + "currency-accounting", + gnc_option_set_ui_widget_currency_accounting, + gnc_option_set_ui_value_currency_accounting, + gnc_option_get_ui_value_currency_accounting + }, { NULL, NULL, NULL, NULL } }; int i; diff --git a/src/libqof/qof/qofbook.cpp b/src/libqof/qof/qofbook.cpp index 3ff9ba6892..8d7f7db585 100644 --- a/src/libqof/qof/qofbook.cpp +++ b/src/libqof/qof/qofbook.cpp @@ -21,7 +21,7 @@ /* * FILE: - * qofbook.c + * qofbook.cpp * * FUNCTION: * Encapsulate all the information about a QOF dataset. @@ -66,14 +66,17 @@ enum PROP_0, // PROP_ROOT_ACCOUNT, /* Table */ // PROP_ROOT_TEMPLATE, /* Table */ -/* keep trading accounts property, while adding currency accounting - preperty, so that files prior to 2.7 can be read/processed; GUI changed - to use currency accounting property as of 2.7 */ +/* keep trading accounts property, while adding book-currency and default + gains properties, so that files prior to 2.7 can be read/processed; GUI + changed to use all three properties as of 2.7. Trading accounts, on the + one hand, and book-currency plus default-gains-policy, on the other, + are mutually exclusive */ PROP_OPT_TRADING_ACCOUNTS, /* KVP */ - PROP_OPT_CURRENCY_ACCOUNTING, /* KVP */ -/* Book currency property only applies if currency accounting method is - set to 'book-currency' */ +/* Book currency and default gains properties only apply if currency + accounting method selected in GUI is 'book-currency'; both required and + both are exclusive with trading accounts */ PROP_OPT_BOOK_CURRENCY, /* KVP */ + PROP_OPT_DEFAULT_GAINS_POLICY, /* KVP */ PROP_OPT_AUTO_READONLY_DAYS,/* KVP */ PROP_OPT_NUM_FIELD_SOURCE, /* KVP */ PROP_OPT_DEFAULT_BUDGET, /* KVP */ @@ -82,13 +85,6 @@ enum N_PROPERTIES /* Just a counter */ }; -enum -{ - CURRENCY_ACCOUNTING_TRADING, - CURRENCY_ACCOUNTING_BOOK_CURRENCY, - CURRENCY_ACCOUNTING_NEITHER, -}; - QOF_GOBJECT_GET_TYPE(QofBook, qof_book, QOF_TYPE_INSTANCE, {}); QOF_GOBJECT_DISPOSE(qof_book); QOF_GOBJECT_FINALIZE(qof_book); @@ -145,13 +141,6 @@ qof_book_get_property (GObject* object, qof_instance_get_kvp (QOF_INSTANCE (book), key, value); g_free (key); break; - case PROP_OPT_CURRENCY_ACCOUNTING: - key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_CURRENCY_ACCOUNTING); - qof_instance_get_kvp (QOF_INSTANCE (book), key, value); - g_free (key); - break; case PROP_OPT_BOOK_CURRENCY: key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, OPTION_SECTION_ACCOUNTS, @@ -159,6 +148,13 @@ qof_book_get_property (GObject* object, qof_instance_get_kvp (QOF_INSTANCE (book), key, value); g_free (key); break; + case PROP_OPT_DEFAULT_GAINS_POLICY: + key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_DEFAULT_GAINS_POLICY); + qof_instance_get_kvp (QOF_INSTANCE (book), key, value); + g_free (key); + break; case PROP_OPT_AUTO_READONLY_DAYS: key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, OPTION_SECTION_ACCOUNTS, @@ -179,6 +175,7 @@ qof_book_get_property (GObject* object, OPTION_NAME_DEFAULT_BUDGET); qof_instance_get_kvp (QOF_INSTANCE (book), key, value); g_free (key); + break; case PROP_OPT_FY_END: key = const_cast("fy_end"); qof_instance_get_kvp (QOF_INSTANCE (book), key, value); @@ -215,13 +212,6 @@ qof_book_set_property (GObject *object, qof_instance_set_kvp (QOF_INSTANCE (book), key, value); g_free (key); break; - case PROP_OPT_CURRENCY_ACCOUNTING: - key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_CURRENCY_ACCOUNTING); - qof_instance_set_kvp (QOF_INSTANCE (book), key, value); - g_free (key); - break; case PROP_OPT_BOOK_CURRENCY: key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, OPTION_SECTION_ACCOUNTS, @@ -229,6 +219,13 @@ qof_book_set_property (GObject *object, qof_instance_set_kvp (QOF_INSTANCE (book), key, value); g_free (key); break; + case PROP_OPT_DEFAULT_GAINS_POLICY: + key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_DEFAULT_GAINS_POLICY); + qof_instance_set_kvp (QOF_INSTANCE (book), key, value); + g_free (key); + break; case PROP_OPT_AUTO_READONLY_DAYS: key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, OPTION_SECTION_ACCOUNTS, @@ -284,22 +281,6 @@ qof_book_class_init (QofBookClass *klass) NULL, G_PARAM_READWRITE)); - g_object_class_install_property - (gobject_class, - PROP_OPT_CURRENCY_ACCOUNTING, - g_param_spec_string("currency-accounting", - "Select Currency Accounting Method", - "Scheme 'trading', 'book-currency', or 'neither' (or NULL). " - "If 'trading', then the book uses trading accounts for " - "managing multiple-currency transactions. If 'book-currency', " - "then the book uses the 'book-currency' as a reference currency " - "for managing multiple-currency transactions. If 'neither', or " - "if the property is not set or NULL, then the book handles " - "multiple-currency transactions in the traditional way gnucash " - "has in the past.", - NULL, - G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, PROP_OPT_BOOK_CURRENCY, @@ -307,7 +288,19 @@ qof_book_class_init (QofBookClass *klass) "Select Book Currency", "The reference currency used to manage multiple-currency " "transactions when 'book-currency' currency accounting method " - "selected.", + "selected; requires valid default gains/loss policy.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_OPT_DEFAULT_GAINS_POLICY, + g_param_spec_string("default-gains-policy", + "Select Default Gains Policy", + "The default policy to be used to calculate gains/losses on " + "dispositions of currencies/commodities other than " + "'book-currency' when 'book-currency' currency accounting " + "method selected; requires valid book-currency.", NULL, G_PARAM_READWRITE)); @@ -895,63 +888,13 @@ qof_book_validate_counter_format_internal(const gchar *p, return NULL; } -/* Returns Currency Accounting Method for book */ -gint -qof_book_currency_accounting_method (const QofBook *book) -{ - KvpFrame *kvp; - KvpValue *value; - - if (!book) - { - PWARN ("No book!!!"); - return CURRENCY_ACCOUNTING_NEITHER; - } - - /* Get the KVP from the current book */ - kvp = qof_instance_get_slots (QOF_INSTANCE (book)); - - if (!kvp) - { - PWARN ("Book has no KVP_Frame"); - return CURRENCY_ACCOUNTING_NEITHER; - } - - /* Get the Currency Accounting Method */ - value = kvp_frame_get_slot_path (kvp, - KVP_OPTION_PATH, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_CURRENCY_ACCOUNTING, - NULL); - if (value) - { - if (strcmp (kvp_value_get_string (value), "trading") == 0) - return CURRENCY_ACCOUNTING_TRADING; - if (strcmp (kvp_value_get_string (value), "book-currency") == 0) - /* Must have a specified book-currency for 'book-currency' Currency - Accounting Method to be valid */ - { - value = kvp_frame_get_slot_path (kvp, - KVP_OPTION_PATH, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_BOOK_CURRENCY, - NULL); - if (!value) - /* Book-currency currency accounting method selected but no - book-currency specified - something is wrong */ - { - return CURRENCY_ACCOUNTING_NEITHER; - } - /* Something is stored there - presumably a valid currency */ - return CURRENCY_ACCOUNTING_BOOK_CURRENCY; - } - } - return CURRENCY_ACCOUNTING_NEITHER; -} - -/* Returns pointer to Book Currency unique_name for book or NULL */ +/** Returns pointer to book-currency name for book, if one exists in the + * KVP, or NULL; does not validate contents nor determine if there is a valid + * default gain/loss policy, both of which are required, for the + * 'book-currency' currency accounting method to apply. Use instead + * 'gnc_book_get_book_currency' which does these validations. */ const gchar * -qof_book_get_book_currency_unique_name (QofBook *book) +qof_book_get_book_currency (QofBook *book) { KvpFrame *kvp; KvpValue *value; @@ -971,30 +914,14 @@ qof_book_get_book_currency_unique_name (QofBook *book) return NULL; } - /* Get the Currency Accounting Method */ - value = kvp_frame_get_slot_path (kvp, - KVP_OPTION_PATH, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_CURRENCY_ACCOUNTING, - NULL); - if (!value) - /* No currency accounting method selected; therefore no book-currency */ - return NULL; - - if (!(strcmp (kvp_value_get_string (value), "book-currency") == 0)) - /* Not book-currency currency accounting method; therefore no - book-currency */ - return NULL; - - /* Book-currency is the currency accounting method; get the book currency */ + /* See if there is a book currency */ value = kvp_frame_get_slot_path (kvp, KVP_OPTION_PATH, OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY, NULL); if (!value) - /* Book-currency currency accounting method selected but no book-currency - specified - something is wrong */ + /* No book-currency */ { return NULL; } @@ -1002,32 +929,59 @@ qof_book_get_book_currency_unique_name (QofBook *book) return kvp_value_get_string (value); } +/** Returns pointer to default gain/loss policy for book, if one exists in the + * KVP, or NULL; does not validate contents nor determine if there is a valid + * book-currency, both of which are required, for the 'book-currency' + * currency accounting method to apply. Use instead + * 'gnc_book_get_default_gains_policy' which does these validations. */ +const gchar * +qof_book_get_default_gains_policy (QofBook *book) +{ + KvpFrame *kvp; + KvpValue *value; + + if (!book) + { + PWARN ("No book!!!"); + return NULL; + } + + /* Get the KVP from the current book */ + kvp = qof_instance_get_slots (QOF_INSTANCE (book)); + + if (!kvp) + { + PWARN ("Book has no KVP_Frame"); + return NULL; + } + + /* See if there is a default gain/loss policy */ + value = kvp_frame_get_slot_path (kvp, + KVP_OPTION_PATH, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_DEFAULT_GAINS_POLICY, + NULL); + if (!value) + /* No default gain/loss policy, therefore not valid book-currency + accounting method */ + { + return NULL; + } + + return kvp_value_get_string (value); +} + + /* Determine whether this book uses trading accounts */ gboolean qof_book_use_trading_accounts (const QofBook *book) { - /* Prior to version 2.7, Gnucash had a boolean flag for trading accounts; - need to accommodate files from version 2.6 and earlier */ const char *opt = NULL; qof_instance_get (QOF_INSTANCE (book), "trading-accts", &opt, NULL); if (opt && opt[0] == 't' && opt[1] == 0) return TRUE; - - /* Else, get the Currency Accounting Method */ - if (qof_book_currency_accounting_method (book) == CURRENCY_ACCOUNTING_TRADING) - return TRUE; - return FALSE; -} - -/* Returns TRUE if this book uses a book-currency */ -gboolean -qof_book_use_book_currency (const QofBook *book) -{ - /* Get the Currency Accounting Method */ - if (qof_book_currency_accounting_method (book) == CURRENCY_ACCOUNTING_BOOK_CURRENCY) - return TRUE; return FALSE; } diff --git a/src/libqof/qof/qofbook.h b/src/libqof/qof/qofbook.h index e3f27fa242..776a4414dc 100644 --- a/src/libqof/qof/qofbook.h +++ b/src/libqof/qof/qofbook.h @@ -245,11 +245,19 @@ void qof_book_mark_readonly(QofBook *book); /** Returns flag indicating whether this book uses trading accounts */ gboolean qof_book_use_trading_accounts (const QofBook *book); -/** Returns TRUE if this book uses a book-currency */ -gboolean qof_book_use_book_currency (const QofBook *book); +/** Returns pointer to book-currency name for book, if one exists in the + * KVP, or NULL; does not validate contents nor determine if there is a valid + * default gain/loss policy, both of which are required, for the + * 'book-currency' currency accounting method to apply. Use instead + * 'gnc_book_get_book_currency' which does these validations. */ +const gchar * qof_book_get_book_currency (QofBook *book); -/** Returns pointer to Book Currency unique_name for book or NULL */ -const gchar * qof_book_get_book_currency_unique_name (QofBook *book); +/** Returns pointer to default gain/loss policy for book, if one exists in the + * KVP, or NULL; does not validate contents nor determine if there is a valid + * book-currency, both of which are required, for the 'book-currency' + * currency accounting method to apply. Use instead + * 'gnc_book_get_default_gains_policy' which does these validations. */ +const gchar * qof_book_get_default_gains_policy (QofBook *book); /** Returns TRUE if the auto-read-only feature should be used, otherwise * FALSE. This is just a wrapper on qof_book_get_num_days_autoreadonly() == 0. */ diff --git a/src/libqof/qof/qofbookslots.h b/src/libqof/qof/qofbookslots.h index 825946289f..f820fd3c09 100644 --- a/src/libqof/qof/qofbookslots.h +++ b/src/libqof/qof/qofbookslots.h @@ -64,8 +64,9 @@ #define OPTION_SECTION_ACCOUNTS N_("Accounts") #define OPTION_NAME_TRADING_ACCOUNTS N_("Use Trading Accounts") -#define OPTION_NAME_CURRENCY_ACCOUNTING N_("Select Currency Accounting Method") -#define OPTION_NAME_BOOK_CURRENCY N_("Select Book Currency") +#define OPTION_NAME_CURRENCY_ACCOUNTING N_("Currency Accounting") +#define OPTION_NAME_BOOK_CURRENCY N_("Book Currency") +#define OPTION_NAME_DEFAULT_GAINS_POLICY N_("Default Gains Policy") #define OPTION_NAME_AUTO_READONLY_DAYS N_("Day Threshold for Read-Only Transactions (red line)") #define OPTION_NAME_NUM_FIELD_SOURCE N_("Use Split Action Field for Number") @@ -80,6 +81,7 @@ * OPTION-NAME-TRADING-ACCOUNTS * OPTION-NAME-CURRENCY-ACCOUNTING * OPTION-NAME-BOOK-CURRENCY + * OPTION_NAME_DEFAULT_GAINS_POLICY * OPTION-NAME-AUTO-READONLY-DAYS * OPTION-NAME_NUM-FIELD-SOURCE * OPTION-SECTION-BUDGETING diff --git a/src/libqof/qof/test/test-qofbook.c b/src/libqof/qof/test/test-qofbook.c index eb1aa2ed8f..b6ad804e15 100644 --- a/src/libqof/qof/test/test-qofbook.c +++ b/src/libqof/qof/test/test-qofbook.c @@ -364,9 +364,6 @@ test_book_increment_and_format_counter ( Fixture *fixture, gconstpointer pData ) g_free( r ); } -/* keep this testing of trading accounts, while adding testing of currency- - accounting-based trading accounts, so that files prior to version 2.7 - can be read/processed */ static void test_book_use_trading_accounts( Fixture *fixture, gconstpointer pData ) { @@ -388,66 +385,89 @@ test_book_use_trading_accounts( Fixture *fixture, gconstpointer pData ) } -static void -test_book_use_trading_accounts_currency_accounting( Fixture *fixture, gconstpointer pData ) -{ - g_assert( qof_book_use_trading_accounts( fixture-> book ) == FALSE ); - - g_test_message( "Testing with existing currency-accounting set to 'trading'" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "currency-accounting", "trading", - NULL); - g_assert( qof_book_use_trading_accounts( fixture-> book ) == TRUE ); - - g_test_message( "Testing with existing currency-accounting set to 'book-currency'" ); - qof_instance_set (QOF_INSTANCE (fixture->book), - "currency-accounting", "book-currency", - NULL); - g_assert( qof_book_use_trading_accounts( fixture-> book ) == FALSE ); - qof_book_commit_edit (fixture->book); - -} - static void test_book_use_book_currency( Fixture *fixture, gconstpointer pData ) { - const char *cur; + const gchar *cur; + const gchar *pol; - g_assert( qof_book_use_book_currency( fixture-> book ) == FALSE ); - g_assert( qof_book_get_book_currency_unique_name( fixture-> book ) == FALSE ); + cur = qof_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , NULL ); + pol = qof_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , NULL ); + g_test_message( "Testing with existing trading accounts set to true - t" ); qof_book_begin_edit (fixture->book); - g_test_message( "Testing with currency-accounting set to 'trading' and no book-currency" ); qof_instance_set (QOF_INSTANCE (fixture->book), - "currency-accounting", "trading", + "trading-accts", "t", NULL); - g_assert( qof_book_use_book_currency( fixture-> book ) == FALSE ); - g_assert( qof_book_get_book_currency_unique_name( fixture-> book ) == FALSE ); - - g_test_message( "Testing with currency-accounting set to 'book-currency' and no book-currency set" ); - qof_instance_set (QOF_INSTANCE (fixture->book), - "currency-accounting", "book-currency", - NULL); - g_assert( qof_book_use_book_currency( fixture-> book ) == FALSE ); - g_assert( qof_book_get_book_currency_unique_name( fixture-> book ) == FALSE ); - - g_test_message( "Testing with currency-accounting set to 'book-currency' and book-currency set" ); - qof_instance_set (QOF_INSTANCE (fixture->book), - "book-currency", "CURRENCY::USD", - NULL); - g_assert( qof_book_use_book_currency( fixture-> book ) == TRUE ); - cur = qof_book_get_book_currency_unique_name( fixture->book ); - g_assert_cmpstr( cur, == , "CURRENCY::USD"); - - g_test_message( "Testing with currency-accounting set to 'trading' and book-currency still set" ); - qof_instance_set (QOF_INSTANCE (fixture->book), - "currency-accounting", "trading", - NULL); - g_assert( qof_book_use_book_currency( fixture-> book ) == FALSE ); - g_assert( qof_book_get_book_currency_unique_name( fixture-> book ) == FALSE ); + cur = qof_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , NULL ); + pol = qof_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , NULL ); qof_book_commit_edit (fixture->book); + qof_book_destroy( fixture->book ); + fixture->book = qof_book_new(); + + g_test_message( "Testing with book-currency set and no default-gains-policy" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "book-currency", "USD", + NULL); + cur = qof_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , "USD" ); + pol = qof_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , NULL ); + qof_book_commit_edit (fixture->book); + + qof_book_destroy( fixture->book ); + fixture->book = qof_book_new(); + + g_test_message( "Testing with default-gains-policy set and no book-currency" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "default-gains-policy", "fifo", + NULL); + cur = qof_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , NULL ); + pol = qof_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , "fifo" ); + qof_book_commit_edit (fixture->book); + + qof_book_destroy( fixture->book ); + fixture->book = qof_book_new(); + + g_test_message( "Testing with book-currency and default-gains-policy set to nonsense" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "book-currency", "myMoney", + NULL); + qof_instance_set (QOF_INSTANCE (fixture->book), + "default-gains-policy", "random", + NULL); + cur = qof_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , "myMoney" ); + pol = qof_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , "random" ); + qof_book_commit_edit (fixture->book); + + qof_book_destroy( fixture->book ); + fixture->book = qof_book_new(); + + g_test_message( "Testing with book-currency and default-gains-policy set to valid values" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "book-currency", "USD", + NULL); + qof_instance_set (QOF_INSTANCE (fixture->book), + "default-gains-policy", "fifo", + NULL); + cur = qof_book_get_book_currency( fixture-> book ); + g_assert_cmpstr( cur, == , "USD" ); + pol = qof_book_get_default_gains_policy( fixture-> book ); + g_assert_cmpstr( pol, == , "fifo" ); + qof_book_commit_edit (fixture->book); } static void @@ -822,7 +842,6 @@ test_suite_qofbook ( void ) GNC_TEST_ADD( suitename, "get counter format", Fixture, NULL, setup, test_book_get_counter_format, teardown ); GNC_TEST_ADD( suitename, "increment and format counter", Fixture, NULL, setup, test_book_increment_and_format_counter, teardown ); GNC_TEST_ADD( suitename, "use trading accounts", Fixture, NULL, setup, test_book_use_trading_accounts, teardown ); - GNC_TEST_ADD( suitename, "use trading accounts - currency accounting", Fixture, NULL, setup, test_book_use_trading_accounts_currency_accounting, teardown ); GNC_TEST_ADD( suitename, "use book-currency", Fixture, NULL, setup, test_book_use_book_currency, teardown ); GNC_TEST_ADD( suitename, "get autofreeze days", Fixture, NULL, setup, test_book_get_num_days_autofreeze, teardown ); GNC_TEST_ADD( suitename, "use split action for num field", Fixture, NULL, setup, test_book_use_split_action_for_num_field, teardown );