packages feed

penny-bin 0.20.0.0 → 0.22.0.0

raw patch · 7 files changed

+99/−281 lines, 7 filesdep ~multiargdep ~penny-lib

Dependency ranges changed: multiarg, penny-lib

Files

doc/ledger-grammar.org view
@@ -16,6 +16,7 @@ Letter = UpperCaseAscii ∪ LowerCaseAscii ∪ NonAscii Dollar = { '$' } Colon = { ':' }+Comma = { ',' } OpenCurly = { '{' } CloseCurly = { '}' } OpenSquare = { '[' }@@ -105,6 +106,18 @@ <lvl3Cmdty> ::= Lvl3CmdtyChar+  * Quantities++<digListSpace> ::= Space Digit++<digListThinSpace> ::= ThinSpace Digit++<digListComma> ::= Comma Digit++<digListPeriod> ::= Period Digit+++<digGrpSpace> ::= Digit+ <digListSpace>++<digGrpThinSpace> ::= Digit+ <digListThinSpace>++<digGrpComma> ::= Digit+ <digListComma>++<digGrpPeriod> ::= Digit+ <digListPeriod>+++  <digitGroup> ::= ThinSpace Digit+ <digitSequence> ::= Digit+ <digitGroup>*
penny-bin.cabal view
@@ -1,5 +1,5 @@ Name: penny-bin-Version: 0.20.0.0+Version: 0.22.0.0 Cabal-version: >=1.8 Build-Type: Simple License: BSD3@@ -11,162 +11,10 @@ bug-reports: omari@smileystation.com Category: Console, Finance License-File: LICENSE-synopsis: Extensible double-entry accounting system - binary and documentation--description: Penny is a double-entry accounting system. It is inspired-  by, but incompatible with, John Wiegley's Ledger, which is available-  at <http://ledger-cli.org/>. Installing this package with cabal-  install will install the executable program and the necessary-  libraries.--  .--  * Penny is a double-entry accounting system. It uses traditional-  accounting terminology, such as the terms \"Debit\" and-  \"Credit\". If you need a refresher on the basics of double-entry-  accounting, pick up a used accounting textbook from your favorite-  bookseller (they can be had cheaply, for less than ten U.S. dollars-  including shipping) or check out-  <http://www.principlesofaccounting.com/>, a great free online text.--  .--  * Penny is based around "Penny.Lincoln", a core library to represent-    transactions and postings and their components, such as their-    amounts and whether they are debits and credits. You can use-    Lincoln all by itself even if you don't use the other components-    of Penny, which you may find handy if you are a Haskell-    programmer. I wrote Penny because I wanted a precise library to-    represent my accounting data so I could analyze it programatically-    and verify its consistency.--  .--  * Penny's command line interface and its reports give you-    great flexibility to filter and sort postings. Each posting-    within a transaction may have its own flags assigned (e.g. to-    indicate whether the posting is cleared) and each posting may have-    infinite \"tags\" assigned to it, giving you another way to-    categorize your postings. For instance, you might have vacation-    related postings in several different accounts, but you can give-    them all a \"vacation\" tag.--  .--  * You can easily build a program to process downloads of Open-    Financial Exchange data from your financial institution. Your-    program will merge new transactions into your ledger-    automatically.--  .--  * Full Unicode support.--  .--  * Penny's reports have color baked in from the beginning. You do not-    have to use color, which is handy if you are sending-    output to a file or if, well, you just don't like color.--  .--  * Penny's reports automatically adjust themselves to the width of-    your screen. You can easily specify how much or how little data to-    see with command line options.--  .--  * Penny handles multiple commodities (for example, multiple-    currencies, stocks and bonds, tracking other assets, etc.) in an-    easy and transparent way that is consistent with double-entry-    accounting principles. It embraces the philosophy outlined in this-    tutorial on multiple commodity accounting:-    <http://www.mscs.dal.ca/~selinger/accounting/tutorial.html>.--  .--  * Penny stores amounts using only integers.  This ensures the-    accuracy of your data, as using floating point values to represent-    money is a bad idea. Here is one explanation:-    <http://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency>. The-    use of integer arithmetic also makes Penny simpler internally, as-    there is no need for arbitrary rounding to compensate for the-    bizarre and inaccurate results that sometimes arise from the use of-    floating-point values to represent currencies.--  .--  * Freely licensed under the MIT license. If you take this code,-    improve it, lock it up and make it proprietary, and sell it,-    AWESOME! I haven't lost anything because I still have my code and,-    what's more, then maybe I can buy your product and not have to-    maintain this one any more!--  .--  * Tested using QuickCheck. The tests are available in the Git-    repository that also contains the main library. Not everything-    is tested, but the tests that exist so far have already rooted-    out some strange corner-case bugs.--  .--  Non-features / disadvantages:--  .--  * Written in Haskell. Yes, I think Haskell is the best tool ever,-    but its compiler is not as commonly installed as compilers for C-    or C++, and non-Haskellers will probably find Penny to be more-    difficult to install than Ledger, as the latter is written in C++.--  .--  * Handling commodities requires that you set up multiple accounts;-    some might find this cumbersome.--  .--  * Young and not well tested yet.--  .--  * Runs only on Unix-like operating systems.--  .--  * Full Penny functionality is available without a Haskell compiler;-    you could even use a pre-compiled binary.  However, Penny does not-    read configuration files at runtime; instead, to change the-    default settings, you will need to have GHC installed so that you-    can compile a custom binary.--  .--  * Can be slow and memory hungry with large data sets. I have a-    ledger file with about 28,000 lines. On my least capable machine-    (which has an Intel Core 2 Duo at 1.6 GHz) this takes about 1.4-    seconds to parse. Not horrible but not instantaneous-    either. Generating a report about all these transactions can take-    about seven seconds and a little less than 300 MB of memory. I-    have eliminated all the obvious slowness from the code and-    attempted a rewrite of the parser, which made no difference; other-    ideas to speed up Penny with large data sets would involve-    substantial changes and this is not at the top of my list because-    the program is currently usable with relatively recent hardware.--  .+synopsis: Deprecated - use penny package instead -  You can install this binary simply by typing "cabal install-  penny-bin", which will install this binary along with all the-  necessary dependencies. The penny-bin package also has all-  documentation. Unfortunately running "cabal install" will not-  install the documentation, so you will need to find the downloaded-  archive (usually in-  "$HOME/.cabal/packages/hackage.haskell.org/penny-bin") and unpack it-  to see the documentation. You will want to start by reading the-  README file, which will point you to additional documentation and-  how to install it if you wish.+description:+  This package is now deprecated.  Use the penny package instead.  extra-source-files:     install-docs@@ -186,7 +34,7 @@ Executable penny   Build-depends:     base ==4.*,-    penny-lib ==0.20.*+    penny-lib ==0.22.*    Main-is: penny-main.hs   Other-modules: Paths_penny_bin@@ -200,13 +48,13 @@ Executable penny-selloff   Build-depends:     base == 4.*,-    penny-lib ==0.20.*,+    penny-lib ==0.22.*,     explicit-exception ==0.1.*,     containers ==0.5.*,     semigroups ==0.9.*,     text ==0.11.*,     parsec ==3.1.*,-    multiarg ==0.16.*,+    multiarg ==0.18.*,     transformers ==0.3.*    Main-is: penny-selloff.hs@@ -220,9 +68,9 @@ Executable penny-diff   Build-depends:     base ==4.*,-    penny-lib ==0.20.*,+    penny-lib ==0.22.*,     text ==0.11.*,-    multiarg ==0.16.*,+    multiarg ==0.18.*,     explicit-exception == 0.1.*    Main-is: penny-diff.hs@@ -237,8 +85,8 @@ Executable penny-reprint   Build-depends:       base ==4.*-    , multiarg ==0.16.*-    , penny-lib ==0.20.*+    , multiarg ==0.18.*+    , penny-lib ==0.22.*     , pretty-show ==1.5.*     , text ==0.11.* @@ -251,9 +99,9 @@ Executable penny-reconcile   Build-depends:       base ==4.*-    , penny-lib ==0.20.*+    , penny-lib ==0.22.*     , text ==0.11.*-    , multiarg ==0.16.*+    , multiarg ==0.18.*     , explicit-exception ==0.1.*    main-is: penny-reconcile.hs
penny-diff.hs view
@@ -1,7 +1,6 @@ module Main where  import Control.Arrow (first, second)-import Data.Either (partitionEithers) import Data.Maybe (fromJust) import Data.List (deleteFirstsBy) import qualified System.Console.MultiArg as M@@ -19,11 +18,8 @@  import qualified Paths_penny_bin as PPB -groupingSpecs :: CR.GroupSpecs-groupingSpecs = CR.GroupSpecs CR.NoGrouping CR.NoGrouping- main :: IO ()-main = runPennyDiff groupingSpecs+main = runPennyDiff  help :: String -> String help pn = unlines@@ -49,11 +45,8 @@ optFile2 :: M.OptSpec Args optFile2 = M.OptSpec [] "2" (M.NoArg (ArgFile File2)) -allOpts :: [M.OptSpec (Either (IO ()) Args)]-allOpts = [ fmap Right optFile1-          , fmap Right optFile2-          , fmap Left (Ly.version PPB.version)-          ]+allOpts :: [M.OptSpec Args]+allOpts = [ optFile1 , optFile2 ]  data File = File1 | File2   deriving (Eq, Show)@@ -94,11 +87,10 @@ -- the line number for the top line for that; otherwise, shows the -- line number for the TopLine. renderTransaction-  :: CR.GroupSpecs-  -> File+  :: File   -> L.Transaction   -> Maybe X.Text-renderTransaction gs f t = fmap addHeader $ CR.transaction gs (noMeta t)+renderTransaction f t = fmap addHeader $ CR.transaction Nothing (noMeta t)   where     lin = case L.tMemo . L.tlCore . fst . L.unTransaction $ t of       Nothing -> L.unTopLineLine . L.tTopLineLine . fromJust@@ -109,56 +101,60 @@     noMeta txn = let (tl, es) = L.unTransaction txn                  in (L.tlCore tl, fmap L.pdCore es) -renderPrice :: CR.GroupSpecs -> File -> L.PricePoint -> Maybe X.Text-renderPrice gs f p = fmap addHeader $ CR.price gs p+renderPrice :: File -> L.PricePoint -> Maybe X.Text+renderPrice f p = fmap addHeader $ CR.price p   where     lin = L.unPriceLine . fromJust . L.priceLine $ p     addHeader x = (showLineNum f lin) `X.append` x -renderNonBlankItem :: CR.GroupSpecs -> File -> NonBlankItem -> Maybe X.Text-renderNonBlankItem gs f =-  S.caseS3 (renderTransaction gs f) (renderPrice gs f) CR.comment+renderNonBlankItem+  :: File+  -> NonBlankItem+  -> Maybe X.Text+renderNonBlankItem f =+  S.caseS3 (renderTransaction f) (renderPrice f) CR.comment -runPennyDiff :: CR.GroupSpecs -> IO ()-runPennyDiff co = do+runPennyDiff :: IO ()+runPennyDiff = do   (f1, f2, dts) <- parseCommandLine   l1 <- C.open [f1]   l2 <- C.open [f2]   let (r1, r2) = doDiffs l1 l2-  showDiffs co dts (r1, r2)+  showDiffs dts (r1, r2)   case (r1, r2) of     ([], []) -> E.exitSuccess     _ -> E.exitWith (E.ExitFailure 1)  showDiffs-  :: CR.GroupSpecs-  -> DiffsToShow+  :: DiffsToShow   -> ([NonBlankItem], [NonBlankItem])   -> IO ()-showDiffs co dts (l1, l2) =+showDiffs dts (l1, l2) =   case dts of     File1Only -> showFile1     File2Only -> showFile2     BothFiles -> showFile1 >> showFile2   where-    showFile1 = showNonBlankItems co File1 l1-    showFile2 = showNonBlankItems co File2 l2+    showFile1 = showNonBlankItems File1 l1+    showFile2 = showNonBlankItems File2 l2  failure :: String -> IO a failure s = IO.hPutStrLn IO.stderr s   >> E.exitWith (E.ExitFailure 2)  showNonBlankItems-  :: CR.GroupSpecs-  -> File+  :: File   -> [NonBlankItem]   -> IO ()-showNonBlankItems o f ls =-  mapM_ (showNonBlankItem o f) ls+showNonBlankItems f ls =+  mapM_ (showNonBlankItem f) ls -showNonBlankItem :: CR.GroupSpecs -> File -> NonBlankItem -> IO ()-showNonBlankItem co f nbi = maybe e TIO.putStr-  (renderNonBlankItem co f nbi)+showNonBlankItem+  :: File+  -> NonBlankItem+  -> IO ()+showNonBlankItem f nbi = maybe e TIO.putStr+  (renderNonBlankItem f nbi)   where     e = failure $ "could not render item: " ++ show nbi @@ -181,22 +177,19 @@ -- an indication of which differences to show. parseCommandLine :: IO (String, String, DiffsToShow) parseCommandLine = do-  as <- M.simpleWithHelp help M.Intersperse allOpts-        (return . Right . Filename)-  let (doVer, args) = partitionEithers as-      toFilename a = case a of+  as <- M.simpleHelpVersion help (Ly.version PPB.version)+        allOpts M.Intersperse+        (return . Filename)+  let toFilename a = case a of         Filename s -> Just s         _ -> Nothing-  case doVer of-    [] -> return ()-    x:_ -> x-  (fn1, fn2) <- case mapMaybe toFilename args of+  (fn1, fn2) <- case mapMaybe toFilename as of     x:y:[] -> return (x, y)     _ -> failure "penny-diff: error: you must supply two filenames."   let getDiffs-        | ((ArgFile File1) `elem` args)-          && ((ArgFile File2) `elem` args) = BothFiles-        | ((ArgFile File1) `elem` args) = File1Only-        | ((ArgFile File2) `elem` args) = File2Only+        | ((ArgFile File1) `elem` as)+          && ((ArgFile File2) `elem` as) = BothFiles+        | ((ArgFile File1) `elem` as) = File1Only+        | ((ArgFile File2) `elem` as) = File2Only         | otherwise = BothFiles   return (fn1, fn2, getDiffs)
penny-main.hs view
@@ -46,10 +46,10 @@     -- If this list is empty, then by default postings are left in the     -- same order as they appear in the ledger files. -  , balanceFormat = const defaultQtyFormat-    -- ^ How to format balances in the balance report. Change this-    -- function if, for example, you want to allow for digit-    -- grouping. The default function does not perform digit grouping.+  , formatQty = qtyFormatter $ S3a Period+    -- ^ How to format quantities. This affects only quantities that+    -- are not parsed from the ledger.  Examples include calculated+    -- totals and inferred quantities.  Affects all reports.    , balanceShowZeroBalances = False     -- ^ Show zero balances in the balance report? If True, show them;@@ -86,11 +86,6 @@     -- SortByQty     -- SortByName -  , convertFormat = const defaultQtyFormat-    -- ^ How to format balances in the convert report. For instance,-    -- this function might perform digit grouping. The default-    -- function does not perform any digit grouping.-   , postingsFields = fields     -- ^ Fields to show by default in the postings report. @@ -105,16 +100,6 @@    , postingsDateFormat = yearMonthDay     -- ^ How to format dates in the postings report.--  , postingsQtyFormat = qtyAsIs-    -- ^ How to format quantities in the balance report. This function-    -- is used when showing the quantity for the posting itself, and-    -- not the quantity for the totals columns (for that, see-    -- postingsBalanceFormat.) For example this function might perform-    -- digit grouping.--  , postingsBalanceFormat = balanceAsIs-    -- ^ How to format balance totals in the postings report.    , postingsSubAccountLength = 2     -- ^ Account names in the postings report are shortened if
penny-reconcile.hs view
@@ -1,5 +1,6 @@ module Main where +import Data.Either (partitionEithers) import Data.Maybe (fromMaybe, fromJust) import qualified Data.Text as X import Control.Monad (guard)@@ -56,27 +57,20 @@   , "  --version  - Show version and exit"   ] -groupSpecs :: C.GroupSpecs-groupSpecs = C.GroupSpecs C.NoGrouping C.NoGrouping--type ShowVer = IO () type Printer = X.Text -> IO () type PosArg = String-type Arg = S.S3 ShowVer Printer PosArg+type Arg = Either Printer PosArg  allOpts :: [MA.OptSpec Arg]-allOpts =-  [ fmap S.S3a $ Ly.version PPB.version-  , fmap S.S3b Ly.output-  ]+allOpts = [ fmap Left Ly.output ]  main :: IO () main = do-  as <- MA.simpleWithHelp help MA.Intersperse allOpts (fmap return S.S3c)-  let (showVer, printers, posArgs) = S.partitionS3 as-  sequence_ showVer+  as <- MA.simpleHelpVersion help (Ly.version PPB.version)+        allOpts MA.Intersperse (return . Right)+  let (printers, posArgs) = partitionEithers as   led <- C.open posArgs   let led' = map (S.mapS4 changeTransaction id id id) led-      rend = fromJust $ mapM (C.item groupSpecs) (map C.stripMeta led')+      rend = fromJust $ mapM (C.item Nothing) (map C.stripMeta led')   let txt = X.concat rend in txt `seq` (Ly.processOutput printers txt) 
penny-reprint.hs view
@@ -1,11 +1,11 @@ module Main where +import Data.Either (partitionEithers) import qualified Penny.Copper as C import qualified Penny.Copper.Render as R import qualified Penny.Liberty as Ly import qualified Data.Text as X import qualified System.Console.MultiArg as MA-import qualified Penny.Steel.Sums as S  import qualified Paths_penny_bin as PPB @@ -30,28 +30,22 @@   , "  --version  - show version and exit"   ] -groupSpecs :: R.GroupSpecs-groupSpecs = R.GroupSpecs R.NoGrouping R.NoGrouping--type MaybeShowVer = IO () type Printer = X.Text -> IO () type PosArg = String -type Arg = S.S3 MaybeShowVer Printer PosArg+type Arg = Either Printer PosArg  allOpts :: [MA.OptSpec Arg]-allOpts =-  [ fmap S.S3a $ Ly.version PPB.version-  , fmap S.S3b $ Ly.output-  ]+allOpts = [ fmap Left Ly.output ]  main :: IO () main = do-  as <- MA.simpleWithHelp help MA.Intersperse allOpts (fmap return S.S3c)-  let (showVers, printers, posArgs) = S.partitionS3 as-  sequence_ showVers+  as <- MA.simpleHelpVersion help (Ly.version PPB.version)+        allOpts MA.Intersperse+        (return . Right)+  let (printers, posArgs) = partitionEithers as   l <- C.open posArgs-  case mapM (R.item groupSpecs) (map C.stripMeta l) of+  case mapM (R.item Nothing) (map C.stripMeta l) of     Nothing -> error "could not render final ledger."     Just x ->       let txt = X.concat x
penny-selloff.hs view
@@ -117,9 +117,7 @@ import qualified System.Console.MultiArg as MA import qualified Text.Parsec as Parsec import qualified Paths_penny_bin as PPB--groupingSpec :: CR.GroupSpecs-groupingSpec = CR.GroupSpecs CR.NoGrouping CR.NoGrouping+import qualified Penny as P  type Err = Ex.Exceptional Error @@ -146,24 +144,13 @@ newtype InputFilename = InputFilename { _unInputFilename :: String }   deriving (Eq, Show) -type MaybeShowVer = IO ()-type Opts = (MaybeShowVer, [String])--posArg :: String -> Opts -> Opts-posArg s (a, ss) = (a, s:ss)--allOpts :: [MA.OptSpec (Opts -> Opts)]-allOpts = [ fmap (\a (_, ss) -> (a, ss)) $ Ly.version PPB.version ]- data ParseResult = ParseResult ProceedsAcct [Cop.LedgerItem]  parseCommandLine :: IO ParseResult parseCommandLine = do-  as <- MA.simpleWithHelp help MA.Intersperse allOpts-        (fmap return posArg)-  let opts = foldr ($) (return (), []) as-  fst opts-  x:xs <- case snd opts of+  as <- MA.simpleHelpVersion help (Ly.version PPB.version)+        [] MA.Intersperse return+  x:xs <- case as of     [] -> fail (show NoInputArgs)     r -> return r   a <- Ex.switch (fail . show . ProceedsParseFailed) return@@ -197,11 +184,11 @@ newtype SaleDate = SaleDate { unSaleDate :: L.DateTime }   deriving (Show, Eq) -newtype SelloffStock = SelloffStock { unSelloffStock :: L.Amount }+newtype SelloffStock = SelloffStock { unSelloffStock :: L.Amount L.Qty }   deriving (Show, Eq)  newtype SelloffCurrency-  = SelloffCurrency { unSelloffCurrency :: L.Amount }+  = SelloffCurrency { unSelloffCurrency :: L.Amount L.Qty }   deriving (Show, Eq)  data SelloffInfo = SelloffInfo@@ -481,7 +468,7 @@   :: SelloffInfo   -> PurchaseDate   -> BasisRealiztn-  -> ((L.Entry, L.PostingData), (L.Entry, L.PostingData))+  -> ((L.Entry L.Qty, L.PostingData), (L.Entry L.Qty, L.PostingData)) basisOffsets s pd p = (po enDr, po enCr)   where     ac = L.Account [basis, grp, dt]@@ -538,7 +525,7 @@   :: GainOrLoss   -> SelloffCurrency   -> CapitalChange-  -> L.Entry+  -> L.Entry L.Qty capChangeEntry gl sc cc = L.Entry dc (L.Amount qt cy)   where     dc = case gl of@@ -552,7 +539,7 @@   -> GainOrLoss   -> CapitalChange   -> PurchaseInfo-  -> (L.Entry, L.PostingData)+  -> (L.Entry L.Qty, L.PostingData) capChangePstg si gl cc p = (en, emptyPostingData ac)   where     ac = capChangeAcct gl si p@@ -563,7 +550,7 @@  proceedsPstgs   :: SelloffInfo-  -> ((L.Entry, L.PostingData), (L.Entry, L.PostingData))+  -> ((L.Entry L.Qty, L.PostingData), (L.Entry L.Qty, L.PostingData)) proceedsPstgs si = (po dr, po cr)   where     po en = (en, emptyPostingData ac)@@ -597,7 +584,7 @@             where               (b1, b2) = basisOffsets si (piDate p) br               c = capChangePstg si gl cc p-    entInputs = map (first Just) (p1:p2:ps)+    entInputs = map (first (Just . Right)) (p1:p2:ps)  makeOutput   :: ProceedsAcct@@ -605,6 +592,7 @@   -> Err X.Text makeOutput pa ldgr = do   let bals = calcBalances ldgr+      formatter = P.getQtyFormat defaultRadGroup ldgr   si <- selloffInfo pa bals   let basisAccts = findBasisAccounts (siGroup si) bals   purchInfos <- mapM (purchaseInfo (siStock si) (siCurrency si))@@ -614,7 +602,7 @@   return     . (`X.snoc` '\n')     . fromMaybe (error "makeOutput: transaction did not render")-    . CR.transaction groupingSpec+    . (CR.transaction (Just formatter))     . (\t -> let (tl, es) = L.unTransaction t              in (L.tlCore tl, fmap L.pdCore es))     . mkTxn si@@ -628,3 +616,6 @@ handleParseResult :: ParseResult -> IO () handleParseResult (ParseResult pa ldgr) =   Ex.switch (error . show) TIO.putStr . makeOutput pa $ ldgr++defaultRadGroup :: S.S3 L.Radix L.PeriodGrp L.CommaGrp+defaultRadGroup = S.S3a L.Period