60 lines
		
	
	
		
			1.8 KiB
		
	
	
	
		
			Haskell
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			60 lines
		
	
	
		
			1.8 KiB
		
	
	
	
		
			Haskell
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env stack
 | 
						|
{- stack runghc --verbosity info
 | 
						|
   --package hledger-lib
 | 
						|
   --package here
 | 
						|
   --package safe
 | 
						|
   --package text
 | 
						|
-}
 | 
						|
 | 
						|
{-# LANGUAGE QuasiQuotes #-}
 | 
						|
 | 
						|
import Hledger
 | 
						|
import Hledger.Cli
 | 
						|
import Text.Printf (printf)
 | 
						|
import System.Environment (getArgs)
 | 
						|
import Safe (headDef)
 | 
						|
import Data.List
 | 
						|
import Data.Function
 | 
						|
import Data.String.Here
 | 
						|
import qualified Data.Text as T
 | 
						|
 | 
						|
------------------------------------------------------------------------------
 | 
						|
cmdmode = hledgerCommandMode
 | 
						|
  [here| dupes
 | 
						|
Reports duplicates in the account tree: account names having the same leaf
 | 
						|
but different prefixes. In other words, two or more leaves that are
 | 
						|
categorized differently.
 | 
						|
Reads the default journal file, or another specified as an argument.
 | 
						|
 
 | 
						|
http://stefanorodighiero.net/software/hledger-dupes.html
 | 
						|
  |]
 | 
						|
  []
 | 
						|
  [generalflagsgroup1]
 | 
						|
  []
 | 
						|
  ([], Nothing)
 | 
						|
------------------------------------------------------------------------------
 | 
						|
 | 
						|
main = do
 | 
						|
  opts <- getHledgerCliOpts cmdmode
 | 
						|
  withJournalDo opts $ \CliOpts{rawopts_=opts,reportopts_=ropts} j -> do
 | 
						|
    mapM_ render $ dupes $ accountsNames j
 | 
						|
 | 
						|
accountsNames :: Journal -> [(String, AccountName)]
 | 
						|
accountsNames j = map leafAndAccountName as
 | 
						|
  where leafAndAccountName a = (T.unpack $ accountLeafName a, a)
 | 
						|
        ps = journalPostings j
 | 
						|
        as = nub $ sort $ map paccount ps
 | 
						|
 | 
						|
 | 
						|
dupes :: (Ord k, Eq k) => [(k, v)] -> [(k, [v])]
 | 
						|
dupes l = zip dupLeafs dupAccountNames
 | 
						|
  where dupLeafs = map (fst . head) d
 | 
						|
        dupAccountNames = map (map snd) d
 | 
						|
        d = dupes' l
 | 
						|
        dupes' = filter ((> 1) . length)
 | 
						|
          . groupBy ((==) `on` fst)
 | 
						|
          . sortBy (compare `on` fst)
 | 
						|
 | 
						|
render :: (String, [AccountName]) -> IO ()
 | 
						|
render (leafName, accountNameL) = printf "%s as %s\n" leafName (concat $ intersperse ", " (map T.unpack accountNameL))
 |