From 605a082d7776453e59d02e4b4db8312589ef8cc6 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Thu, 27 Sep 2018 10:54:16 -1000 Subject: [PATCH] bs/bse/cf/is: use account type declarations if any These commands now detect the account types declared by account directives. Whenever such declarations are not present, built-in regular expressions are used, as before. --- hledger-lib/Hledger/Data/Journal.hs | 97 +++++++++++++------ .../Hledger/Cli/Commands/Incomestatement.hs | 2 +- tests/journal/accounts.test | 57 ++++++++++- 3 files changed, 122 insertions(+), 34 deletions(-) diff --git a/hledger-lib/Hledger/Data/Journal.hs b/hledger-lib/Hledger/Data/Journal.hs index 5f5a54dac..40355b950 100644 --- a/hledger-lib/Hledger/Data/Journal.hs +++ b/hledger-lib/Hledger/Data/Journal.hs @@ -53,7 +53,7 @@ module Hledger.Data.Journal ( -- * Standard account types journalBalanceSheetAccountQuery, journalProfitAndLossAccountQuery, - journalIncomeAccountQuery, + journalRevenueAccountQuery, journalExpenseAccountQuery, journalAssetAccountQuery, journalLiabilityAccountQuery, @@ -279,24 +279,65 @@ journalAccountNames = journalAccountNamesDeclaredOrImplied journalAccountNameTree :: Journal -> Tree AccountName journalAccountNameTree = accountNameTreeFrom . journalAccountNames --- standard account types +-- queries for standard account types --- | A query for Profit & Loss accounts in this journal. --- Cf . -journalProfitAndLossAccountQuery :: Journal -> Query -journalProfitAndLossAccountQuery j = Or [journalIncomeAccountQuery j - ,journalExpenseAccountQuery j - ] +-- | Get a query for accounts of a certain type (Asset, Liability..) in this journal. +-- The query will match all accounts which were declared as that type by account directives, +-- plus all their subaccounts which have not been declared as a different type. +-- If no accounts were declared as this type, the query will instead match accounts +-- with names matched by the provided case-insensitive regular expression. +journalAccountTypeQuery :: AccountType -> Regexp -> Journal -> Query +journalAccountTypeQuery atype fallbackregex j = + case M.lookup atype (jdeclaredaccounttypes j) of + Nothing -> Acct fallbackregex + Just as -> + -- XXX Query isn't able to match account type since that requires extra info from the journal. + -- So we do a hacky search by name instead. + And [ + Or $ map (Acct . accountNameToAccountRegex) as + ,Not $ Or $ map (Acct . accountNameToAccountRegex) differentlytypedsubs + ] + where + differentlytypedsubs = concat + [subs | (t,bs) <- M.toList (jdeclaredaccounttypes j) + , t /= atype + , let subs = [b | b <- bs, any (`isAccountNamePrefixOf` b) as] + ] --- | A query for Income (Revenue) accounts in this journal. --- This is currently hard-coded to the case-insensitive regex @^(income|revenue)s?(:|$)@. -journalIncomeAccountQuery :: Journal -> Query -journalIncomeAccountQuery _ = Acct "^(income|revenue)s?(:|$)" +-- | A query for accounts in this journal which have been +-- declared as Asset by account directives, or otherwise for +-- accounts with names matched by the case-insensitive regular expression +-- @^assets?(:|$)@. +journalAssetAccountQuery :: Journal -> Query +journalAssetAccountQuery = journalAccountTypeQuery Asset "^assets?(:|$)" --- | A query for Expense accounts in this journal. --- This is currently hard-coded to the case-insensitive regex @^expenses?(:|$)@. +-- | A query for accounts in this journal which have been +-- declared as Liability by account directives, or otherwise for +-- accounts with names matched by the case-insensitive regular expression +-- @^(debts?|liabilit(y|ies))(:|$)@. +journalLiabilityAccountQuery :: Journal -> Query +journalLiabilityAccountQuery = journalAccountTypeQuery Liability "^(debts?|liabilit(y|ies))(:|$)" + +-- | A query for accounts in this journal which have been +-- declared as Equity by account directives, or otherwise for +-- accounts with names matched by the case-insensitive regular expression +-- @^equity(:|$)@. +journalEquityAccountQuery :: Journal -> Query +journalEquityAccountQuery = journalAccountTypeQuery Equity "^equity(:|$)" + +-- | A query for accounts in this journal which have been +-- declared as Revenue by account directives, or otherwise for +-- accounts with names matched by the case-insensitive regular expression +-- @^(income|revenue)s?(:|$)@. +journalRevenueAccountQuery :: Journal -> Query +journalRevenueAccountQuery = journalAccountTypeQuery Revenue "^(income|revenue)s?(:|$)" + +-- | A query for accounts in this journal which have been +-- declared as Expense by account directives, or otherwise for +-- accounts with names matched by the case-insensitive regular expression +-- @^(income|revenue)s?(:|$)@. journalExpenseAccountQuery :: Journal -> Query -journalExpenseAccountQuery _ = Acct "^expenses?(:|$)" +journalExpenseAccountQuery = journalAccountTypeQuery Expense "^expenses?(:|$)" -- | A query for Asset, Liability & Equity accounts in this journal. -- Cf . @@ -306,25 +347,17 @@ journalBalanceSheetAccountQuery j = Or [journalAssetAccountQuery j ,journalEquityAccountQuery j ] --- | A query for Asset accounts in this journal. --- This is currently hard-coded to the case-insensitive regex @^assets?(:|$)@. -journalAssetAccountQuery :: Journal -> Query -journalAssetAccountQuery _ = Acct "^assets?(:|$)" - --- | A query for Liability accounts in this journal. --- This is currently hard-coded to the case-insensitive regex @^(debts?|liabilit(y|ies))(:|$)@. -journalLiabilityAccountQuery :: Journal -> Query -journalLiabilityAccountQuery _ = Acct "^(debts?|liabilit(y|ies))(:|$)" - --- | A query for Equity accounts in this journal. --- This is currently hard-coded to the case-insensitive regex @^equity(:|$)@. -journalEquityAccountQuery :: Journal -> Query -journalEquityAccountQuery _ = Acct "^equity(:|$)" +-- | A query for Profit & Loss accounts in this journal. +-- Cf . +journalProfitAndLossAccountQuery :: Journal -> Query +journalProfitAndLossAccountQuery j = Or [journalRevenueAccountQuery j + ,journalExpenseAccountQuery j + ] -- | A query for Cash (-equivalent) accounts in this journal (ie, -- accounts which appear on the cashflow statement.) This is currently --- hard-coded to be all the Asset accounts except for those containing the --- case-insensitive regex @(receivable|:A/R|:fixed)@. +-- hard-coded to be all the Asset accounts except for those with names +-- containing the case-insensitive regular expression @(receivable|:A/R|:fixed)@. journalCashAccountQuery :: Journal -> Query journalCashAccountQuery j = And [journalAssetAccountQuery j, Not $ Acct "(receivable|:A/R|:fixed)"] @@ -1089,7 +1122,7 @@ tests_Journal = tests "Journal" [ test "assets" $ expectEq (namesfrom journalAssetAccountQuery) ["assets","assets:bank","assets:bank:checking","assets:bank:saving","assets:cash"] ,test "liabilities" $ expectEq (namesfrom journalLiabilityAccountQuery) ["liabilities","liabilities:debts"] ,test "equity" $ expectEq (namesfrom journalEquityAccountQuery) [] - ,test "income" $ expectEq (namesfrom journalIncomeAccountQuery) ["income","income:gifts","income:salary"] + ,test "income" $ expectEq (namesfrom journalRevenueAccountQuery) ["income","income:gifts","income:salary"] ,test "expenses" $ expectEq (namesfrom journalExpenseAccountQuery) ["expenses","expenses:food","expenses:supplies"] ] diff --git a/hledger/Hledger/Cli/Commands/Incomestatement.hs b/hledger/Hledger/Cli/Commands/Incomestatement.hs index e93081809..3793e7066 100644 --- a/hledger/Hledger/Cli/Commands/Incomestatement.hs +++ b/hledger/Hledger/Cli/Commands/Incomestatement.hs @@ -34,7 +34,7 @@ Note this report shows all account balances with normal positive sign cbcqueries = [ CBCSubreportSpec{ cbcsubreporttitle="Revenues" - ,cbcsubreportquery=journalIncomeAccountQuery + ,cbcsubreportquery=journalRevenueAccountQuery ,cbcsubreportnormalsign=NormallyNegative ,cbcsubreportincreasestotal=True } diff --git a/tests/journal/accounts.test b/tests/journal/accounts.test index 5f87bcef5..2e7955ec9 100644 --- a/tests/journal/accounts.test +++ b/tests/journal/accounts.test @@ -1,6 +1,6 @@ # account names -# 1. account directives are affected by aliases and apply account. +# 1. "apply account" and "alias" affect "account" directives. < apply account c alias c:a=b @@ -8,7 +8,62 @@ account a $ hledger -f - accounts b +# 2. account directives can declare account type. +# Here "asset" is a liability, despite the name. So are its subaccounts. +# "b" is a liability. "b:bb" is an asset. +< +; a liability +account asset L +; an asset +account b:bb A +; a liability +account b L +2018/1/1 + (asset:a) 1 + (b) 2 + (b:bb) 3 + +$ hledger -f - bs -N --flat +Balance Sheet 2018/01/01 + + || 2018/01/01 +=============++============ + Assets || +-------------++------------ + b:bb || 3 +-------------++------------ + || 3 +=============++============ + Liabilities || +-------------++------------ + asset:a || -1 + b || -2 +-------------++------------ + || -3 + +# 3. Tree mode. A little weird, b appears twice. +# It must be shown above bb, but since not an asset, its balance is excluded there. +# It is shown again in the liabilities section, this time with balance. +$ hledger -f - bs -N +Balance Sheet 2018/01/01 + + || 2018/01/01 +=============++============ + Assets || +-------------++------------ + b || 3 + bb || 3 +-------------++------------ + || 3 +=============++============ + Liabilities || +-------------++------------ + asset || -1 + a || -1 + b || -2 +-------------++------------ + || -3 # TODO # a trailing : should give a clear error