--alias command-line option
This commit is contained in:
		
							parent
							
								
									30b7448f45
								
							
						
					
					
						commit
						957c349780
					
				
							
								
								
									
										18
									
								
								MANUAL.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								MANUAL.md
									
									
									
									
									
								
							| @ -439,7 +439,8 @@ Included files are also affected, eg: | |||||||
| ### Account aliases | ### Account aliases | ||||||
| 
 | 
 | ||||||
| You can define account aliases to rewrite certain account names (and their subaccounts). | You can define account aliases to rewrite certain account names (and their subaccounts). | ||||||
| The format is `alias ORIGACCT = ALIAS`. Use `end aliases` to forget all previously defined aliases. | The format is `alias ORIG = ALIAS`, where ORIG and ALIAS are full account names. | ||||||
|  | To forget all aliases defined to this point, use `end aliases`. | ||||||
| 
 | 
 | ||||||
| Here's an example: say a sole proprietor has a personal.journal: | Here's an example: say a sole proprietor has a personal.journal: | ||||||
| 
 | 
 | ||||||
| @ -475,6 +476,21 @@ giving: | |||||||
|         expenses:office supplies            $1 |         expenses:office supplies            $1 | ||||||
|         assets:business checking           $-1 |         assets:business checking           $-1 | ||||||
| 
 | 
 | ||||||
|  | You can also specify aliases on the command line. This could be useful to | ||||||
|  | rewrite account names when sharing a report with someone else, such as | ||||||
|  | your accountant: | ||||||
|  | 
 | ||||||
|  |     $ hledger --alias 'my earning=income:business' | ||||||
|  | 
 | ||||||
|  | Command-line alias options are applied after any alias directives in the | ||||||
|  | journal.  At most one alias directive and one alias option will be applied | ||||||
|  | to each account name. | ||||||
|  | 
 | ||||||
|  | Aliases tend to be a little more reliable than post-processing with sed or | ||||||
|  | similar, as they know about account name syntax, posting type indicators | ||||||
|  | etc. Note aliases only change the displayed names, not the account | ||||||
|  | hierarchy - aliasing two accounts to the same name does not merge them | ||||||
|  | into one account. | ||||||
| 
 | 
 | ||||||
| ## Core commands | ## Core commands | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -242,6 +242,13 @@ journalSelectingDate ActualDate j = j | |||||||
| journalSelectingDate EffectiveDate j = | journalSelectingDate EffectiveDate j = | ||||||
|     j{jtxns=map (journalTransactionWithDate EffectiveDate) $ jtxns j} |     j{jtxns=map (journalTransactionWithDate EffectiveDate) $ jtxns j} | ||||||
| 
 | 
 | ||||||
|  | -- | Apply additional account aliases (eg from the command-line) to all postings in a journal. | ||||||
|  | journalApplyAliases :: [(AccountName,AccountName)] -> Journal -> Journal | ||||||
|  | journalApplyAliases aliases j@Journal{jtxns=ts} = j{jtxns=map fixtransaction ts} | ||||||
|  |     where | ||||||
|  |       fixtransaction t@Transaction{tpostings=ps} = t{tpostings=map fixposting ps} | ||||||
|  |       fixposting p@Posting{paccount=a} = p{paccount=accountNameApplyAliases aliases a} | ||||||
|  | 
 | ||||||
| -- | Do post-parse processing on a journal, to make it ready for use. | -- | Do post-parse processing on a journal, to make it ready for use. | ||||||
| journalFinalise :: ClockTime -> LocalTime -> FilePath -> String -> JournalContext -> Journal -> Either String Journal | journalFinalise :: ClockTime -> LocalTime -> FilePath -> String -> JournalContext -> Journal -> Either String Journal | ||||||
| journalFinalise tclock tlocal path txt ctx j@Journal{files=fs} = | journalFinalise tclock tlocal path txt ctx j@Journal{files=fs} = | ||||||
|  | |||||||
| @ -99,7 +99,7 @@ postingsDateSpan [] = DateSpan Nothing Nothing | |||||||
| postingsDateSpan ps = DateSpan (Just $ postingDate $ head ps') (Just $ addDays 1 $ postingDate $ last ps') | postingsDateSpan ps = DateSpan (Just $ postingDate $ head ps') (Just $ addDays 1 $ postingDate $ last ps') | ||||||
|     where ps' = sortBy (comparing postingDate) ps |     where ps' = sortBy (comparing postingDate) ps | ||||||
| 
 | 
 | ||||||
| -- balanced/non-balanced posting indicators | -- AccountName stuff that depends on PostingType | ||||||
| 
 | 
 | ||||||
| accountNamePostingType :: AccountName -> PostingType | accountNamePostingType :: AccountName -> PostingType | ||||||
| accountNamePostingType a | accountNamePostingType a | ||||||
| @ -131,6 +131,15 @@ concatAccountNames :: [AccountName] -> AccountName | |||||||
| concatAccountNames as = accountNameWithPostingType t $ intercalate ":" $ map accountNameWithoutPostingType as | concatAccountNames as = accountNameWithPostingType t $ intercalate ":" $ map accountNameWithoutPostingType as | ||||||
|     where t = headDef RegularPosting $ filter (/= RegularPosting) $ map accountNamePostingType as |     where t = headDef RegularPosting $ filter (/= RegularPosting) $ map accountNamePostingType as | ||||||
| 
 | 
 | ||||||
|  | -- | Rewrite an account name using the first applicable alias from the given list, if any. | ||||||
|  | accountNameApplyAliases :: [(AccountName,AccountName)] -> AccountName -> AccountName | ||||||
|  | accountNameApplyAliases aliases a = withorigtype | ||||||
|  |     where | ||||||
|  |       (a',t) = (accountNameWithoutPostingType a, accountNamePostingType a) | ||||||
|  |       firstmatchingalias = headDef Nothing $ map Just $ filter (\(orig,_) -> orig == a' || orig `isAccountNamePrefixOf` a') aliases | ||||||
|  |       rewritten = maybe a' (\(orig,alias) -> alias++drop (length orig) a') firstmatchingalias | ||||||
|  |       withorigtype = accountNameWithPostingType t rewritten | ||||||
|  | 
 | ||||||
| tests_Hledger_Data_Posting = TestList [ | tests_Hledger_Data_Posting = TestList [ | ||||||
| 
 | 
 | ||||||
|   "accountNamePostingType" ~: do |   "accountNamePostingType" ~: do | ||||||
|  | |||||||
| @ -482,12 +482,7 @@ modifiedaccountname = do | |||||||
|   prefix <- getParentAccount |   prefix <- getParentAccount | ||||||
|   let prefixed = prefix `joinAccountNames` a |   let prefixed = prefix `joinAccountNames` a | ||||||
|   aliases <- getAccountAliases |   aliases <- getAccountAliases | ||||||
|   let t = accountNamePostingType prefixed |   return $ accountNameApplyAliases aliases prefixed | ||||||
|       a' = accountNameWithoutPostingType prefixed |  | ||||||
|       match = headDef Nothing $ map Just $ filter (\(orig,_) -> orig == a' || orig `isAccountNamePrefixOf` a') aliases |  | ||||||
|       rewritten = maybe a' (\(orig,alias) -> alias++drop (length orig) a') match |  | ||||||
|       withtype = accountNameWithPostingType t rewritten |  | ||||||
|   return withtype |  | ||||||
| 
 | 
 | ||||||
| -- | Parse an account name. Account names may have single spaces inside | -- | Parse an account name. Account names may have single spaces inside | ||||||
| -- them, and are terminated by two or more spaces. They should have one or | -- them, and are terminated by two or more spaces. They should have one or | ||||||
|  | |||||||
| @ -62,6 +62,7 @@ options_cli :: [OptDescr Opt] | |||||||
| options_cli = [ | options_cli = [ | ||||||
|   Option "f" ["file"]         (ReqArg File "FILE")   "use a different journal/timelog file; - means stdin" |   Option "f" ["file"]         (ReqArg File "FILE")   "use a different journal/timelog file; - means stdin" | ||||||
|  ,Option ""  ["no-new-accounts"] (NoArg NoNewAccts)  "don't allow to create new accounts" |  ,Option ""  ["no-new-accounts"] (NoArg NoNewAccts)  "don't allow to create new accounts" | ||||||
|  |  ,Option ""  ["alias"]        (ReqArg Alias "ACCT=ALIAS")  "display ACCT's name as ALIAS instead" | ||||||
|  ,Option "b" ["begin"]        (ReqArg Begin "DATE")  "report on transactions on or after this date" |  ,Option "b" ["begin"]        (ReqArg Begin "DATE")  "report on transactions on or after this date" | ||||||
|  ,Option "e" ["end"]          (ReqArg End "DATE")    "report on transactions before this date" |  ,Option "e" ["end"]          (ReqArg End "DATE")    "report on transactions before this date" | ||||||
|  ,Option "p" ["period"]       (ReqArg Period "EXPR") ("report on transactions during the specified period\n" ++ |  ,Option "p" ["period"]       (ReqArg Period "EXPR") ("report on transactions during the specified period\n" ++ | ||||||
| @ -96,6 +97,7 @@ options_cli = [ | |||||||
| data Opt =  | data Opt =  | ||||||
|     File          {value::String} |     File          {value::String} | ||||||
|     | NoNewAccts |     | NoNewAccts | ||||||
|  |     | Alias       {value::String} | ||||||
|     | Begin       {value::String} |     | Begin       {value::String} | ||||||
|     | End         {value::String} |     | End         {value::String} | ||||||
|     | Period      {value::String} |     | Period      {value::String} | ||||||
| @ -306,6 +308,18 @@ journalFilePathFromOpts opts = do | |||||||
|   f <- if istimequery then myTimelogPath else myJournalPath |   f <- if istimequery then myTimelogPath else myJournalPath | ||||||
|   return $ last $ f : optValuesForConstructor File opts |   return $ last $ f : optValuesForConstructor File opts | ||||||
| 
 | 
 | ||||||
|  | aliasesFromOpts :: [Opt] -> [(AccountName,AccountName)] | ||||||
|  | aliasesFromOpts opts = map parseAlias $ optValuesForConstructor Alias opts | ||||||
|  |     where | ||||||
|  |       -- similar to ledgerAlias | ||||||
|  |       parseAlias :: String -> (AccountName,AccountName) | ||||||
|  |       parseAlias s = (accountNameWithoutPostingType $ strip orig | ||||||
|  |                      ,accountNameWithoutPostingType $ strip alias') | ||||||
|  |           where | ||||||
|  |             (orig, alias) = break (=='=') s | ||||||
|  |             alias' = case alias of ('=':rest) -> rest | ||||||
|  |                                    _ -> orig | ||||||
|  | 
 | ||||||
| -- | Gather filter pattern arguments into a list of account patterns and a | -- | Gather filter pattern arguments into a list of account patterns and a | ||||||
| -- list of description patterns. We interpret pattern arguments as | -- list of description patterns. We interpret pattern arguments as | ||||||
| -- follows: those prefixed with "desc:" are description patterns, all | -- follows: those prefixed with "desc:" are description patterns, all | ||||||
|  | |||||||
| @ -38,7 +38,7 @@ import System.Time (ClockTime, getClockTime, diffClockTimes, TimeDiff(TimeDiff)) | |||||||
| import Test.HUnit | import Test.HUnit | ||||||
| import Text.Printf | import Text.Printf | ||||||
| 
 | 
 | ||||||
| import Hledger.Cli.Options (Opt(..),journalFilePathFromOpts,whichDateFromOpts) | import Hledger.Cli.Options | ||||||
| import Hledger.Data | import Hledger.Data | ||||||
| import Hledger.Read | import Hledger.Read | ||||||
| import Hledger.Utils | import Hledger.Utils | ||||||
| @ -51,7 +51,8 @@ withJournalDo opts args _ cmd = do | |||||||
|   -- We kludgily read the file before parsing to grab the full text, unless |   -- We kludgily read the file before parsing to grab the full text, unless | ||||||
|   -- it's stdin, or it doesn't exist and we are adding. We read it strictly |   -- it's stdin, or it doesn't exist and we are adding. We read it strictly | ||||||
|   -- to let the add command work. |   -- to let the add command work. | ||||||
|   journalFilePathFromOpts opts >>= readJournalFile Nothing >>= either error' (cmd opts args) |   journalFilePathFromOpts opts >>= readJournalFile Nothing >>= | ||||||
|  |     either error' (cmd opts args . journalApplyAliases (aliasesFromOpts opts)) | ||||||
| 
 | 
 | ||||||
| -- -- | Get a journal from the given string and options, or throw an error. | -- -- | Get a journal from the given string and options, or throw an error. | ||||||
| -- readJournalWithOpts :: [Opt] -> String -> IO Journal | -- readJournalWithOpts :: [Opt] -> String -> IO Journal | ||||||
|  | |||||||
							
								
								
									
										49
									
								
								tests/aliases.test
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								tests/aliases.test
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | |||||||
|  | # alias-related tests | ||||||
|  | 
 | ||||||
|  | # 1. command-line --alias option.  Note multiple applicable aliases, but | ||||||
|  | # only one is applied per account name.  Spaces are allowed if quoted. | ||||||
|  | bin/hledger -f- print --alias 'a a=A' --alias b=B | ||||||
|  | <<< | ||||||
|  | 2011/01/01 | ||||||
|  |     a a  1 | ||||||
|  |     c | ||||||
|  | 
 | ||||||
|  | >>> | ||||||
|  | 2011/01/01 | ||||||
|  |     A             1 | ||||||
|  |     c            -1 | ||||||
|  | 
 | ||||||
|  | >>>=0 | ||||||
|  | 
 | ||||||
|  | # 2. alias directive, and an account with unbalanced posting indicators. | ||||||
|  | bin/hledger -f- print | ||||||
|  | <<< | ||||||
|  | alias b=B | ||||||
|  | 
 | ||||||
|  | 2011/01/01 | ||||||
|  |     (b)  1 | ||||||
|  | 
 | ||||||
|  | >>> | ||||||
|  | 2011/01/01 | ||||||
|  |     (B)             1 | ||||||
|  | 
 | ||||||
|  | >>>=0 | ||||||
|  | 
 | ||||||
|  | # 3. --alias options run after alias directives. Subaccounts are also | ||||||
|  | # matched and rewritten. Accounts with an internal part matching the alias | ||||||
|  | # are ignored. | ||||||
|  | bin/hledger -f- print --alias a=A --alias B=C | ||||||
|  | <<< | ||||||
|  | alias a=B | ||||||
|  | 
 | ||||||
|  | 2011/01/01 | ||||||
|  |     [a:x]    1 | ||||||
|  |     [x:a:x] | ||||||
|  | 
 | ||||||
|  | >>> | ||||||
|  | 2011/01/01 | ||||||
|  |     [C:x]             1 | ||||||
|  |     [x:a:x]            -1 | ||||||
|  | 
 | ||||||
|  | >>>2 | ||||||
|  | >>>=0 | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user