packages feed

penny-bin 0.4.0.0 → 0.6.0.0

raw patch · 30 files changed

+4683/−928 lines, 30 filesdep +containersdep +explicit-exceptiondep +multiargdep ~penny-libnew-component:exe:penny-diffnew-component:exe:penny-reconcilenew-component:exe:penny-reprintnew-component:exe:penny-selloff

Dependencies added: containers, explicit-exception, multiarg, parsec, pretty-show, semigroups, text, transformers

Dependency ranges changed: penny-lib

Files

LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2011-2012 Omari Norman.+Copyright (c) 2011-2013 Omari Norman. All rights reserved.  Redistribution and use in source and binary forms, with or without@@ -8,14 +8,15 @@     * Redistributions of source code must retain the above copyright     notice, this list of conditions and the following disclaimer. -    * Redistributions in binary form must reproduce the above copyright-    notice, this list of conditions and the following disclaimer in-    the documentation and/or other materials provided with the-    distribution.+    * Redistributions in binary form must reproduce the above+    copyright notice, this list of conditions and the following+    disclaimer in the documentation and/or other materials provided+    with the distribution. -    * Neither the name of Omari Norman nor the names of its-    contributors may be used to endorse or promote products derived-    from this software without specific prior written permission.+    * Neither the name of Omari Norman nor the names of contributors+    to this software may be used to endorse or promote products+    derived from this software without specific prior written+    permission.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ README view
@@ -0,0 +1,17 @@+This is the package with executables and documentation for Penny. To+compile and install the binaries, running "cabal install" will work.++To install the manual pages and the documentation, run "sh+install-docs". It will install the manual pages to $PREFIX/share/man+and the other documentation to $PREFIX/share/doc/penny. By default+$PREFIX is /usr/local; you can change this by editing the+install-docs file and changing the "PREFIX" variable.++To remove the manual pages and the documentation, run "sh+install-docs remove."++The first thing you will want to look at is the manual page+penny-basics(7).  Then you will want to examine the starter.pny file+in the examples directory, which will show you how to write a ledger+file. penny-suite(7) will then direct you to other documentation that+may interest you.
+ doc/amex-file-format.org view
@@ -0,0 +1,66 @@+Amex CSV file format++Current as of December 2, 2012++This documents the Amex CSV that can be downloaded from the Amex+website. Obtain this by using the "Download" link that is available+when viewing account transactions. Be sure to select the checkbox for+"additional transaction details" or the like, as this format contains+the unique Amex ID numbers.++Newlines are with linefeeds only (UNIX style, not Windows style.)+Although newlines separate records, they also appear within some of+the fields, so you cannot split the file into records using newlines+alone (which makes it difficult to use line-oriented tools like awk.)++Here are the fields, in order. The fields are separated by commas. The+list starts at 1 (org-mode does not seem to like starting lists at 0.)++Many fields are unknown. These have always appeared blank in the data+I have.++1. Date. This is given as MM/DD/YYYY followed by a three-digit day of+   the week, such as "Wed".++2. Unknown++3. Description. This is the payee name (often truncated) followed by+   the city and state. This contains many spaces (presumably so it+   would line up in a table?) Appears wrapped in double quotes.++4. Unknown++5. Unknown++6. Unknown++7. Unknown++8. Amount. Charges appear simply with the amount; payments and credits+   appear with a prepended minus sign. This field is not wrapped in+   double quotes.++9. Unknown++10. Category. Appears to categorize the merchant (e.g. "Hardware+    Store") but the exact format is not immediately apparent;+    sometimes a number appears with the description, sometimes there+    are just numbers. This field is wrapped in double quotes.++11. Doing Business As. This seems to be the best place to pull the+    payee information from, as the Description field (number 3) mixes+    this up with other information. Double-quoted.++12. Address. Contains a street address with a city and state. Looks+    something like Street Address (newline) City (newline) State+    2-digit. Note the embedded newlines. Double-quoted.++13. Postcode. Postal code, then newline, then country. Note the+    embedded newline. Double-quoted.++14. Amex ID. A long integer. Appears within both double quotes and+    single quotes, e.g. "'320123300342679128'".++15. Unknown++16. Unknown
+ doc/amex-workflow.org view
@@ -0,0 +1,68 @@+Amex Workflow++Here is a proposed workflow for dealing with the download of Amex+transactions and the reconciliation of Amex statements.++* Import to database using amex-import++This will parse downloaded data from the Amex website and import them+to the database of all Amex transactions and will assign all+transactions a "U-number." The U-number is a unique number that+identifies an Amex transaction. U-numbers are used because, although+Amex assigns unique identifiers to transactions, they are extremely+long.++* Merge from database to ledger file using amex-merge++Checks to ensure that every transaction that is in the Amex database+is represented in the ledger. Follows these steps:++- Is the database transaction's U-number found in the numbers for the+  Amex transactions that are in the ledger? If so, stop.++- Otherwise, go through existing Amex ledger transactions that do not+  have a U-number and see if any of these match the database+  transaction. There is a match if all of the following are true:++  - The quantities match exactly;++  - The ledger transaction is a debit or a credit, as appropriate (if+    Amex shows an increase in the balance, then the ledger shows a+    credit; for payments, the ledger shows a debit.)++  - The date on the Amex transaction matches the local date on the+    ledger transaction.++- If no matches are found, create a transaction and append it to the+  end of the list of Ledger transactions. Assign the appropriate+  U-number. Prints new ledger file to stdout.++After running amex-merge, study the results. If necessary, assign the+U-numbers to already existing ledger transactions, or edit the newly+created transactions.++* Clear using amex-clear++This command will also work with a list of downloaded transactions. It+will follow these steps:++- Make sure that all the downloaded transactions are in the+  database. If not, quit with an error.++- Make sure that all the downloaded transaction U-numbers are also in+  the list of ledger transactions. If not, quit with an error.++- Make sure that each ledger posting corresponding to the downloaded+  transaction has no flag for the posting itself (a flag for the+  transaction is OK.) It one has a flag, quit with an error.++- Assign the flag "C" to each transaction. Prints new ledger file to stdout.++After running amex-clear, make sure that the balance of all cleared+and reconciled ledger Amex postings is the same as the Amex statement+balance.++* Reconcile using amex-reconcile++This command simply changes the "C" flags in the Amex ledger account+to "R" flags.
+ doc/bofa-file-format.org view
@@ -0,0 +1,159 @@+Bank of America download file format++This is the format for the "WEB Connect for Quicken 2010 and above"+option that is on the Bank of America download format. Ostensibly this+is OFX in SGML, but I don't know if this is actually true as I have+not yet found much about what "WEB Connect" is. A simple CSV would be+preferable; however, B of A does not make unique transaction IDs+available in CSV format. (QIF does not have this information, neither+does the "Microsoft Excel format" on the website, which appears to+just be a CSV.)++The file has DOS line endings (\r\n).++Unlike XML, tags are not always closed. The tag names are always in+all caps. It does appear that tags that open alone on a line are+balanced, while tags that are followed on the same line with data are+not balanced.++* Header++There is a header portion with nine lines. Each has the field name,+followed by a colon, and then some information (no space between the+colon and the data). They have field names as follows:++- OFXHEADER+- DATA+- VERSION+- SECURITY+- ENCODING+- CHARSET+- COMPRESSION+- OLDFILEUID+- NEWFILEUID++Then there is a blank line.++* OFX tag+This tag is balanced. It encloses all the data. All other tags are+within this tag.++* SIGNONMSGSRSV1+Sign on message? Balanced. Encloses some tags:++** SONRS+Balanced.++*** STATUS+Balanced.++**** CODE+Not balanced. There is a digit on the same line.++**** SEVERITY+Not balanced. Says "INFO" on the same line.++*** DTSERVER+Not balanced. Has some sort of date-time information?++*** LANGUAGE+Not balanced. Says ENG.++*** DTACCTUP+Not balanced. Has some date-time information. Looks like same+information in DTSERVER.++*** FI+Balanced. Financial institution?++**** ORG+Not balanced. Bank of America.++**** FID+Not balanced. Says 5959.++*** INTU.BID+Not balanced. Four-digit number.++*** INTU.USERID+Not balanced. Has your Bank of America user ID.++* BANKMSGSRSV1+Balanced.++** STMTTRNRS+Balanced.++*** TRNUID+Not balanced. "0"++*** STATUS+Balanced.++**** CODE+Not balanced. "0"++**** SEVERITY+Not balanced. "INFO"++*** STMTRS+Balanced.++**** CURDEF+Not balanced. "USD"++**** BANKACCTFROM+Balanced.++***** BANKID+Not Balanced. Digits.++***** ACCTID+Not balanced. Digits.++***** ACCTTYPE+Not balanced. Checking++**** BANKTRANLIST+Balanced.++***** DTSTART+Not balanced. Digits. Some sort of date-time.++***** DTEND+Not balanced. Digits. Some sort of date-time.++***** STMTTRN+Balanced. Where interesting stuff starts.++****** TRNTYPE+Not balanced. Transaction Type? Mostly CREDIT or DEBIT in the data I+have, though it might also be ATM or who knows what else.++****** DTPOSTED+Not balanced. A date-time. I am guessing the format for a date-time is+YYYYMMDDHHMMSS. B of A is leaving the MMSS as zeroes.++****** TRNAMT+Not balanced. Digits with a decimal point. CREDIT transactions have no+minus sign; DEBIT transactions have a leading minus sign.++****** FITID+Not balanced. Financial Institution ID? Digits with some points and+maybe a minus.++****** NAME+Not balanced. The payee name.++****** CHECKNUM+Appears on checks. Not balanced.++**** LEDGERBAL+Balanced.++***** BALAMT+Not balanced. An amount.++***** DTASOF+Not balanced. Date as of? A date-time. Unlike the others, this+date-time has minutes and seconds.
+ doc/dependencies.dot view
@@ -0,0 +1,23 @@+/* This file shows the dependencies between the different main modules+of Penny.++To render, run+dot -Tps dependencies.dot -o dependencies.ps+*/+digraph G {+        Brenner -> Lincoln;+        Brenner -> Copper;+        Cabin -> Lincoln;+        Cabin -> Liberty;+        Cabin -> Shield;+        Copper -> Lincoln;+        Liberty -> Lincoln;+        Liberty -> Copper;+        Shield -> Lincoln;+        Zinc -> Cabin;+        Zinc -> Copper;+        Zinc -> Liberty;+        Zinc -> Lincoln;+        Zinc -> Shield;+}+
+ doc/ledger-grammar.org view
@@ -0,0 +1,289 @@+* Terminals++AllPoints = { c | c >= U+0000, c <= U+10ffff }+Invalid = { c | c >= U+D800, c <= U+DFFF }+Unicode = { x ∈ AllPoints | x ∉ Invalid }+Newline = { U+000A }+Space = { U+0020 }+Tab = { U+0009 }+White = { Space Tab }+NonNewline = { x ∈ Unicode | x ∉ Newline }+NonNewlineNonSpace = { x ∈ NonNewline | x ∉ White }+UpperCaseAscii = { x | x >= 'A', x <= 'Z' }+LowerCaseAscii = { x | x >= 'a', x <= 'z' }+Digit = { x | x >= '0', x <= '9' }+NonAscii = { x ∈ NonNewline | x > U+007f }+Letter = UpperCaseAscii ∪ LowerCaseAscii ∪ NonAscii+Dollar = { '$' }+Colon = { ':' }+OpenCurly = { '{' }+CloseCurly = { '}' }+OpenSquare = { '[' }+CloseSquare = { ']' }+DoubleQuote = { '"' }+Period = { '.' }+Hash = { '#' }+ThinSpace = { U+2009 }+DateSep = { '/', '-' }+Plus = { '+' }+Minus = { '-' }+LessThan = { '<' }+GreaterThan = { '>' }+OpenParen = { '(' }+CloseParen = { ')' }+Semicolon = { ';' }+Apostrophe = { U+0027 }+Tilde = { '~' }+Underscore = { '_' }+Asterisk = { '*' }+AtSign = { '@' }++* Accounts++** Level 1 Accounts++Level 1 Accounts can have nearly any character, including+spaces. However, when they appear in a ledger file, they must be+quoted. There is no need to quote them when they appear on the command+line, as each command-line argument is already isolated.++Lvl1AcctChar = { x ∈ NonNewline | x ∉ CloseCurly, x ∉ Colon }+<lvl1SubAcct> ::= Lvl1AcctChar++<lvl1FirstSubAcct> ::= <lvl1SubAcct>+<lvl1OtherSubAcct> ::= Colon <lvl1SubAcct>+<lvl1Acct> ::= <lvl1FirstSubAcct> <lvl1OtherSubAcct>*+<quotedLvl1Acct> ::= OpenCurly <lvl1Acct> CloseCurly++** Level 2 Accounts++Level 2 Accounts contain a more restrictive range of characters than+Level 1 Accounts. The first sub-account must begin with a letter or+with any non-ASCII value. All other characters may be nearly any+character except a space or asterisk. Level 2 Accounts are not quoted.++Lvl2AcctOtherChar = { x ∈ NonNewline | x ∉ White, x ∉ Colon, x ∉ Asterisk,+                                       x ∉ GreaterThan, X ∉ LessThan }+<lvl2FirstSubAcct> ::= Letter Lvl2AcctOtherChar*+<lvl2OtherSubAcct> ::= Colon Lvl2AcctOtherChar++<lvl2Acct> ::= <lvl2FirstSubAcct> <lvl2OtherSubAcct>*++** Ledger file accounts++When an account appears in a ledger file it can be a quoted Level 1+Account or a Level 2 Account.++<ledgerAcct> ::= <quotedLvl1Acct> | <lvl2Acct>++* Commodities++** Level 1 Commodities++Level 1 Commodities can have a broad selection of characters,+including spaces; but they must be quoted when they appear in a ledger+file. They are not quoted when they appear on a command line.++Lvl1CmdtyChar = { x ∈ NonNewline | x ∉ DoubleQuote }+<lvl1Cmdty> ::= Lvl1CmdtyChar++<quotedLvl1Cmdty> ::= DoubleQuote <lvl1Cmdty> DoubleQuote++** Level 2 Commodities++Level 2 Commodities must begin with a letter, a dollar sign, or a+non-ASCII character. They are not quoted. All other characters may be+any non-space character.++Lvl2CmdtyFirstChar = Letter ∪ Dollar+Lvl2CmdtyOtherChar = { x ∈ NonNewline | x ∉ White }+<lvl2Cmdty> ::= Lvl2CmdtyFirstChar Lvl2CmdtyOtherChar*++** Level 3 Commodities++In Level 3 Commodities, all characters must be letters or the $+symbol. They are not quoted.++Lvl3CmdtyChar = Letter ∪ Dollar+<lvl3Cmdty> ::= Lvl3CmdtyChar+++* Quantities++<digitGroup> ::= ThinSpace Digit++<digitSequence> ::= Digit+ <digitGroup>*+<digitPostSequence> ::= Period <digitSequence>?+<quantity> ::= Period <digitSequence>+               | <digitSequence> <digitPostSequence>?++* Amounts++An amount is a commodity and a quantity.++<leftCmdtyLvl1Amt> ::= <quotedLvl1Cmdty> White* <quantity>+<leftCmdtyLvl3Amt> ::= <lvl3Cmdty> White* <quantity>+<leftSideCmdtyAmt> ::= <leftCmdtyLvl1Amt> | <leftCmdtyLvl3Amt>+<rightSideCmdty> ::= <quotedLvl1Cmdty> | <lvl2Cmdty>+<rightSideCmdtyAmt> ::= <quantity> White* <rightSideCmdty>+<amount> ::= <leftSideCmdtyAmt> | <rightSideCmdtyAmt>++* Comments++Comments are not associated with any transaction or price; they stand+alone.++<comment> ::= Hash NonNewline* Newline White*++* DateTime++The grammar does not restrict texts to valid dates or times; a later+phase must validate the dates and times.++<year> ::= Digit Digit Digit Digit+<month> ::= Digit Digit+<day> ::= Digit Digit+<date> ::= <year> DateSep <month> DateSep <day>++<hours> ::= Digit Digit?+<minutes> ::= Colon Digit Digit+<seconds> ::= Colon Digit Digit++<time> ::= <hours> <minutes> <seconds>?++<tzSign> ::= Plus | Minus+<tzNumber> ::= Digit Digit Digit Digit+<timeZone> ::= <tzSign> <tzNumber>++<timeWithZone> ::= <time> White* <timeZone>?+<dateTime> ::= <date> White* <timeWithZone>?++* Entries++An entry is a debit or credit and an amount.++<debit> ::= LessThan+<credit> ::= GreaterThan+<drCr> ::= <debit> | <credit>+<entry> ::= <drCr> White* <amount>++* Flag++Flags are surrounded by square braces.++FlagChar = { x ∈ NonNewline | x ∉ CloseSquare }++<flag> ::= OpenSquare FlagChar* CloseSquare++* Memos++Memos are associated with a particular transaction or posting.++** Posting memo++<postingMemoLine> ::= Apostrophe NonNewline* Newline White*+<postingMemo> ::= <postingMemoLine>+++** Transaction memo++<transactionMemoLine> ::= Semicolon NonNewline* Newline White*+<transactionMemo> ::= <transactionMemoLine>+++* Number++Numbers are surrounded by parentheses.++NumberChar = { x ∈ NonNewline | x ∉ CloseParen }++<number> ::= OpenParen NumberChar* CloseParen++* Payees++** Level 1 payees++These allow the most latitide in the range of characters, but they+must be quoted with tildes if in a ledger file.++QuotedPayeeChar = { x ∈ NonNewline | x ∉ Tilde }++<lvl1Payee> ::= QuotedPayeeChar*+<quotedLvl1Payee> ::= Tilde <lvl1Payee> Tilde++** Level 2 payees++Their first character must be a letter.++<lvl2Payee> ::= Letter NonNewline*++* Prices++A price has a DateTime, a From commodity, and a To amount.++<fromCmdty> ::= <quotedLvl1Cmdty> | <lvl2Cmdty>+<price> ::= AtSign White* <dateTime> White*+            <fromCmdty> White+ <amount> Newline White*++* Tags++Each tag is preceded by an asterisk. The tag text consists of letters,+digits, and underscores.++TagChar = { x ∈ NonNewlineNonSpace | x ∉ Asterisk, x ∉ GreaterThan,+                                     x ∉ LessThan }+<tag> ::= Asterisk TagChar* White*+<tags> ::= <tag>+++* Top Line++This is the first line of a transaction. It also includes the+transaction memo.++<topLinePayee> ::= <quotedLvl1Payee> | <lvl2Payee>+<topLineFlagNum> ::= <flag>? White* <number>?+                     | <number>? White* <flag>?+<topLine> ::= <transactionMemo>? <dateTime> White*+              <topLineFlagNum> White* <topLinePayee>? Newline White*++* FlagNumberPayee++<numberPayee> ::= <number> White* <quotedLvl1Payee>?+<payeeNumber> ::= <quotedLvl1Payee> White* <number>?+<afterFlag> ::= <numberPayee> | <payeeNumber>+<flagFirst> ::= <flag> White* <afterFlag>?++<flagPayee> ::= <flag> White* <quotedLvl1Payee>?+<payeeFlag> ::= <quotedLvl1Payee> White* <flag>?+<afterNumber> ::= <flagPayee> | <payeeFlag>+<numberFirst> ::= <number> White* <afterNumber>?++<numberFlag> ::= <number> White* <flag>?+<flagNumber> ::= <flag> White* <number>?+<afterPayee> ::= <numberFlag> <flagNumber>+<payeeFirst> ::= <quotedLvl1Payee> White* <afterPayee>?++<flagNumPayee> ::= <flagFirst> | <numberFirst> | <payeeFirst>++* Posting++<postingAcct> ::= <quotedLvl1Acct> | <lvl2Acct>++<posting>+  ::= <flagNumPayee>? White* <postingAcct> White*+      <tags>? White* <entry>? White* Newline White* <postingMemo>?+      White*++* Transaction++A transaction always has at least two postings. In addition, the+postings must be balanced; however, this restriction is not in the+grammar. As a corollary, at most one posting can appear without an+entry; this restriction also is not in the grammar.++<transaction> ::= <topLine> <posting> <posting> <posting>*++* Blank line++<blankLine> ::= Newline White*++* Item++<item> ::= <comment> | <price> | <transaction> | <blankLine>++* Ledger++<ledger> ::= White* <item>*
+ examples/more-file-format-details.pny view
@@ -0,0 +1,108 @@+# This file contains more details on the Penny file format. Read it if+# you are interested, or if you are getting error messages you don't+# understand when you use penny on your file.++# A complete EBNF grammar for the file format is in the file+# doc/ledger-file-format.org.++# Use a Unicode file format when you save your data. Penny works with+# UTF-8, which is the de-facto standard Unicode encoding on Unix+# systems. If you do not want to bother with Unicode, it's best to use+# just the ASCII subset of Unicode. Using a non-Unicode encoding might+# work, but I have not tested that. Also, if you want to put digit+# grouping characters in your penny file, Unicode is your only option,+# as the Unicode thin space (U+2009) is the only digit grouping+# character you can use.++# If you have no idea what all this encoding stuff is about, the first+# thing to remember is that there is no such thing as plain text. Read+# this:++# http://www.joelonsoftware.com/articles/Unicode.html++# Since you can use Unicode, feel free to use whatever currency+# symbols you want:++2012-12-20 Euroworks+    Income:Salary                                       > 2100 €+    Assets:Bank++# More detail on dates:++# Dates are written in the format YYYY-MM-DD. If the month or the day+# has only one digit, you must use leading zeroes. In addition, dates+# can include a time. Times are written in 24-hour format, either as+# HH:MM or as HH:MM:SS; again, if any component has only one digit,+# you must use a leading zero.++# You can use either a dash or a slash to separate YYYY and the MM and+# the DD.++# All dates and times are considered to be UTC. If you provide a time,+# you may optionall specify the time zone offset by using a leading+# plus or a leading minus.++# Here are some sample postings with various dates and times.++2012-12-17 02:23 Transfer from savings+    Assets:Savings                                      > $200.00+    Assets:Checking                                     < $200.00++2012-12-17 12:23:35 Transfer from savings+    Assets:Savings                                      > $200.00+    Assets:Checking                                     < $200.00++# Eastern time+2012-12-17 14:56:00 -0500 Transfer from savings+    Assets:Savings                                      > $200.00+    Assets:Checking                                     < $200.00++# Quoting for various fields:++# Usually you can write information in your ledger file without using+# characters to indicate which field is which. However, sometimes if+# you want to use slightly more unusual characters, you have to+# surround that field with characters. The canonical guide to what you+# must do is in the bin/doc/ledger-grammar.org file. Here are the highlights:++# Accounts can be unquoted if the very first character of the account+# is a letter (capital or lower-case, A-Z or a-z) or any character+# above Unicode code point 127 (e.g. ñ, or é, or í, or...). In+# addition every other character may be nearly any character except a+# space or an asterisk. Otherwise, you must surround your account with+# curly braces. Example:++2012-12-18 CVS+    {Expenses:Personal Care}                            < $52.35+        'Earplugs+    Liabilities:Amex++# You can assign payees to individual postings as well as to the+# entire transaction. Payees assigned to individual postings must be+# surrounded with tildes. You do not usually have to quote payees that+# are assigned to the whole transaction; however, you do have to quote+# them if the first character is not a letter. Examples:++2012-12-18 ~7-Eleven~+    Expenses:Food                                       < $12.99+    Liabilities:Amex++2012-12-19 Spacely Sprockets+    Income:Salary                                       > $2000.00+    ~State of Maryland~ Expenses:Taxes:State            < $200.00+    ~Healthco~ Expenses:Insurance:Health                < $75.00+    Assets:Checking++# Whether or not you have to quote commodities is complicated. For+# full details, look at bin/doc/ledger-grammar.org. The full rule set+# depends on whether the commodity appears on the left side or the+# right side of the quantity. (Whether there is a space between the+# commodity and the quantity is not relevant.)++# However, here is a simple rule that is always true. If your+# commodity name consists ONLY of letters, the dollar sign, and+# non-ASCII characters, you never need to quote it. Since most stock+# ticker symbols are only letters, and because this means that you+# never have to quote lone commodity symbols like $, €, ¥, etc, you+# should not usually have to quote commodities.+
+ examples/starter.pny view
@@ -0,0 +1,271 @@+# This file contains some sample Penny data. It is used as a basis for+# the examples in the penny-getting-started manual page.++# This file, like all of Penny, assumes you have basic knowledge of+# double-entry accounting. See penny-basics(1).++# As you can see, comments begin with the # symbol. You can only have+# single-line comments; there are no multi-line comments (e.g. like+# <!-- --> in XML or /* */ in C.) Also, comments must be the only item+# on a line (you cannot, for example, have part of a posting on one+# line and then a comment at the end of the same line.)++# We will distinguish between *transactions* and *postings*. A posting+# must have, at a minimum, an account, an indication of whether it is+# a debit or a credit, a commodity, and a quantity. A transaction must+# have at least two postings. All the postings in a transaction must+# balance--that is, their debits and credits must add up to the same+# number.++# Some first examples:++2012-12-01 Opening balances+    Equity                                              > $1554.60+    Assets:Checking                                     < $1554.60++2012-12-01 Spacely Sprockets+    Income:Salary                                       > $3000.00+    Assets:Checking                                     < $3000.00++# As shown above, debits are indicated with < (a less-than sign, which+# looks like a left-pointing arrow) and credits with a > (which looks+# like a right-pointing arrow.) So, in the "Spacely Sprockets"+# transaction, you have debited your Assets:Checking account by $3000,+# and you have credited your Salary account by $3000.++# If you try to have a transaction which is NOT balanced--that is, the+# sum of the debits is not equal to the sum of the credits--then Penny+# will complain and quit. The following text is not a valid+# transaction so it is commented out:++# 2012-12-01 Unbalanced+#     Income:Salary                                     > $3000.00+#     Assets:Checking                                   > $3000.00++# There, you have two credits, but no debit.++# Penny has no concept of negative numbers. All numbers are+# positive. Furthermore, quantities cannot be zero--they must be+# greather than zero, even if infinitesimally so.++# Some more transactions to get us started:+2012-12-01 Landlord+    Expenses:Rent                                       < $700.00+    Assets:Checking                                     > $700.00++2012-12-02 Whole Foods+    Expenses:Food                                       < $43.10+    Assets:Checking                                     > $43.10++2012-12-02 Amazon.com+    Expenses:Books                                      < $23.99+    Liabilities:Amex                                    > $23.99++# You can choose not to indicate the debit/credit, commodity, and+# quantity for one of the postings. Penny will use the debit or+# credit, quantity, and commodity from the one that you fill in to+# "infer" what is is for the other posting.++2012-12-02 CVS+    Expenses:Drugs                                      < $12.14+    Assets:Checking++# Here, Penny infers that the Assets:Checking account is credited+# $12.14.++# The different parts of your Penny file--accounts, dates, and so+# on--are separated with spaces. If you have data that includes+# spaces--for instance, you have a space in an account name--then you+# have to surround it with special characters. This is called+# "quoting". This is also true if you want to include other special+# characters in a particular part of your file. For example, to quote+# an account that has spaces, use curly braces:++2012-12-03 CVS+    {Expenses:Personal Care}                            < $82.21+    Liabilities:Amex++# When you are writing a payee name in the first line of a transaction+# (next to the date), it's okay to have spaces.++# You can have more than two postings in a transaction. Your debits+# and credits still have to be equal:+2012-12-03 Safeway+    Expenses:Food                                       < $8.94+    {Expenses:Personal Care}                            < $20.00+    Liabilities:Amex                                    > $28.94++# If you have more than two postings, you can still leave one of them+# without a debit or credit, commodity, and quantity, and Penny will+# infer what it is.+2012-12-04 Home Depot+    Expenses:Hardware                                   < $50.00+    Expenses:Garden                                     < $9.97+    Liabilities:Amex++# Each account is broken into "sub-accounts", which are separated by+# colons. In all the examples we've seen so far, there are two+# sub-accounts, but you can have as many or as few (down to one)+# sub-accounts as you like.+2012-12-05 Whole Foods+    Expenses:Entertaining:Food                          < $67.75+    Assets:Checking++# Postings can have memos. The memo begins with an apostrophe.+2012-12-06 Eli Zabar+    Expenses:Food                                       < $23.04+        'Some expensive bread.+        'Next time try the one with the raisins.+    Liabilities:Amex++# In addition, transactions can have memos. Each line of these begins+# with a semicolon. They must appear immediately before a+# transaction. They are different from comments because a transaction+# memo is associated with its transaction, while a comment is+# associated with nothing.++; Tried the new Potbelly+2012-12-06 Potbelly+    Expenses:Food                                       < $9.83+    Liabilities:Amex++# Whitespace separates the different elements of a transaction and a+# posting. However Penny does not impose particular indentation on+# you. The indentation you see above is just a convention that I think+# looks readable, but you can do it a different way. Penny also does+# not take sides in the tabs vs spaces holy war.++2012-12-06 Duane Reade+Expenses:Magazines < $2.98+Liabilities:Amex++# You do however have to keep each posting on a separate line, and you+# have to keep all the lines of a transaction together without any+# blank lines in between.++# You can also choose how to write your commodities--that is, before+# or after the quantity, and with or without a space between the+# commodity and the quantity. Currently Penny remembers how you write+# your commodities, though what it remembers has effects in only a few+# places. In particular, it does not affect how your reports are formatted.++# Ok, so this following example does not really make sense with+# dollars, but it gives you the idea.++2012-12-06 Flo+    Expenses:Insurance:Car                              < 230.00 $+    Assets:Checking++# Transactions and postings can have a "flag". I use only+# single-character flags; however, you can make your flags as long as+# you like. A flag is enclosed in square brackets. You might use these+# to indicate when a transaction has cleared your bank, or to remember+# what kind of a transaction it is.++# This flag applies to the entire transaction.+2012-12-06 [ATM] Cash+    Assets:Checking                                     > $100.00+    Expenses:Cash++# This flag applies just to the posting it is next to.+2012-12-10 Cash+    Expenses:Cash                                       < $100.00+    [ATM] Assets:Checking++# Similarly, transactions and postings can have a "number". You might+# use this for check numbers. This number applies to the whole+# transaction...++2012-12-11 (804) Comcast+    Expenses:Cable                                      < $123.99+    Assets:Checking++# ... and this one just to the posting.++2012-12-12 Washington Gas+    Expenses:Utilities:Gas                              < $23.14+    (805) Assets:Checking++# The payee on the first line of the posting is optional. The only+# thing required on the first line is the date. You can also put a+# payee on the individual postings, but they must be surrounded with+# tildes.++2012-12-12+    ~Zork enterprises~ (203) [R] Expenses:Gifts          < $16.99+    [R] ~Barristers Inc.~ (204) Expenses:Gifts           < $42.99+    Assets:Checking++# As the above example shows, you can have a payee, number, and flag+# on a single posting, or any combination of those. They can appear in+# any order. The account name always appears after these.++# A posting can have tags. This gives you another way (beyond+# accounts) to classify postings. For example, let's say you take a+# vacation. You spend money and put the expenses in different+# accounts, but you want to track the vacation expenses together. You+# precede each tag with an asterisk (you can have more than one tag on+# a posting) and the tags go after the account name. A tag can have+# nearly any character except for '<', '>', '*', or a space.++2012-12-13 Amtrak+    {Expenses:Train fare} *vacation2012                 < $700.00+    Liabilities:Amex++2012-12-14 Fitzpatrick Grand Central Hotel+    Expenses:Hotel *vacation2012                        < $400.00+    Liabilities:Amex++# You can use either dashes or slashes as the date separator. You can+# even mix them up :)++2012/12-15 Aquagrill+    Expenses:Food *vacation2012                         < $87.00+    Liabilities:Amex++# People in different countries use different characters for the radix+# point (decimal point) and to separate digits into groups. Penny+# imposes a single uniform way to represent the radix point and digit+# grouping. As you have seen above, use the full stop character+# (period) to represent the radix point.++# If you want to group digits, use the Unicode thin space+# character. This is Unicode code point 2009. Most editors will render+# it the same way they render a space, but it's not the same+# character. Different editors will have different ways to enter this+# character; in Emacs, press C-x 8 <ret> 2009. In Vim, in insert mode+# press Ctrl-V, then u2009. That looks like this:++2012-12-16 Import savings+    {Equity:Opening Balances}                           > $32 985.23+    Assets:Savings++# That's everything you need to get started recording your data with+# Penny. If you want to track multiple commodities (for instance+# stocks, or the price of your real estate) look at the stocks.pny+# file, which will help you get started there.++# Here are some more transactions that will provide useful fodder for+# the manual pages:++2013-01-02 Conoco+    Expenses:Gas                                        < $54.22+    Assets:Checking++2012-01-04 Express+    Expenses:Clothes                                    < $103.00+    Liabilities:Amex++2012-01-05 Amazon.com+    Expenses:Food                                       < $23.60+    Expenses:Books                                      < $16.00+    Liabilities:Amex++2012-01-06 Landlord+    Expenses:Rent                                       < $700.00+    Assets:Checking++2012-01-06 Spacely Sprockets+    Income:Salary                                       > $3000.00+    Assets:Checking+
+ examples/stocks-realized.pny view
@@ -0,0 +1,14 @@+; transaction created by penny-selloff for sale on 2012-12-31+2012-12-31 Realize gain or loss+    {Proceeds:LUV:2012-12-31}                           < $ 4800.00+    {Proceeds:LUV:2012-12-31}                           > LUV 300+    {Basis:LUV:2012-10-19}                              < LUV 100+    {Basis:LUV:2012-10-19}                              > $ 1000.00+    {Income:Capital Gain:LUV:2012-12-31:2012-10-19}     > $ 333.33+    {Basis:LUV:2012-11-19}                              < LUV 100+    {Basis:LUV:2012-11-19}                              > $ 1200.00+    {Income:Capital Gain:LUV:2012-12-31:2012-11-19}     > $ 400.00+    {Basis:LUV:2012-12-19}                              < LUV 100+    {Basis:LUV:2012-12-19}                              > $ 1400.00+    {Income:Capital Gain:LUV:2012-12-31:2012-12-19}     > $ 466.67+
+ examples/stocks.pny view
@@ -0,0 +1,246 @@+# This file documents some recommended practices for dealing+# with multi-commodity transactions.++# You might want to examine this tutorial that discusses+# multiple-commodity double-entry accounting generally; it is quite+# useful.+#+# http://www.mscs.dal.ca/~selinger/accounting/tutorial.html++# All the examples in the starter.pny file use dollars as the+# commodity. However, Penny is flexible: you can track any commodity+# you want. The simple rule is that all transactions must be+# balanced--that is, the sum of the debits must be equal to the sum of+# the credits. Each posting can have only one commodity. A debit sum+# of one commodity must be offset by a credit sum of the same+# commodity. So, let's say you buy 100 shares of F for $1 each. The+# following transaction is INVALID so it is commented out:++# 2012/12/19 Buy stock+#    Assets:Checking                         > $100.00+#    Assets:Brokerage                        < 100 F++# That transaction is not balanced. The $100 credit needs an+# offsetting $100 debit, and the debit of 100 F needs an offsetting+# credit of 100 F. Here's the method I recommend:++2012-10-19 Purchase stock+    Assets:Brokerage                                    < 100 F+    Assets:Checking                                     > $1000.00+    Basis:F:2012-10-19                                  > 100 F+    Basis:F:2012-10-19                                  < $1000.00++# Offsetting your debit and credit against a single Basis account will+# allow you to get more useful information out of your ledger file, as+# we will see later. By naming the Basis account as shown here (with+# the second sub-account being the commodity symbol, and the third+# sub-account being the date on which the commodity was purchased) the+# penny-selloff program will be able to help you out later when you+# sell the commodity.++# You will also want to enter a price into your file. The convert+# report will use this information. A price tells the convert report+# how to convert one commodity to another. Prices have no effect on+# any other Penny report. For instance this price expresses the price+# of the prevoius transaction:++@ 2012-10-19 F $10.00++# This means that one unit of F is equivalent to $10.00. Prices only+# work one way; that is, the price above does NOT tell Penny that+# $10.00 is equivalent to one unit of F. If you wanted to express+# that, include another price. It is only possible to express that ONE+# unit of some commodity is equal to some number of units of another+# commodity. For instance, this price would be invalid:+#+# @ 2012-10-19 100F $1000.00+++# Any transaction that has two commodities will have at least four+# postings. This is because all transasctions in Penny must always be+# balanced, and every debit balance must be offset with a credit+# balance of the exact same commodity.++# A month later I sell my stock. Let's say it has done very well.++2012-11-19 Sell stock+    Assets:Brokerage                                    > 100 F+    Assets:Checking                                     < $1400.00+    Basis:F:2012-10-19                                  < 100 F+    Basis:F:2012-10-19                                  > $1400.00++# And a corresponding price:++@ 2012-11-19 F $14.00++# At this point, Basis:F:2012-10-19 has a credit balance of+# $400.00. This is your capital gain from the sale. You probably want+# to close out the Basis:F:2012-10-19 account by transferring its+# balance to an income account. (You could even do this in the same+# transaction as the stock sale.)++2012-11-19 Capital Gain+    Basis:F:2012-10-19                                  < $400.00+    {Income:Capital Gain:F:2012-10-19}                  > $400.00++# That's one way to do it. It's simple enough when you just have+# one lot of stock. What if you have a bunch of different lots?+# This can happen with dividend reinvestment especially. Here's+# an example:++2012-10-19 Purchase stock+    Assets:Brokerage                                    < 100 LUV+    Assets:Checking                                     > $1000.00+    Basis:LUV:2012-10-19                                > 100 LUV+    Basis:LUV:2012-10-19                                < $1000.00++2012-11-19 Purchase stock+    Assets:Brokerage                                    < 100 LUV+    Assets:Checking                                     > $1200.00+    Basis:LUV:2012-11-19                                > 100 LUV+    Basis:LUV:2012-11-19                                < $1200.00++2012-12-19 Purchase stock+    Assets:Brokerage                                    < 100 LUV+    Assets:Checking                                     > $1400.00+    Basis:LUV:2012-12-19                                > 100 LUV+    Basis:LUV:2012-12-19                                < $1400.00+++# Okay, time to sell. Get rid of all the LUV.  To get the capital gain+# right, it must be distributed amongst all the LUV purchases. You+# could do this yourself but who wants to?  So enter a transaction+# like this first:++2012-12-31 Sell stock+    Assets:Brokerage                                    > 300 LUV+    Assets:Checking                                     < $4800.00+    Proceeds:LUV:2012-12-31                             < 300 LUV+    Proceeds:LUV:2012-12-31                             > $4800.00++# At this point your assets and brokerage accounts look good.  If you+# don't care about capital gains, you can just stop here. But if you+# want to close out the Basis accounts, you can use the penny-selloff+# program. It creates transactions for you, which you can append to+# your Penny file. You pass penny-selloff two arguments: first, the+# name of the Proceeds account, and second, the filename to draw from.++# penny-selloff 'Proceeds:LUV:2012-12-31' filename++# penny-selloff will use all transactions from all the filenames+# given. First it will make sure that the given Proceeds account has a+# balance that consists of a debit amount and a credit amount with+# differing commodities. penny-selloff takes the debit amount to be+# the commodity you are selling off, and the credit amount of the+# balance is the amount you received in the sale.++# penny-selloff then takes the second sub-account from the given+# account. Here, it is LUV. It then looks for accounts named+# Basis:SUB-ACCOUNT:***. Here it looks for Basis:LUV:***. *** is a+# Penny date (and optional time.)  All of these accounts that have a+# balance are totaled up. This total balance must have a credit+# balance that is greater than or equal to the debit balance of the+# Proceeds account, and a debit balance whose commodity is the same+# commodity as the credit balance of the Proceeds account.++# penny-selloff then computes a per-share price of the proceeds and+# calculates sale postings for each Basis account that has a+# balance. Shares are sold off on a FIFO (first-in, first-out) basis,+# so the oldest available shares are sold first. blah blah blah blah++# so for example, with the above transactions, running++# penny-selloff 'Proceeds:LUV:2012-12-31' stocks.pny++# will create a posting that looks like this:++;transaction created by penny-selloff for sale on 2012-12-31+2012-12-31 Realize gain or loss+    Proceeds:LUV:2012-12-31                             < $ 4800.00+    Proceeds:LUV:2012-12-31                             > LUV 300+    Basis:LUV:2012-10-19                                < LUV 100+    Basis:LUV:2012-10-19                                > $ 1000.00+    {Income:Capital Gain:LUV:2012-12-31:2012-10-19}     > $ 333.33+    Basis:LUV:2012-11-19                                < LUV 100+    Basis:LUV:2012-11-19                                > $ 1200.00+    {Income:Capital Gain:LUV:2012-12-31:2012-11-19}     > $ 400.01+    Basis:LUV:2012-12-19                                < LUV 100+    Basis:LUV:2012-12-19                                > $ 1400.00+    {Income:Capital Gain:LUV:2012-12-31:2012-12-19}     > $ 466.66++# Thus the capital gain shows up in the Capital Gain account, and all+# the Basis accounts and the Proceeds account have zero balances.++# You could also track commodities other than stocks. Let's say you+# buy a house.++2012-12-31 Buy a house+    {Assets:Real Estate}                                < HouseA 1+    Liabilities:Mortgage                                > $ 80 000.00+    Assets:Savings                                      > $ 20 000.00+        'Down payment+    Basis:House1:2012-12-31                             > HouseA 1+    Basis:House1:2012-12-31                             < $ 100 000.00++@ 2012-12-31 HouseA $100 000.00++# And some more stock purchases that we can use as examples in the+# penny-commodities man page:+2012-09-17 Buy stock+    Assets:Brokerage                                    < 100 YHOO+    Basis:YHOO:2012-09-17                               > 100 YHOO+    Assets:Checking                                     > $1568.00+    Basis:YHOO:2012-09-17                               < $1568.00++@ 2012-09-17 YHOO $15.68++2012-11-16 Buy stock+    Assets:Brokerage                                    < 100 YHOO+    Basis:YHOO:2012-11-16                               > 100 YHOO+    Assets:Checking                                     > $1786.00+    Basis:YHOO:2012-11-16                               < $1786.00++@ 2012-11-16 YHOO $17.86++2012-12-18 Buy stock+    Assets:Brokerage                                    < 100 YHOO+    Basis:YHOO:2012-12-18                               > 100 YHOO+    Assets:Checking                                     > $1982.00+    Basis:YHOO:2012-12-18                               < $1982.00++@ 2012-12-18 YHOO $19.82++@ 2012-12-20 YHOO $19.69++# And your house is going up in value:++@ 2013-01-01 HouseA $101 000.00+@ 2013-06-01 HouseA $102 000.00++# And an example involving the purchase and sale of stock:++2012-01-03 Buy stock+    Assets:Brokerage                                    < 100 UNP+    Assets:Checking                                     > $10800.00+    Basis:UNP:2012-01-03                                > 100 UNP+    Basis:UNP:2012-01-03                                < $10800.00++2012-12-28 Sell stock+    Assets:Brokerage                                    > 100 UNP+    Assets:Checking                                     < $12354.00+    Basis:UNP:2012-01-03                                < 100 UNP+    Basis:UNP:2012-01-03                                > $12354.00++# Here is another example of a sale using a Proceeds account:+2012-01-03 Buy stock+    Assets:Brokerage                                    < 100 C+    Assets:Checking                                     > $3072.00+    Basis:C:2012-01-03                                  > 100 C+    Basis:C:2012-01-03                                  < $3072.00++2012-12-03 Sell stock+    Assets:Brokerage                                    > 100 C+    Assets:Checking                                     < $3901.00+    Proceeds:C:2012-12-03                               < 100 C+    Proceeds:C:2012-12-03                               > $3901.00+
+ install-docs view
@@ -0,0 +1,59 @@+#!/bin/sh++# install-docs installs documentation for penny. You can edit the+# PREFIX variable below. Man pages go to+# $PREFIX/share/man/man[0-9]. Other documentation goes to+# $PREFIX/share/doc/penny.++# To remove the documentation, run "sh install-docs remove."++PREFIX=/usr/local+MANDIR=$PREFIX/share/man+DOCDIR=$PREFIX/share/doc/penny++doInstall() {+    for suffix in 1 7; do+        install -m755 -d $MANDIR/man${suffix}+        for filename in man/*.${suffix}; do+            install -v -m644 $filename $MANDIR/man${suffix}+        done+    done++    install -m755 -d $DOCDIR+    for filename in doc/*.{org,dot}; do+        install -v -m644 $filename $DOCDIR+    done++    install -m755 -d $DOCDIR/examples+    for filename in examples/*; do+        install -v -m644 $filename $DOCDIR/examples+    done+}++remove() {+    for suffix in 1 7; do+        for filename in man/*.${suffix}; do+            rm -v $MANDIR/man${suffix}/"$(basename $filename)"+        done+        rmdir -v -p $MANDIR/man${suffix}+    done+    rmdir -v -p $MANDIR++    for filename in doc/*.{org,txt}; do+        rm -v $DOCDIR/"$(basename $filename)"+    done+    rmdir -v -p $DOCDIR++    for filename in examples/*; do+        rm -v $DOCDIR/examples/"$(basename $filename)"+    done+    rmdir -v -p $DOCDIR/examples++}    +        ++if [ x$1 = xremove ]; then+    remove+else+    doInstall+fi
+ man/penny-basics.7 view
@@ -0,0 +1,372 @@+.TH penny-basics 1++.SH NAME+penny-basics - getting started with Penny++.SH PENNY IS DOUBLE-ENTRY ACCOUNTING++Penny is a double-entry accounting system. Other accounting systems+use double-entry principles--indeed, they are double-entry accounting+systems--but they do not use traditional double-entry accounting terms+such as+.I debit+and+.IR credit .+Or, they might use these terms, but not as they are used in a+traditional accounting sense. Unlike other systems, Penny does not try+to hide the details of double-entry accounting from you. Therefore,+you will need to have basic knowledge of double-entry accounting to+use Penny.++Ask yourself this question: what is a debit? If you said "it's what+happens when my bank charges me money," read "Learning about+double-entry accounting" below. If you said "left", then you're+probably ready to use Penny.++.SH LEARNING ABOUT DOUBLE-ENTRY ACCOUNTING++Since there are many great places to learn the basics of double-entry+accounting, I won't try to write about it here. Instead, here are some+places to look.++.IP \(bu+The Wikipedia article on double-entry accounting+(http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system) is+good.++.IP \(bu+Principles of Accounting (http://www.principlesofaccounting.com/) is a+great free online text.++.IP \(bu+Or, go to your favorite bookseller and find a used textbook on+accounting. These can be had for less than ten U.S. dollars, including+shipping.++.SH THE penny PROGRAM++The+.B penny+program is Penny's most basic tool. It gives you reports on what you+have recorded in your+.IR ledger ,+which is simply a text file (or multiple files) containing your+financial transactions.+.B penny+will never modify the data in your ledger; you maintain it yourself by hand (or by using some of the other programs in the+.B penny+suite, such as+.BR penny-fit ,+which will modify this data for you under some circumsances.)++Examine the+.I starter.pny+file, located in the+.I examples+directory of the+.I penny-bin+package. It shows the basics of how to write a Penny ledger file. This+man page will use this file for examples. Please follow along by+typing some of the commands yourself and experiment on your own.++.SH HOW penny WORKS+First, some terminology. Your ledger file contains+.IR transactions .+Each transaction consists of at least two+.BR postings .+All the postings in a transaction must+.IR balance ;+that is, the sum of the debits for each commodity must equal the sum+of the credits for that commodity.+.B penny+first reads in all your transactions. All the transactions must be balanced; if they are not,+.B penny+quits with an error message. Then+.B penny+splits the transactions into postings. After that,+.B penny+deals only with the postings, and not with the transactions that they were a part of.++.B penny+then will discard some of the postings and keep others, depending on the+.I filter specification+that you give. You can also sort the postings; by default they are left in the order that they were in in the file. Then a+.I report+that you specify is shown. The reports have additional options that you may specify.++This is all more clear with some examples, so let's go!++.SH SEEING THE POSTINGS IN AN ACCOUNT++Let's say you want to see the postings in your checking account.++.EX+penny --account Assets:Checking postings starter.pny+.EE++does what you would expect. If you want to limit the postings to those+before or after a certain date, try one of these:++.EX+penny -a Assets:Checking --and -d '>=' 2012-12-06 postings starter.pny+.EE++.EX+penny -a Assets:Checking --and -d '>=' 2012=12-06 --and \\+  -d '<' 2012-12-11 postings starter.pny+.EE++As these examples show, you can use+.I -a+instead of+.IR --account ,+and+.I -d+instead of+.IR --date .+Also, options such as+.I -a+and+.I -d+are called+.IR operands .+If you use more than one operand, you must join them together using+.IR operators .+The operators available to you are+.IR "--not " , "--and " ", and " "--or" ,+in that order of precedence. Here is a more complicated example that+uses the various operators and operands:++.EX+penny -a Assets:Checking --and --open --payee 'Whole Foods' \\+  --or --payee Comcast --close --and --date '>=' 2012-12-01 --and \\+  --date '<' 2012-01-01 pos starter.pny+.EE++This example also shows that you can abbreviate the name of the+report; that is, instead of+.IR postings ,+you can say+.IR pos .+You can use the shortest unambiguous abbreviation, so you could even+simply say+.I p+as there is no other report that starts with a+.IR p .++On the far right side of each+.I postings+report is a running balance, like in a checkbook register. You may have noticed that in the examples above the running total reflects only the postings you saw in the report. Sometimes that is what you want. Other times you might want to see the total running balance, even as it is affected by the postings not shown in the report. To do this, include the filtering options+.B after+the word+.I postings+rather than before. Filtering options that are included before the word+.I postings+affect which postings are part of the report. Only postings that are part of the report affect the running balance. Filtering options+.B after+the word+.I postings+affect which postings are+.B shown+in the report. Postings that are part of the report but are not shown+still affect the running balance.++Here is an example:++.EX+penny --sort date -a Assets:Checking pos -d '>' 2012-12-10 \\+  starter.pny+.EE++This example introduces a new option,+.IR --sort .+You specify the field name on which you want to sort your postings. To+sort them in ascending order, use all lower case letters when+specifying the name of the field. To sort in descending order,+capitalize the first letter. By default, +.B penny+does not sort your postings; it leaves them in the same order they+were in in your ledger file. If you want to see the running balance of+a report, it's important to make sure the postings are sorted in+chronologial order, either because you told+.B penny+to sort them or because you always keep your postings in chronological+order in your ledger file (I don't.)++Here we also see that you can use the+.I -d+option in place of+.IR --date .++.SH SEEING THE BALANCE OF AN ACCOUNT++To see the balance of your checking account, you could run++.EX+penny -a Assets:Checking pos --tail 1 starter.pny+.EE++and look at the running balance shown. This command included all the+postings in your checking account in the postings report, but it only+showed the last posting. (You could have used the UNIX+.B tail+program, but that will not always work well because sometimes a single+posting will take up more than one line on your screen.)++Another way to do it is with++.EX+penny -a Assets:Checking balance starter.pny+.EE++which shows the accounts hierarchically. The+.I balance+report is your friend if you want to see the balance of many accounts+at once.++.SH INCOME AND EXPENSES++How does your income compare to your expenses for a certain month?+Try:++.EX+penny --open --account-level 0 Income --or --account-level 0 Expenses \\+  --close --and -d '>=' 2012-12-01 --and -d '<' 2013-01-01 \\+  balance starter.pny+.EE++The+.I --account-level+operand takes two arguments. The first one is the number of the sub-account you wish to match. For instance, the account+.I Expenses:Food+has two sub-accounts: the first,+.IR Expenses ,+is numbered 0, and the second,+.IR Food ,+is numbered 1. The second argument to+.I --account-level+is the pattern you wish to use. All matching accounts will be part of your+.I balance+report. The result of this command shows your total expenses and total income, and the difference between the two is shown on the top+.I Total+line. (You don't have to use the+.I --account-level+operand, but it is more precise in this instance. Even more precise+would have been to specify+.I --exact+at the beginning of the command line. Without+.IR --exact ,+a pattern matches if the pattern you specify is found anywhere within+the target text.)++.SH BALANCING YOUR CHECKBOOK++Or, that fun task also known as "reconciling your account." First you+want to make sure that the reconciled balance of your checking account+is the same as what the bank says it was:++.EX+penny -a Assets:Checking --and --flag R bal starter.pny+.EE++Compare this total against the opening balance shown on your bank+statment. If they match, good. If not, figure out why and fix it+before proceeding. Then, find each posting shown on your bank+statement in your ledger. Add a+.I C+flag to each posting. After you have found all the bank's postings in+your ledger, run this:++.EX+penny -a Assets:Checking --and --open --flag R --or --flag C --close \\+  bal starter.pny+.EE++The balance shown should match what is on your bank statement. If not,+make sure you have matched up all the bank's postings with a posting+in your file, and make sure the bank's amount matches your amount. You+can easily see which postings you have just marked as cleared with:++.EX+penny -a Assets:Checking --and --flag C pos stater.pny+.EE++Once the balances match up, use your text editor or+.BR penny-reconcile (1)+to change the+.I C+flags to+.I R+flags.++.SH WHAT'S YOUR NET WORTH?++Try++.EX+penny -a Assets --or -a Liabilities balance starter.pny++.SH COLORS++Output from+.B penny+is easier to read when it's colorful.+.B penny+can use up to 256 colors on your terminal. Just make sure that your+.I TERM+environment variable is set to a terminal that supports 256 colors. I use+.BR xterm (1),+which supports 256 colors, but by default+.B xterm+sets the+.I TERM+environment variable to+.IR xterm ,+which only supports 8 colors. To make+.B xterm+set the+.I TERM+environment variable to one that supports 256 colors, I have the following text in my+.I ~/.Xresources+file:++.EX+XTerm*termName: xterm-256color+.EE++After running++.EX+xrdb -merge ~/.Xresources+.EE++and launching a new+.BR xterm ,+the new setting should take effect. It's likely your operating system+is already set up to automatically merge your+.I ~/.Xresources+file when you launch an X session.++If you don't like colors, use+.IR "--scheme plain" .+By default,+.B penny+does not use colors if its standard output is not a terminal, though you can override this with+.IR --color-to-file=yes .+This can be useful if you are sending output to a pager such as+.BR less (1)+and you want to see colors (with+.BR less ,+you will want to use the+.I -R+option.)++.SH WHERE TO GO FROM HERE++This is enough to get you started with+.BR penny .+If you want to know more, see+.BR penny (1),+which is an exhaustive reference. Also, see+.BR penny-suite (7),+which lists all Penny programs and documentation.
+ man/penny-commodities.7 view
@@ -0,0 +1,199 @@+.TH penny-commodities 7++.SH NAME+penny-commodities - tracking multiple commodities with Penny++.SH INTRODUCTION++Using+.B penny+is easy if you only want to track one commodity--most likely your home+country's currency. Things get a little more complicated if you want+to track more than one commodity, such as stocks, bonds, real estate,+or depreciable assets like vehicles. You probably want to keep track+of how much the commodity cost and the ultimate capital gain or loss.++This man page will tell you how to make entries in your ledger file+for commodity purchases, and how you can use+.B penny+to gather the information you need. You will also want to examine the+.I stocks.pny+file, which is in the+.I examples+directory of the+.I penny-bin+tarball. It contains many examples.++.SH ALL TRANSACTIONS MUST BE BALANCED++This means that the sum of all debits of a given commodity must equal+the sum of all credits of the same commodity. So, what if you want to+buy a commodity, like a stock? You could not record a transaction like+the following one, because it is not balanced:++.EX+# 2012-01-01 Purchase stock+#     Assets:Brokerage             < 100 LUV+#     Assets:Checking              > $1000.00+.EE++The transaction above is not balanced. Penny will never allow you to+create unbalanced transactions. Here is how I recommend solving this+problem:++.EX+2012-01-01 Purchase stock+    Assets:Brokerage       < 100 LUV+    Assets:Checking        > $1000.00+    Basis:LUV:2012-01-01   > 100 LUV+    Basis:LUV:2012-01-01   < $1000.00+.EE++This transaction is balanced. You will also find it helpful to record+a price:++.EX+@ 2012-01-01 LUV $10.00+.EE++This tells+.B penny+that on that date, one share of LUV was worth $10.00.+.B penny+will only know this if you tell it so; it will not infer this+information from the transactions you enter. Currently prices are only+used by the+.I convert+report, as we will see shortly.++.SH GAINS AND LOSSES BEFORE SALE++You have bought shares of YHOO at various times and you have entered+prices for them in your ledger file:++.EX+2012-09-17 Buy stock+    Assets:Brokerage           < 100 YHOO+    Basis:YHOO:2012-09-17      > 100 YHOO+    Assets:Checking            > $1568.00+    Basis:YHOO:2012-09-17      < $1568.00++@ 2012-09-17 YHOO $15.68++2012-11-16 Buy stock+    Assets:Brokerage           < 100 YHOO+    Basis:YHOO:2012-11-16      > 100 YHOO+    Assets:Checking            > $1786.00+    Basis:YHOO:2012-11-16      < $1786.00++@ 2012-11-16 YHOO $17.86++2012-12-18 Buy stock+    Assets:Brokerage           < 100 YHOO+    Basis:YHOO:2012-12-18      > 100 YHOO+    Assets:Checking            > $1982.00+    Basis:YHOO:2012-12-18      < $1982.00++@ 2012-12-18 YHOO $19.82++@ 2012-12-20 YHOO $19.69+.EE++You want to see how your investment is doing. Run the following+command:++.EX+penny -a Basis:YHOO convert stocks.pny+.EE++This shows you the current gain or loss on each purchase+transaction. A credit indicates that your investment has gained value;+a debit indicates a loss in value.++.SH GAINS AND LOSSES AFTER SALE++When you sell your commodity, record debits and credits agains the same+.I Basis+account that you used when purchasing the commodity.++.EX+2012-01-03 Buy stock+    Assets:Brokerage          < 100 UNP+    Assets:Checking           > $10800.00+    Basis:UNP:2012-01-03      > 100 UNP+    Basis:UNP:2012-01-03      < $10800.00++2012-12-28 Sell stock+    Assets:Brokerage          > 100 UNP+    Assets:Checking           < $12354.00+    Basis:UNP:2012-01-03      < 100 UNP+    Basis:UNP:2012-01-03      > $12354.00+.EE++Then you can see the profit or loss on the sale by using the+.I balance+report:++.EX+penny -a Basis:UNP bal stocks.pny+.EE++This shows a credit balance, indicating a capital gain.++Alternatively, if you don't want to figure out which+.I Basis+account to use when selling your commodity, simply use a+.I Proceeds+account, and then let+.BR penny-selloff (1)+calculate the gain or loss for you:++.EX+2012-01-03 Buy stock+    Assets:Brokerage          < 100 C+    Assets:Checking           > $3072.00+    Basis:C:2012-01-03        > 100 C+    Basis:C:2012-01-03        < $3072.00++2012-12-03 Sell stock+    Assets:Brokerage          > 100 C+    Assets:Checking           < $3901.00+    Proceeds:C:2012-12-03     < 100 C+    Proceeds:C:2012-12-03     > $3901.00+.EE++And then run+.BR penny-selloff :++.EX+penny-selloff Proceeds:C:2012-12-03 stocks.pny+.EE++It will create a transaction for you which will show you the total+capital gain or loss.++.SH TRACKING CAPITAL ASSETS++You could also use Penny to track a capital asset such as your+home. This example will get you started:++.EX+2012-12-31 Buy a house+    {Assets:Real Estate}        < HouseA 1+    Liabilities:Mortgage        > $ 80000.00+    Assets:Savings              > $ 20000.00+        'Down payment+    Basis:House1:2012-12-31     > HouseA 1+    Basis:House1:2012-12-31     < $ 100000.00++@ 2012-12-31 HouseA $100000.00+.EE++.SH BUGS+Report any bugs in the programs or documentation to+.MT omari@smileystation.com+Omari Norman+.ME++.SH SEE ALSO+.BR penny-suite (1)
+ man/penny-custom.7 view
@@ -0,0 +1,63 @@+.TH penny-custom 7++.SH NAME+penny-custom - creating a custom Penny program++.SH DESCRIPTION+.BR penny (1)+does not have a configuration file. Instead, it is easy to compile your own+.B penny+binary that has the settings you want. The advantage of this+configuration-file-free approach is that it is easier for me to write+and means that I don't have to cook up some sort of configuration file+format. The disadvantage is that you will need to have the Glasgow+Haskell Compiler (GHC) installed.++Compiling your own+.B penny+program can be useful if you find yourself always using some settings,+and you wish you could set them by default. (Sometimes it may be easy+to do this using shell aliases, functions, or scripts; other times,+this might not be so easy.) Also, there are some settings that can+only be changed by compiling your own+.B penny+binary.++These instructions will assume you have the+.B cabal+program installed and that you compiled+.B penny+from source (currently nobody distributes precompiled+.B penny+binaries, so this is a fairly safe assumption.)++All you need to do is download the+.B penny-bin+package, or find out where the+.B penny-bin+package archive is on your system. Unpack the+.B penny-bin+archive, and then edit the+.I penny.hs+file. This file is commented so editing it should be fairly simple+even if you know little or no Haskell.++Then, from the directory that has your+.I penny.hs+file, run+.I cabal install+and you should be all set, if you have already set up the+.B cabal+program to install programs to a location that is in your+path. Alternatively, run+.I ghc --make penny.hs+and then you will have a+.B penny+binary in the current directory and you can do what you want with it.++.SH BUGS+Please report any bugs in the programs or documentation to+omari@smileystation.com.++.SH SEE ALSO+.BR penny-suite (7)
+ man/penny-diff.1 view
@@ -0,0 +1,55 @@+.TH penny-diff 1++.SH NAME+penny-diff - show differences between two Penny files++.SH SYNOPSIS+.B penny-diff+[-12] FILE1 FILE2++.SH DESCRIPTION+.B penny-diff+shows the Penny items present in FILE1 that are not present in FILE2,+as well as the items present in FILE2 that are not present in+FILE1. An item is a transaction, a price point, or a single-line+comment. Blank lines are ignored. Differences in the order of items are+ignored. Because each single-line comment is its own item, no+difference will be detected if the single-line comments appear in a+different order in the two files, as long as all the same lines are+present.++.SH OPTIONS+.TP+.B -1+Show only the items present in FILE1 that are not present in FILE2.++.TP+.B -2+Show only the items present in FILE2 that are not present in FILE1.++.SH VERSUS diff(1)+You could do this with+.BR diff (1).+The biggest difference is that+.B penny-diff+understands and parses the ledger file, while+.B diff+does not. Experiment with the two programs to see which works better in your situation (and take note of options in+.B diff+such as+.IR -b " and " -w ,+which can ignore some whitespace changes.)++.SH EXIT STATUS+0 if there are no differences between FILE1 and FILE2; 1 if there are+differences; 2 if there was some problem (such as a file that could+not be parsed.)++.SH BUGS+Please report bugs in the program or documentation to+.MT omari@smileystation.com+Omari Norman.+.ME++.SH SEE ALSO+.BR penny-suite (7)
+ man/penny-fit.1 view
@@ -0,0 +1,215 @@+.TH penny-fit 7++.SH NAME+penny-fit - Penny financial institution statements parser++.SH SYNOPSIS+.B penny-fit+[global-options] COMMAND [local-options] ARGS++.SH DESCRIPTION+.B penny-fit+works with data you have downloaded from your financial+institution. It parses statements that you have downloaded and adds+transactions from the statements to your ledger, skipping those that+have already been added. It also helps you reconcile your ledger with+financial institution statements.++First you will have to configure and compile a+.B penny-fit+binary. You can use the+.B penny-fit-sample.hs+file in the+.B penny-bin+package as an example. The comments in that file should help you get+started.++.SH IMPORTING++To use+.BR penny-fit ,+first you will download the appropriate data from your financial+institution and place it in a file.+.I penny-fit -h+will tell give you a little more information about the place to look+on your institution's web site to download the data. Currently+.B penny-fit+can handle Bank of America and American Express data; maybe you can+add support for more institutions.++Then, run+.IR "penny-fit -f ACCOUNT import FILENAME" .+The first time you run this command, you will have to add the+.I --new+option after the+.I import+command, which allows the creation of a new database. Without this+option, if the database is not found,+.B penny-fit+quits with an error message. The ACCOUNT must be a financial+institution account that you configured in your+.B penny-fit+binary (if you configured a default account and you want to use that,+you can omit the+.I -f+option.) The FILENAME is the location of the data that you just+downloaded.++The+.I import+command will examine the data that you downloaded. Using the unique+identifiers already assigned to each posting by your financial+institution, the+.I import+command determines whether you have already downloaded each particular+posting. If the posting is new,+.I import+assigns a different, unique number to the posting. This is called a+.IR U-number .+The U-number allows you to uniquely identify each posting that you+download. The data from the financial institution, along with the+U-number, is added to a database at the location specified in your+configuration.+.I import+automatically skips postings that have already been processed, so you+do not have to worry about importing duplicate postings.++.SH MERGING++Next you will want to merge new postings into your ledger. Do this by+running+.I penny-fit -f ACCOUNT merge LEDGER_FILE...+where LEDGER_FILE is one or more filenames for your ledger files.+.I merge+examines your ledgers to see if each of the postings in the database+for this financial institution is represented in your ledger. To do+this it looks at the postings in the+.I pennyAcct+specified in your configuration. For each U-number in the database,+.I merge+sees if there is a posting in the+.I pennyAcct+with a tag bearing the U-number (e.g. if the U-number is 5, it looks+for the tag+.IR U5 .)+If a posting has more than one U-number tag, only the first is used;+the others are ignored. If such a posting is found,+.I merge+moves on to the next U-number in the database.++If no matching posting is found for a U-number,+.I merge+sees if there is a matching posting that does+.I not+have a U-number tag. If there is a posting in the+.I pennyAcct+that has the same quantity and date as the financial institution+posting,+.I merge+will then examine the debit or credit of the ledger posting. This+table describes whether+.I penny-fit+will find a match:++.TS+tab(:);+l l l l+- - - -+l l l l.+T{+If the financial institution posting is a+T}:T{+and translator is+T}:T{+and the ledger posting is a+T}:T{+then is there a match?+T}+increase:IncreaseIsDebit:debit:Yes+increase:IncreaseIsDebit:credit:No+increase:IncreaseIsCredit:debit:No+increase:IncreaseIsCredit:credit:Yes+decrease:IncreaseIsDebit:debit:No+decrease:IncreaseIsDebit:credit:Yes+decrease:IncreaseIsCredit:debit:Yes+decrease:IncreaseIsCredit:credit:No+.TE++If+.B penny-fit+finds a match for a financial institution posting in this way, then it+will assign a new U-number tag to the posting. If+.B penny-fit+does not find a match, then it will create an entirely new transaction and append it to the end of your ledger.++The result of+.I merge+is printed to standard output. You will probably want to redirect it+to a new file. Use+.BR diff (1)+or+.BR penny-diff (1)+to see what changes+.I merge+made. Typically you will need to edit the output somewhat.++.SH RECONCILING+Next you may wish to reconcile your ledger with your financial+institution data (that is, "balance the checkbook".) Typically the+most time-consuming part of this process is finding the postings in+your ledger that match the postings on your bank statement.+.B penny-fit clear+will help with this, dramatically speeding up the process.  To do+this, download data from your financial institution that corresponds+to the data that is covered within the current statement period. Run+.B penny-fit import+and+.B penny-fit merge+as described above. Then run++.EX+penny-fit -f ACCOUNT clear FIT_FILE LEDGER_FILE...+.EE++where FIT_FILE is the data file you downloaded from your financial+institution, and LEDGER_FILE contains your ledger data. The+.I clear+command will mark as cleared (that is, assign a+.I C+flag to) all postings in your LEDGER_FILEs that correspond to one of+the postings in the FIT_FILE. It does this by matching the U-number+tags on your postings to the U-numbers in the database. If a posting+has more than one U-number tag, only the first is used; the others are+ignored. The results are printed to standard output; you will probably+want to redirect this to a file. Once you have verified that things+are as they should be, you can use+.BR penny-reconcile (1)+to mark the cleared postings as reconciled.+.BR penny-basics (7)+has more details on how to use+.B penny+when reconciling a financial institution statement.++.SH OTHER COMMANDS+The+.I database+command prints the database for a particular financial institution to+standard output in human-readable form (the database unfortunately is+not in plain human-readable text.) For instance you might use this to+see what U-number is assigned to a particular financial institution+posting.++The+.I print+command parses a downloaded file of financial institution data and+prints the result to standard output. This is useful for seeing the+contents of a financial institution data file, or for testing new+parsers.++.SH BUGS+To quote another man page: "Bugs? You must be kidding, there are no+bugs in this software. But if we happen to be wrong, send us an email+with as much detail as possible to" omari@smileystation.com.++.SH SEE ALSO+.BR penny-suite (7)
+ man/penny-reconcile.1 view
@@ -0,0 +1,70 @@+.TH penny-reconcile 1++.SH NAME+penny-reconcile - mark cleared postings as reconciled++.SH SYNOPSIS+.B penny-reconcile+FILE...++.SH DESCRIPTION+Finds all postings in the input ledger files whose flag is exactly one+letter: the letter+.IR C .+Changes those flags to the letter+.I R+and prints the resulting ledger to standard output. This is useful+when reconciling a financial institution account: you may first mark+postings in your ledger that match a posting on your bank statement+with a+.I C+flag. Because only postings from the current statement will be marked with a+.I C+flag, it is easier to use+.BR penny (1)+to list only the postings that you have just cleared.  Then, after+ensuring that the statement is properly reconciled,+.B penny-reconcile+will automatically mark all the posts reconciled.++If no+.IR FILE ", or " FILE " is " - ,+read standard input.++.SH VERSUS sed(1) OR YOUR TEXT EDITOR+You could do this with+.BR sed (1)+or your text editor. Unlike those programs,+.B penny-reconcile+knows the structure of a ledger file. So+.B penny-reconcile+will not, for example, change the text+.IR [C] " to " [R]+where the+.I [C]+appears within a comment, while a naive+.BR sed (1)+script would do so. This would happen only rarely though, so you might+be just fine using a query-replace function in your text editor.++Also,+.B penny-reconcile+will tidy up your ledger file--that is, it might rearrange or delete+the non-significant whitespace within a transaction and posting, similar to+.BR penny-reprint (1).+This might be good or bad.+.BR sed (1)+or your text editor, on the other hand, will not do this.++.SH EXIT STATUS+0 if everything went fine; some other value if something went wrong+(e.g. a ledger file could not be parsed.)++.SH BUGS+Please report bugs in the program or documentation to+.MT omari@smileystation.com+Omari Norman.+.ME++.SH SEE ALSO+.BR penny-suite (7)
+ man/penny-reprint.1 view
@@ -0,0 +1,36 @@+.TH penny-reprint 1++.SH NAME+penny-reprint - read and reprint Penny ledger++.SH SYNOPSIS+.B penny-reprint+.I FILE...++.SH DESCRIPTION++Reads the Penny ledger(s) you specify and prints them to standard+output, in a (hopefully) more tidy format. All comments are retained,+but blank lines and insignicant whitespace within the transactions+(such as the amount of whitespace between multiple tags) are ignored.++If no+.IR FILE ,+or+.I FILE+is+.IR - ,+read standard input.++.SH EXIT STATUS+0 if everything went fine; another value if there were problems (such+as inability to read a ledger file.)++.SH BUGS+Please report any bugs in the program or documentation to+.MT omari@smileystation.com+Omari Norman+.ME++.SH SEE ALSO+.BR penny-suite (7)
+ man/penny-selloff.1 view
@@ -0,0 +1,85 @@+.TH penny-selloff 1++.SH NAME+penny-selloff - calculate capital gains and losses from commodity sales++.SH SYNOPSIS+.B penny-selloff+.I PROCEEDS-ACCOUNT FILE...++.SH DESCRIPTION++This is a description of how+.B penny-selloff+works. You will be lost if you have not read+.BR penny-commodities (7)+and looked at the+.I stocks.pny+file in the+.I examples+directory of the+.I penny-bin+tarball.++.B penny-selloff+examines the names of the sub-accounts of the+.IR PROCEEDS-ACCOUNT .+The first sub-account name is ignored (typically it will be+.IR Proceeds .)+The second sub-accont is the+.I group+(typically it will be the name of the commodity you are selling,+though it does not have to be.) The third sub-account is the date+(and, optionally, the time) when you sold the commodity.++.B penny-selloff+then parses all the ledger files given on the command line. (If no+files are given, or if a file is+.IR - ,+it reads standard input.)+Then+.B penny-selloff+examines the balance of the proceeds account. It must have exactly one+debit amount and one credit amount in the balance. The debit balance+is the commodity you are selling. The credit balance is the commodity+you received in return for the sale (typically your home currency.)++Next,+.B penny-selloff+examines the balances of all accounts in the parsed ledger files. It+finds accounts that have+.I Basis+as the first sub-account and whose second sub-account matches the+.I group+found in the proceeds account (again, typically this will be the name+of the commodity you are selling, though it does not have to be.) The+third sub-account must be a date and optional time, which was when the+commodity was purchased. Each of these accounts must have one debit+amount, which is the cost of the purchase, and a credit balance, which+is the amount of the commodity that was purchased.++Finally,+.B penny-selloff+computes postings that will eliminate the balances of the+.I Basis+accounts. It also computes the capital gain or loss from each+sale. The oldest commodities are sold first.++.B penny-selloff+creates a single transaction with postings that eliminate the balances+.I Basis+accounts corresponding to the commodities that were sold. The+transaction also contains postings with the capital gain or loss from+each sale. This transaction is printed to standard output.++.SH EXIT STATUS+0 if everything went fine; non-zero if there was a problem.++.SH BUGS+Please report bugs in the program or documentation to+.MT omari@smileystation.com+Omari Norman.+.ME++.SH SEE ALSO+.BR penny-suite "(7), " penny-commodities (7)
+ man/penny-suite.7 view
@@ -0,0 +1,129 @@+.TH penny-suite 7++.SH NAME+penny-suite - extensible double-entry accounting system++.SH DESCRIPTION++This manual page lists all the different components of Penny and also+catalogues all the documentation files and manual pages that are+available.++.SH PENNY PROGRAMS++Penny consists of many programs. Each has its own manual page.++.TP+.BR penny (1)++reports on postings in your ledger file++.TP+.BR penny-selloff (1)++calculate capital gains and losses on commodity sales++.TP+.BR penny-diff (1)++show differences between ledger files++.TP+.BR penny-reprint (1)++tidy up a ledger file, retaining comments++.TP+.BR penny-reconcile (1)++marks cleared postings as reconciled++.SH PENNY MANUAL PAGES++In addition to the manual pages shown above, a few more overview man+pages are available.++.TP+.BR penny-basics (7)++getting started with+.B penny++.TP+.BR penny-commodities (7)++tracking multiple commodities, such as stocks, with Penny++.TP+.BR penny-fit (1)++describes how you might be able to create a program that automatically+parses downloaded statements from your financial institution and+merges the resulting postings into your ledger file.++.TP+.BR penny-custom (7)++how to make a custom \fBpenny\fR program with your own settings++.SH SAMPLE FILES++There are many files available filled with sample data. They are in the+.I examples+directory of the+.B penny-bin+tarball.++.TP+.BR starter.pny++describes the basics of the Penny ledger file format, and contains+sample data. The+.BR penny-basics (7)+manual page uses the sample data from this file.++.TP+.BR stocks.pny++shows how to use Penny to track multiple commodities, like stocks++.TP+.BR stocks-realized.pny++shows an example of the results of using+.BR penny-selloff (1)+++.SH TEXT FILES++Much documentation is available only in plain-text form. These files+are in the+.B doc+directory of the+.B penny-bin+tarball.++.TP+.BR amex-file-format.org++the file format American Express uses for its downloadable data++.TP+.BR bofa-file-format.org++the file format Bank of America uses for its downloadable data++.TP+.BR dependencies.dot++the dependencies between the various parts of the Penny library++.TP+.BR ledger-grammar.org++an EBNF grammar for the ledger file format++.SH HADDOCK++Feel free to examine the Haskell source code of the Penny library,+which also contains Haddock documentation markup.
+ man/penny.1 view
@@ -0,0 +1,651 @@+.TH penny 1++.SH NAME+penny - report on postings in financial ledger++.SH SYNOPSIS+penny [global options] report name [report options] FILE...++.SH DESCRIPTION++This manual page is a complete reference for the operation of+.BR penny .+To get started, see+.BR penny-getting-started (1).++The+.B penny+program prepares reports based upon the+.B postings+in your ledger file. Each transaction has at least two postings. After+.B penny+verifies that your ledger file is+.I balanced+(that is, every transaction has debits and credits that are equal)+.B penny+splits each transaction into its component postings. After this point,+.B penny+for the most part deals only with postings, not with transactions.++You may specify+.IR "global options" .+Most global options allow you to create a +.I filter expression+that determines which postings are ultimately used for the report. The+filter expression may also contain additional options that perform+tasks other than filtering. For example, you may specify how to+perform text matching (for instance, you may want to use regular+expressions) or you may specify that you want to sort the postings (by+default, they are not sorted and are left in the order in which they+were found in the input ledger).++Other global options control the color scheme of the report and+whether colors are used at all.++Next you must specify a+.IR report .+Currently there are three reports. The+.BR postings+report shows information about each posting, like a checkbook register.+There are two reports that show account balances only: the+.BR balance+report and the+.B convert+report. Each report shows only the postings returned by the posting+filter expression.++You may then specify options pertaining to the report. The+.BR postings+report takes many options that format its output and that specify+which postings are shown in the report; however, postings that the+filter expression returned that are not shown in the report still+affect the running balance. This can be useful if, for example, you+want to see the total balance in your bank account and how some+transactions affect it but you do not want to see all transactions+since the beginning of time. The+.BR balance+and+.B convert+reports take many fewer options.++Finally you may specify one or more files from which to draw the+data. If you do not specify a file, standard input is used. The report+is always printed to standard output.++Entering the data into the ledger files is your responsibility;+.B penny+will never modify this data. To see how to enter data into the file,+see the file+.BR examples/starter.pny ,+which is included inside the package for+.BR penny-bin .++.SH COMPARERS++Many options perform comparisons; for example, the+.B --date+option compares postings to a date you specify to determine+which postings to keep and which to reject. Where+.I comparer+appears below, you must supply one of the following strings.+You will need to quote many of them, because many of these+characters will have special meaning for your shell.++.TS+tab(:);+l l l+- - -+lB lB l.+Primary form:Alternate form:Comparison performed+<::Less than+<=::Less than or equal to+==:T{+=+T}:Equals+>::Greater than+>=::Greater than or equal to+/=:!=:Not equal to+.TE++.SH DISPLAY OPTIONS++.TP+.BI "--scheme " SCHEME_NAME+Use the given color scheme. By default, three schemes are available:+.IR dark ,+designed for dark-background terminals,+.IR light ,+for light-background terminals, and+.IR plain ,+which uses the terminal's default colors.+By default the+.I dark+scheme is used.++.TP+.BI "--color-to-file " "no|yes"+Whether to use color when standard output is not a terminal. Default is+.IR no .+If standard output is a terminal, the maximum color capabilities of your terminal are used. (If you do not like color, preface your+.B penny+command with+.IR TERM=dumb+or, alternatively, use+.BR "--scheme plain" ,+which has the same effect.)++.SH POSTING FILTERS+.SS Dates++.TP+.BI "--date | -d " "comparer timespec"++The date of the posting must be within the time frame given.+.BR date+is the same format as dates in the+.B penny+file and+is either a date alone, such as+.BR 2012-04-25 ,+or a date and a time, such as+.BR "2012-04-25 14:25 -0400" .++.TP+.B --current+Same as+.BI "--date <= " "right now"++.SS Serials+These perform matching based on serials. For more on serials,+see the section+.B SERIALS+below. Each option takes the form+.IR "option comparer number" .+.TP+.B --globalTransaction+.TQ+.B --revGlobalTransaction+.TQ+.B --globalPosting+.TQ+.B --revGlobalPosting+.TQ+.B --fileTransaction+.TQ+.B --revFileTransaction+.TQ+.B --filePosting+.TQ+.B --revFilePosting++.SS Pattern matching++These options allow you to filter postings by specifying a pattern+that must match a particular component of the posting. By default the simple+.I within+matcher is used, and matches are case-insensitive.++.TP+.BI "--account | -a " pattern+The sub-accounts of the account are separated with colons, and the+match succeeds if the pattern matches this entire colon-separated+name.++.TP+.BI "--account-level " "number pattern"+The account is separated into sub-accounts, which are numbered+beginning at zero. The match succeeds if the posting has a sub-account+numbered at the given level, and if that sub account matches the given+pattern.++.TP+.BI --account-any " pattern"+The account is separated into sub-accounts. The match succeeds if the+pattern matches any of a posting's sub-accounts.++.TP+.BI  "--payee | -p " pattern+Succeeds if the pattern matches the posting's payee. If the posting+has no payee, the payee of the parent transaction is used (if there is+one).++.TP+.BI "--tag | -t " pattern+Succeeds if any one of the posting's tags matches the given pattern.++.TP+.BI --number " pattern"+Succeeds if the posting's number matches the given pattern. (This is+the number that you specify in parentheses in your ledger file, not+the line number or any of the serial numbers.)  If the posting has no+number, the number of the parent transaction is used (if there is+one).++.TP+.BI --flag " pattern"+Succeeds if the posting's flag matches the given pattern. If the+posting has no number, the flag of the parent transaction is used+(if there is one).++.TP+.BI --commodity " pattern"+Succeeds if the posting's commodity matches the given pattern.++.TP+.BI "--posting-memo " pattern+The posting memo must match the given pattern. For the purpose of this+option, the line breaks in the posting memo are replaced with spaces.++.TP+.BI "--transaction-memo " pattern+The transaction memo must match the given pattern. For the purpose of+this option, the line breaks in the transaction memo are replaced with+spaces.++.SS Other posting characteristics++.B --debit+The entry must be a debit.++.B --credit+The entry must be a credit.++.BI --qty " comparer number"+The entry's quantity must fall within the given range.++.SS Operators++Each of the options above is a single operand. If you have multiple+operands, you must join them together using operators.  These+operators are specified below, from highest to lowest precedence. All+operators are left associative.++.TP+.BI "--open " expr " --close"+Force precedence using parentheses. Enclose a complete expression+between the+.B --open+and+.B --close+options.++.TP+.BI "--not " expr+True if+.I expr+is false.++.TP+.IB expr1 " --and " expr2+True if+.I expr1+and+.I expr2+are both true.++.TP+.IB expr1 " --or " expr2+True if+.I expr1+or+.I expr2+is true.++.SS Options affecting patterns++These options affect how patterns are interpreted. The order of the+.B penny+command line is significant; each of these options only affects+patterns that appear after it on the command line.++.TP+.B "-i | --case-insensitive"+Patterns are case insensitive (default)++.TP+.B "-I | --case-sensitive"+Patterns are case sensitive++.TP+.B --within+Use the "within" matcher (default), which matches if the pattern given+appears anywhere within the target text. This is a simple+letter-for-letter match, not a regular expression, though its case+sensitivity is affected by the+.B --case-insensitive+and+.B --case-sensitive+options.++.TP+.B --pcre+Use the "pcre" matcher, which uses Perl-compatible regular expressions (see+.BR pcresyntax "(1) and " pcrepattern (1))++.TP+.B --posix+Use the "posix" matcher, which uses POSIX regular expressions (see+.BR regex (7))++.TP+.B --exact+Use the "exact" matcher, which matches if the given pattern is a+letter-for-letter match of the target text, with case sensitivity+determined by the+.B --case-insensitive+and+.B --case-sensitive+options.++.SH REMOVING POSTINGS AFTER SORTING AND FILTERING++.TP+.BI "--head " n+Keep only the first+.I n+postings.++.TP+.BI "--tail " n+Keep only the last+.I n+postings.++.SH SORTING++.TP+.BI "--sort | -s " key+Sorts postings according to a key. Use multiple+.B --sort+options to sort by more than one key. Valid keys are: payee, date,+flag, number, account, drCr, qty, commodity, postingMemo,+transactionMemo.++The postings are sorted in ascending order if the first letter of the+key is lowercase; descending order if the first letter of the key is+uppercase.++Postings by default are sorted by date in ascending order; however,+any specification of a+.B --sort+option on the command line overrides this. For example,+.B --sort payee+sorts postings by payee from A-Z, while+.B --sort date --sort payee+sorts postings by date from oldest to newest and sorts postings with+the same date in payee order from A to Z.++If you want to leave postings in the order in which they appeared in+your ledger file, use+.BR "--sort none" .+++.SH HELP+.TP+.B --help | -h+Show help and exit++.SH POSTINGS REPORT++The+.B postings+report, or+.B pos+for short, shows postings in order with a running balance. This report+takes all the options shown above in the categories from "Posting+filters" to "Removing postings after sorting and filtering." These+options affect which postings are shown in the report. Postings that+are not shown in the report but which were not filtered out in the+filtering stage still affect the report's running balance.++Additional options for the+.B postings+report:++.SS Additional serial filtering options+These options affect which postings are shown. Postings that were not+filtered in the filtering stage but that are not shown still affect+the running balance. In addition to using the same options that are+used for filtering, these additional options are available that are+based on some additional serials. They take the form+.IR "option comparer number " .+For more information on serials, see the+.B SERIALS+section below.++.TP+.B --filtered+filtered serial, forward component+.TP+.B --revFiltered+filtered serial, reverse component+.TP+.B --sorted+sorted serial, forward component+.TP+.B --revSorted+sorted serial, reverse component++.SS Other additional options for the postings report++.TP+.BI --width " num"+Gives a hint for roughly how wide the report should be, in+columns. (By default the+.I COLUMNS+environment variable is used.)++.TP+.BI "--show" " field"+.TQ+.BI "--hide" " field"+Show or hide fields from the displayed report. Fields are displayed in+a fixed order, which is the same as the order that the table below is+in. Fields with an asterisk are shown by default.++.\" Do not precede the empty fields in the table with any spaces. This+.\" will cause GNU tbl to segfault.+.\" See+.\" http://lists.gnu.org/archive/html/groff/2010-11/msg00014.html+.\" Apparently the bug has been fixed but who knows how long that will+.\" take to propogate.++.TS+tab(:);+lB lB l.+:globalTransaction:globalTransaction serial, forward component+:revGlobalTransaction:globalTransaction serial, reverse component+:globalPosting:globalPosting serial, forward component+:revGlobalPosting:globalPosting serial, reverse component+:fileTransaction:fileTransaction serial, forward component+:revFileTransaction:fileTransaction serial, reverse component+:filePosting:filePosting serial, forward component+:revFilePosting:filePosting serial, reverse component+:filtered:filtered serial, forward component+:revFiltered:revFiltered serial, reverse component+:sorted:sorted serial, forward component+:revSorted:sorted serial, reverse component+:visible:visible serial, forward component+:revVisible:visible serial, reverse component+:lineNum:line number (starting from 1)+*:date:transaction's date+:flag:posting or transaction flag+:number:posting or transaction's number+*:payee:posting or transaction's payee+*:account:posting's account+*:postingDrCr:whether the posting is a debit or credit+*:postingCmdty:posting's commodity+*:postingQty:posting's quantity+*:totalDrCr:whether the running total is a debit or credit+*:totalCommodity:commodity of the running total+*:totalQty:quantity of the running total+:tags:posting's tags+:memo:the posting and transaction memo+:filename:filename where the posting came from+.TE++.TP+.B --show-all+Show all fields++.TP+.B --hide-all+Hide all fields++.TP+.BI "--zero-balances " "show|hide"+Whether to show the balance of all commodities in the+.IR totalDrCr ", " totalCommodity ", and " totalQty+fields, even if that balance is zero. (default: hide)++.TP+.B --help | -h+Show help and exit++.SH BALANCE REPORT+The+.B balance+report summarizes the balances in each account that is represented in+the postings that remain after the filtering specifications are+carried out. You can use sorting specifications in the posting filter+expression, but they will have no effect. The+accounts are shown hierarchically.++The+.B balance+report accepts the following options:++.TP+.BI "--zero-balances " "show|hide"+Whether to show balances that are zero (default: hide)++.TP+.BI "--order " "ascending|descending"+Sort in ascending (default) or descending order by account name++.TP+.B --help | -h+Show help and exit++.SH CONVERT REPORT++The+.B convert+report shows account balances after converting all amounts to a single+commodity. In addition to converting commodities, it also can sort+accounts by their balances.  Accepts ONLY the following report+options:++.TP+.BI "--zero-balances " "show|hide"+Whether to show balances that are zero (default: hide)++.TP+.BI "--commodity | -c " "TARGET-COMMODITY"+Convert all commodities to+.IR TARGET-COMMODITY .+By default, the commodity that appears most often as the target+commodity in your price data is used. If there is a tie, the price+closest to the end of your list of prices is used.++.TP+.BI "--date | -d " "DATE-TIME"+Convert prices as of the date and time given. By default, the current+date and time is used.++.TP+.BI "--sort | -s " "name|qty"+Sort balances by sub-account name (default) or by quantity++.TP+.BI "--order " "ascending|descending"+Sort in ascending (default) or descending order++.TP+.B --help | -h+Show help and exit++.SH SERIALS++Each posting is assigned several+.IR serials ,+each of which is a pair of ordinal numbers. The first number in the+pair, or+.IR "forward component" ,+is assigned by numbering the transactions or postings from+beginning to end beginning at zero, while the second number in the+pair, or+.IR "reverse component" ,+is assigned by numbering the transactions or postings from end to+beginning, beginning at zero. Here are all the serials that are+assigned to each posting.++.TP+.B globalTransaction+All transactions are numbered in order, beginning with those in the+first file specified on the command line and ending with the last+file. Occurs before the transactions are split into postings.++.TP+.B fileTransaction+Like+.IR globalTransaction ,+but numbering restarts with each new file.++.TP+.B globalPosting+All postings are numbered in order, beginning with those in the first+file specified on the command line and ending with the last file.++.TP+.B filePosting+Like+.IR globalPosting ,+but numbering restarts with each new file.++.TP+.B filtered+Postings are numbered after first removing the postings as specified+by the filtering options specified on the command line.++.TP+.B sorted+Postings are numbered in order after the sorting options have been+applied, which occurs after the filtering options have been applied.++.TP+.B visible+Postings are numbered in order after removing the postings as+specified by the options to the+.I postings+report. (Applies only to the+.I postings+report.)++.SH DEFAULT OPTIONS+This manual page often specifies defaults for various options. These+are the defaults that come "out of the box." You may configure your+own default options (see+.BR penny-custom (7).)+If you do that, the defaults you configure will be reflected in the+output of+.IR "penny -h" .++.SH EXIT STATUS+.B 0+if no errors;+.B 1+if there was a problem.++.SH BUGS+Please report any bugs in the software or documentation to+omari@smileystation.com.++.SH SEE ALSO+.BR penny-suite (7)++The file+.B examples/starter.pny+in the tarball for the+.B penny-bin+package shows you how to write a ledger file.
penny-bin.cabal view
@@ -1,9 +1,9 @@ Name: penny-bin-Version: 0.4.0.0+Version: 0.6.0.0 Cabal-version: >=1.8 Build-Type: Simple-License: MIT-Copyright: 2012 Omari Norman.+License: BSD3+Copyright: 2012-2013 Omari Norman author: Omari Norman maintainer: omari@smileystation.com stability: Experimental@@ -11,36 +11,169 @@ bug-reports: omari@smileystation.com Category: Console, Finance License-File: LICENSE-synopsis: Extensible double-entry accounting system - binary+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. For more information, see the page for the Penny library,-  at <http://hackage.haskell.org/package/penny-lib>.+  libraries.    . -  Penny currently is designed only for UNIX-like operating systems.+  * 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.++  .++  * 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!++  .++  * Uses no GHC extensions. However, the code is only tested under GHC+    and for all practical purposes it will only run under GHC at this+    time because it uses libraries such as Data.Text that are+    available only under GHC. Despite this I expect I will continue to+    avoid language extensions.++  .++  * 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.++  .+   You can install this binary simply by typing "cabal install   penny-bin", which will install this binary along with all the-  necessary dependencies. However, this will not install any of the-  documentation. To get the documentation, you will want to manually-  download the "penny-bin-*.tar.gz" file, available at the bottom of the-  Hackage page (probably the bottom of the page you are looking at-  now.) Alternatively, you can figure out where the "cabal" utility-  stored the archive file for you. After unpacking the archive you-  will find a file, "penny.1", which is the manual page. You may copy-  this file to the appropriate location on your system (maybe-  \/usr\/share\/man\/man1) or you can simply view it by giving the-  appropriate path to the UNIX "man" command. You will also find the-  file "samplePennyFile", which will show you a sample ledger.+  necessary dependencies. The penny-bin package also has all+  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. -extra-source-files: penny.1 samplePennyFile+extra-source-files:+    install-docs+  , README+  , doc/*.org+  , doc/*.dot+  , examples/*.pny+  , man/*.1+  , man/*.7+    source-repository head     type: git@@ -49,13 +182,98 @@ Executable penny   Build-depends:     base ==4.*,-    penny-lib ==0.4.*+    penny-lib ==0.6.0.0    Main-is: penny.hs   GHC-Options: -Wall   if flag(debug)     GHC-Options: -rtsopts -auto-all -caf-all +  if ! flag(build-penny)+    buildable: False++Executable penny-selloff+  Build-depends:+    base == 4.*,+    penny-lib ==0.6.0.0,+    explicit-exception ==0.1.*,+    containers ==0.4.*,+    semigroups ==0.8.*,+    text ==0.11.*,+    parsec ==3.1.*,+    multiarg ==0.8.*,+    transformers ==0.3.*++  Main-is: penny-selloff.hs+  GHC-Options: -Wall+  if flag(debug)+    GHC-Options: -rtsopts -auto-all -caf-all++  if ! flag(build-selloff)+    buildable: False++Executable penny-diff+  Build-depends:+    base ==4.*,+    penny-lib ==0.6.0.0,+    text ==0.11.*,+    multiarg ==0.8.*,+    explicit-exception == 0.1.*++  Main-is: penny-diff.hs+  GHC-Options: -Wall+  if flag(debug)+    GHC-Options: -rtsopts -auto-all -caf-all++  if ! flag(build-diff)+    buildable: False++Executable penny-reprint+  Build-depends:+      base ==4.*+    , penny-lib ==0.6.0.0+    , pretty-show ==1.2.*+    , text ==0.11.*++  main-is: penny-reprint.hs+  ghc-options: -Wall+  if ! flag(build-reprint)+    buildable: False++Executable penny-reconcile+  Build-depends:+      base ==4.*+    , penny-lib ==0.6.0.0+    , text ==0.11.*+    , multiarg ==0.8.*+    , explicit-exception ==0.1.*++  main-is: penny-reconcile.hs+  ghc-options: -Wall+  if ! flag(build-reconcile)+    buildable: False++ Flag debug   Description: Turns on debugging options   Default: False++Flag build-penny+  Description: Build the penny executable+  Default: True++Flag build-selloff+  Description: Build the penny-selloff executable+  Default: True++Flag build-diff+  Description: Build the penny-diff executable+  Default: True++Flag build-reprint+  Description: Build the penny-reprint executable+  Default: True++Flag build-reconcile+  Description: Build the penny-reconcile executable+  Default: True
+ penny-diff.hs view
@@ -0,0 +1,208 @@+module Main where++import qualified Control.Exception as CEx+import Control.Monad (when)+import qualified Control.Monad.Exception.Synchronous as Ex+import Data.Maybe (fromJust)+import Data.List (deleteFirstsBy)+import qualified System.Console.MultiArg as M+import qualified Penny.Lincoln as L+import qualified Penny.Lincoln.Predicates as P+import qualified Penny.Copper as C+import qualified Penny.Copper.Render as CR+import Data.Maybe (mapMaybe)+import qualified Data.Text as X+import qualified Data.Text.IO as TIO+import qualified System.Exit as E+import qualified System.IO as IO++groupingSpecs :: CR.GroupSpecs+groupingSpecs = CR.GroupSpecs CR.NoGrouping CR.NoGrouping++main :: IO ()+main = runPennyDiff groupingSpecs++help :: String+help = unlines+  [ "usage: penny-diff [-12] FILE1 FILE2"+  , "Shows items that exist in FILE1 but not in FILE2,"+  , "as well as items that exist in FILE2 but not in FILE1."+  , "Options:"+  , "-1 Show only items that exist in FILE1 but not in FILE2"+  , "-2 Show only items that exist in FILE2 but not in FILE1"+  ]++data Args = ArgFile File | Filename String | Help+  deriving (Eq, Show)++data DiffsToShow = File1Only | File2Only | BothFiles++optFile1 :: M.OptSpec Args+optFile1 = M.OptSpec [] "1" (M.NoArg (ArgFile File1))++optFile2 :: M.OptSpec Args+optFile2 = M.OptSpec [] "2" (M.NoArg (ArgFile File2))++data File = File1 | File2+  deriving (Eq, Show)++-- | All possible items, but excluding blank lines.+data NonBlankItem+  = Transaction L.Transaction+  | Price L.PricePoint+  | Comment C.Comment+  deriving (Eq, Show)++clonedNonBlankItem :: NonBlankItem -> NonBlankItem -> Bool+clonedNonBlankItem nb1 nb2 = case (nb1, nb2) of+  (Transaction t1, Transaction t2) -> P.clonedTransactions t1 t2+  (Price p1, Price p2) -> p1 == p2+  (Comment c1, Comment c2) -> c1 == c2+  _ -> False++toNonBlankItem :: C.Item -> Maybe NonBlankItem+toNonBlankItem i = case i of+  C.Transaction t -> Just (Transaction t)+  C.PricePoint p -> Just (Price p)+  C.IComment c -> Just (Comment c)+  _ -> Nothing+++showLineNum :: File -> Int -> X.Text+showLineNum f i = X.pack ("\n" ++ arrow ++ " " ++ show i ++ "\n")+  where+    arrow = case f of+      File1 -> "<=="+      File2 -> "==>"+++-- | Renders a transaction, along with a line showing what file it+-- came from and its line number. If there is a TransactionMemo, shows+-- the line number for the top line for that; otherwise, shows the+-- line number for the TopLine.+renderTransaction+  :: CR.GroupSpecs+  -> File+  -> L.Transaction+  -> Maybe X.Text+renderTransaction gs f t = fmap addHeader $ CR.transaction gs t+  where+    (L.Family tl _ _ _) = L.unTransaction t+    lin = case L.tMemo tl of+      Nothing -> L.unTopLineLine . fromJust+                 . L.tTopLineLine $ tl+      Just _ -> L.unTopMemoLine . fromJust+                . L.tTopMemoLine $ tl+    addHeader x = (showLineNum f lin) `X.append` x++renderPrice :: CR.GroupSpecs -> File -> L.PricePoint -> Maybe X.Text+renderPrice gs f p = fmap addHeader $ CR.price gs 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 n = case n of+  Transaction t -> renderTransaction gs f t+  Price p -> renderPrice gs f p+  Comment c -> CR.comment c++runPennyDiff :: CR.GroupSpecs -> IO ()+runPennyDiff co = do+  (f1, f2, dts) <- parseCommandLine+  l1 <- parseFile f1+  l2 <- parseFile f2+  let (r1, r2) = doDiffs l1 l2+  showDiffs co dts (r1, r2)+  case (r1, r2) of+    ([], []) -> E.exitSuccess+    _ -> E.exitWith (E.ExitFailure 1)++showDiffs+  :: CR.GroupSpecs+  -> DiffsToShow+  -> ([NonBlankItem], [NonBlankItem])+  -> IO ()+showDiffs co dts (l1, l2) =+  case dts of+    File1Only -> showFile1+    File2Only -> showFile2+    BothFiles -> showFile1 >> showFile2+  where+    showFile1 = showNonBlankItems co File1 l1+    showFile2 = showNonBlankItems co File2 l2++failure :: String -> IO a+failure s = IO.hPutStrLn IO.stderr s+  >> E.exitWith (E.ExitFailure 2)++showNonBlankItems+  :: CR.GroupSpecs+  -> File+  -> [NonBlankItem]+  -> IO ()+showNonBlankItems o f ls =+  mapM_ (showNonBlankItem o f) ls++showNonBlankItem :: CR.GroupSpecs -> File -> NonBlankItem -> IO ()+showNonBlankItem co f nbi = maybe e TIO.putStr+  (renderNonBlankItem co f nbi)+  where+    e = failure $ "could not render item: " ++ show nbi+++-- | Returns a pair p, where fst p is the items that appear in file1+-- but not in file2, and snd p is items that appear in file2 but not+-- in file1.+doDiffs+  :: C.Ledger+  -> C.Ledger+  -> ([NonBlankItem], [NonBlankItem])+doDiffs l1 l2 = (r1, r2)+  where+    mkNbList = mapMaybe toNonBlankItem . C.unLedger+    (nb1, nb2) = (mkNbList l1, mkNbList l2)+    df = deleteFirstsBy clonedNonBlankItem+    (r1, r2) = (nb1 `df` nb2, nb2 `df` nb1)++parseFile :: String -> IO C.Ledger+parseFile s = do+  eiTxt <- CEx.try $ TIO.readFile s+  txt <- case eiTxt of+    Left e -> failure (show (e :: IOError))+    Right g -> return g+  let fn = L.Filename . X.pack $ s+      c = C.FileContents txt+      parsed = C.parse [(fn, c)]+  case parsed of+    Ex.Exception e -> failure (show e)+    Ex.Success g -> return g+++-- | Returns a tuple with the first filename, the second filename, and+-- an indication of which differences to show.+parseCommandLine :: IO (String, String, DiffsToShow)+parseCommandLine = do+  as <- M.getArgs+  let parsed = M.simple+                M.Intersperse [optFile1, optFile2] Filename as+  ls <- case parsed of+    Ex.Exception e ->+      let err = "penny-diff: could not parse command line: "+            ++ show e+      in failure err+    Ex.Success g -> return g+  when (any (== Help) ls) (putStrLn help >> E.exitSuccess)+  let toFilename a = case a of+        Filename s -> Just s+        _ -> Nothing+  (fn1, fn2) <- case mapMaybe toFilename ls of+    x:y:[] -> return (x, y)+    _ -> failure "penny-diff: error: you must supply two filenames."+  let getDiffs+        | ((ArgFile File1) `elem` ls)+          && ((ArgFile File2) `elem` ls) = BothFiles+        | ((ArgFile File1) `elem` ls) = File1Only+        | ((ArgFile File2) `elem` ls) = File2Only+        | otherwise = BothFiles+  return (fn1, fn2, getDiffs)
+ penny-reconcile.hs view
@@ -0,0 +1,91 @@+module Main where++import Data.Maybe (fromMaybe, mapMaybe, fromJust)+import qualified Data.Text as X+import qualified Data.Text.IO as TIO+import Control.Monad (guard)+import qualified Control.Monad.Exception.Synchronous as Ex+import qualified Penny.Copper as C+import qualified Penny.Lincoln as L+import qualified System.Console.MultiArg as MA+import System.Exit (exitSuccess)++-- | Changes a posting to mark it reconciled, if it was already marked+-- as cleared.+changePosting :: L.Posting -> L.PostingChangeData+changePosting p = fromMaybe L.emptyPostingChangeData $ do+  fl <- L.pFlag p+  guard (L.unFlag fl == X.singleton 'C')+  let fl' = L.Flag . X.singleton $ 'R'+  return $ L.emptyPostingChangeData { L.pcFlag = Just (Just fl') }++-- | Changes a TopLine to mark it as reconciled, if it was already+-- marked as cleared.+changeTopLine :: L.TopLine -> L.TopLineChangeData+changeTopLine t = fromMaybe L.emptyTopLineChangeData $ do+  fl <- L.tFlag t+  guard (L.unFlag fl == X.singleton 'C')+  let fl' = L.Flag . X.singleton $ 'R'+  return $ L.emptyTopLineChangeData { L.tcFlag = Just (Just fl') }++changeTransaction :: L.Transaction -> L.Transaction+changeTransaction t =+  let fam = L.mapParent changeTopLine+            . L.mapChildren changePosting+            . L.unTransaction+            $ t+  in L.changeTransaction fam t++help :: String+help = unlines+  [ "usage: penny-reconcile [-h] FILE..."+  , "Finds all transactions and postings bearing a \"C\" flag"+  , "and changes them to a \"R\" flag in the listed FILEs."+  , "If no FILE, or if FILE is -, read standard input."+  , ""+  , "Output is printed to standard output. Input files are not"+  , "changed."+  , ""+  , "Options:"+  , "  -h, --help Show help and exit."+  ]++toPosArg :: Arg -> Maybe String+toPosArg a = case a of+  APosArg s -> Just s+  _ -> Nothing++data Arg+  = AHelp+  | APosArg String+  deriving (Eq, Show)++data Opts+  = NeedsHelp+  | DoIt [String]+  deriving Show+++parseArgs :: [String] -> Opts+parseArgs ss =+  let opts = [ MA.OptSpec ["help"] "h" (MA.NoArg AHelp) ]+  in case MA.simple MA.Intersperse opts APosArg ss of+      Ex.Exception e -> error . show $ e+      Ex.Success g ->+        if any (== AHelp) g+        then NeedsHelp+        else DoIt $ mapMaybe toPosArg g++groupSpecs :: C.GroupSpecs+groupSpecs = C.GroupSpecs C.NoGrouping C.NoGrouping++main :: IO ()+main = do+  os <- fmap parseArgs MA.getArgs+  ls <- case os of+    NeedsHelp -> putStrLn help >> exitSuccess+    DoIt ss -> return ss+  led <- C.openStdin ls+  let led' = C.mapLedger (C.mapItem id id changeTransaction) led+      rend = fromJust $ C.ledger groupSpecs led'+  TIO.putStr rend
+ penny-reprint.hs view
@@ -0,0 +1,30 @@+module Main where++import qualified Penny.Copper as C+import qualified Penny.Copper.Render as R+import qualified Data.Text.IO as TIO+import System.Environment (getArgs)++help :: String+help = unlines+  [ "usage: penny-reprint FILE..."+  , "Tidies the formatting of a Penny ledger file."+  , "All memos, comments, and blank lines are preserved,"+  , "and the order of the transactions and postings is not changed."+  , "However, the whitespace that separates different elements"+  , "of a posting will change in order to tidy things up."+  , ""+  , "If no FILE, or if FILE is \"-\", read stanard input."+  , "Result is printed to standard output."+  ]++groupSpecs :: R.GroupSpecs+groupSpecs = R.GroupSpecs R.NoGrouping R.NoGrouping++main :: IO ()+main = do+  as <- getArgs+  l <- C.openStdin as+  case R.ledger groupSpecs l of+    Nothing -> error "could not render final ledger."+    Just x -> TIO.putStr x
+ penny-selloff.hs view
@@ -0,0 +1,623 @@+-- | penny-selloff++-- Steps++-- * In IO monad: read values given on command line.++-- * Parse command line. Fails if command line fails to parse.++-- * If help is requested, show help and exit successfully.++-- * In IO monad: read text of files given on command line. Fails if+-- there is an IO failure.++-- * Parse files given on command line. Fails if files fail to parse.++-- * Calculate balances of all accounts. Remove zero balances. This+-- step never fails.++-- * Find Proceeds account specified on command line. Split it into a+-- group name (the second sub-account) and a selloff label (the third+-- sub-account). Obtain the SelloffStockAmt, which is the debit+-- balance, and the SelloffCurrencyAmt, which is the credit+-- balance. Fails if the Proceeds account does not have exactly three+-- sub-accounts, or if the account does not have a balance with+-- exactly one debit balance and exactly one credit balance. Returns a+-- record with the group name, the selloff label, the selloff currency+-- amount, and the selloff stock amount. (Remember, an amount is a Qty+-- and a Commodity.)++-- * Filter account balances to find all Basis accounts with a+-- matching group name. Only accounts that have Basis as the first+-- sub-account AND a matching group name as the second sub-account are+-- further analyzed; all other accounts are discarded. Returns a list+-- of matching accounts, but only with a list of remaining+-- sub-accounts after the first and second sub-accounts, and the+-- balances of these accounts. This computation does not fail.++-- * For each basis account, parse out the purchase information. This+-- consists of a DateTime, which is the time of the purchase; the Qty+-- of stock purchased; and the Qty of currency paid for the+-- stock. Returns this information in a record. Fails if the basis+-- account does not have exactly two commodities in its balance, or if+-- there is not a credit balance matching the stock commodity, or if+-- there is not a debit balance matching the currency commodity, or if+-- the basis account has more than one remaining sub-account; or if+-- the DateTime cannot be parsed.++-- * For each basis account, compute the basis realization+-- information. First sort the basis accounts with the earliest+-- accounts coming first. Then for each account calculate how many+-- shares to debit and how much currency to credit. Do this in a+-- stateful transforming function that will transform the purchase+-- information into a pair with basis realization information and+-- purchase information. The state contains the number of shares+-- remaining that need to have their basis realized, and the total+-- cost of realized shares.++-- To calculate each basis realization, compare the number of shares+-- purchased with the number still remaining to be realized. If the+-- number purchased is less than or equal to the number remaining to+-- be realized, return a basis realization that realizes all the+-- shares purchased. If the number of shares purchased is more than+-- the number still remaining to be realized, then realize all the+-- shares that still need to be realized, and credit the cost+-- proportionally. If there are no shares remaining to be realized,+-- return no realization information at all.++-- Returns a list of pairs of basis realizations and purchase+-- information; purchases that have no basis realizations are+-- discarded. Also returns the total cost of shares sold. Fails if+-- there are still shares that have not had their basis realized+-- (i.e. the number of shares in the Proceeds account is greater than+-- the number of shares in the selloff group.)++-- * Compute the capital gain or loss. Take the difference between the+-- selloff currency quantity and the cost of shares sold. If the+-- selloff currency quantity is greater, there is a capital+-- gain. Record credits to an Income:Capital Gain account. If the cost+-- of shares sold is greater, there is a capital loss. Record debits+-- to an Expenses:Capital Loss account. To calculate the gain or loss+-- per purchase transaction, use the allocate function in the Qty+-- module. The target total is the total capital gain or loss, and+-- each allocation is the number of shares purchased in each+-- account. Returns a list of quantities (one for each capital gain or+-- loss, which corresponds to each purchase account) and an indication+-- of whether there was a capital gain or loss (this need only be+-- reported once.) Fails if the allocation fails.++-- * Create output transaction. This never fails (if it does, it is a+-- programmer error; just apply error.)++-- * In IO monad: Print output transaction.++module Main where++import qualified Control.Monad.Exception.Synchronous as Ex+import Control.Monad (when)+import qualified Control.Monad.Trans.State as St+import Control.Monad.Trans.Class (lift)+import Data.Foldable (toList)+import Data.List (find)+import Data.List.NonEmpty (NonEmpty((:|)))+import qualified Data.List.NonEmpty as NE+import Data.Maybe (isJust, mapMaybe, catMaybes, fromMaybe)+import Data.Text (pack)+import qualified Data.Text as X+import qualified Data.Text.IO as TIO+import qualified Penny.Lincoln.Balance as Bal+import qualified Penny.Lincoln.Transaction.Unverified as U+import qualified Penny.Cabin.Balance.Util as BU+import Penny.Cabin.Options (ShowZeroBalances(..))+import qualified Penny.Copper as Cop+import qualified Penny.Copper.Parsec as CP+import qualified Penny.Copper.Render as CR+import qualified Penny.Lincoln as L+import qualified Data.Map as M+import qualified System.Console.MultiArg as MA+import Text.Parsec as Parsec++groupingSpec :: CR.GroupSpecs+groupingSpec = CR.GroupSpecs CR.NoGrouping CR.NoGrouping++type Err = Ex.Exceptional Error++data Error+  = ParseFail MA.Error+  | NoInputArgs+  | ProceedsParseFailed Parsec.ParseError+  | NoInputFiles+  | LedgerParseError Cop.ErrorMsg+  | NoSelloffAccount+  | NotThreeSelloffSubAccounts+  | BadSelloffBalance+  | BadPurchaseBalance+  | BadPurchaseDate Parsec.ParseError+  | NotThreePurchaseSubAccounts [L.SubAccount]+  | BasisAllocationFailed+  | ZeroCostSharesSold+  | InsufficientSharePurchases+  | NoPurchaseInformation+  | SaleDateParseFailed Parsec.ParseError+  deriving Show++data ProceedsAcct = ProceedsAcct { unProceedsAcct :: L.Account }+  deriving Show++newtype InputFilename = InputFilename { unInputFilename :: String }+  deriving (Eq, Show)++loadFile :: InputFilename -> IO (L.Filename, Cop.FileContents)+loadFile (InputFilename fn) = fmap f (TIO.readFile fn)+  where+    f fc = (L.Filename . pack $ fn, Cop.FileContents fc)++data ParseResult+  = NeedsHelp+  | ParseResult ProceedsAcct [InputFilename]++data Flag+  = Help+  | PosArg String+  deriving (Show, Eq)++parseCommandLine :: [String] -> Err ParseResult+parseCommandLine ss =+  let os = [MA.OptSpec ["help"] "h" (MA.NoArg Help)]+  in case MA.simple MA.Intersperse os PosArg ss of+    Ex.Exception e -> Ex.Exception . ParseFail $ e+    Ex.Success g ->+      if isJust . find (== Help) $ g+      then return NeedsHelp+      else do+        let toArg a = case a of+              PosArg s -> Just s+              _ -> Nothing+        x:xs <- case mapMaybe toArg g of+          [] -> Ex.throw NoInputArgs+          r -> return r+        a <- Ex.mapException ProceedsParseFailed+              . Ex.fromEither+              $ Parsec.parse CP.lvl1Acct "" (pack x)+        when (null xs) $ Ex.throw NoInputFiles+        return $ ParseResult (ProceedsAcct a) (map InputFilename xs)++help :: String+help = "usage: penny-selloff PROCEEDS_ACCOUNT FILE..."++parseFiles+  :: [(L.Filename, Cop.FileContents)]+  -> Err Cop.Ledger+parseFiles ls = Ex.mapException LedgerParseError+  $ Cop.parse ls++calcBalances :: Cop.Ledger -> [(L.Account, L.Balance)]+calcBalances =+  let toTxn i = case i of+        Cop.Transaction t -> Just t+        _ -> Nothing+  in BU.flatten+      . BU.balances (ShowZeroBalances False)+      . map (L.Box ())+      . concatMap L.postFam+      . mapMaybe toTxn+      . Cop.unLedger++newtype Group = Group { unGroup :: L.SubAccount }+  deriving (Show, Eq)++newtype SaleDate = SaleDate { unSaleDate :: L.DateTime }+  deriving (Show, Eq)++newtype SelloffStock = SelloffStock { unSelloffStock :: L.Amount }+  deriving (Show, Eq)++newtype SelloffCurrency+  = SelloffCurrency { unSelloffCurrency :: L.Amount }+  deriving (Show, Eq)++data SelloffInfo = SelloffInfo+  { siGroup    :: Group+  , siSaleDate    :: SaleDate+  , siStock    :: SelloffStock+  , siCurrency :: SelloffCurrency+  } deriving Show++selloffInfo+  :: ProceedsAcct -> [(L.Account, L.Balance)] -> Err SelloffInfo+selloffInfo (ProceedsAcct pa) bals = do+  bal <- fmap snd+          . Ex.fromMaybe NoSelloffAccount+          . find ((== pa) . fst)+          $ bals+  (g, d) <- case L.unAccount pa of+    _ : s2 : s3 : [] -> return (s2, s3)+    _ -> Ex.throw NotThreeSelloffSubAccounts+  (sStock, sCurr) <- selloffStockCurr bal+  date <- fmap SaleDate+          . Ex.mapException SaleDateParseFailed+          . Ex.fromEither+          . Parsec.parse CP.dateTime ""+          $ (L.text d)+  return $ SelloffInfo (Group g) date sStock sCurr++selloffStockCurr :: L.Balance -> Err (SelloffStock, SelloffCurrency)+selloffStockCurr bal = do+  let m = L.unBalance bal+  when (M.size m /= 2) $ Ex.throw BadSelloffBalance+  let toPair (cy, bl) = case bl of+        Bal.Zero -> Nothing+        Bal.NonZero col -> Just (cy, col)+      ps = mapMaybe toPair . M.toList $ m+      findBal dc = Ex.fromMaybe BadSelloffBalance+                    . find ((== dc) . Bal.drCr . snd)+                    $ ps+  (cyStock, (Bal.Column _ qtyStock)) <- findBal L.Debit+  (cyCurr, (Bal.Column _ qtyCurr)) <- findBal L.Credit+  let sellStock = SelloffStock+        (L.Amount qtyStock cyStock+          (Just L.CommodityOnLeft) (Just L.SpaceBetween))+      sellCurr = SelloffCurrency+        (L.Amount qtyCurr cyCurr+          (Just L.CommodityOnLeft) (Just L.SpaceBetween))+  return (sellStock, sellCurr)+++basis :: L.SubAccount+basis = L.SubAccount . pack $ "Basis"++findBasisAccounts+  :: Group+  -> [(L.Account, L.Balance)]+  -> [([L.SubAccount], L.Balance)]+findBasisAccounts (Group g) = mapMaybe f+  where+    f ((L.Account a), b) = case a of+      s0 : s1 : s2 : ss -> if (s0 == basis) && (s1 == g)+                           then Just (s2:ss, b) else Nothing+      _ -> Nothing+++data PurchaseDate = PurchaseDate { unPurchaseDate :: L.DateTime }+  deriving Show++data PurchaseStockQty+  = PurchaseStockQty { unPurchaseStockQty :: L.Qty }+  deriving (Eq, Show)++data PurchaseCurrencyQty+  = PurchaseCurrencyQty { unPurchaseCurrencyQty :: L.Qty }+  deriving (Eq, Show)++data PurchaseInfo = PurchaseInfo+  { piDate :: PurchaseDate+  , piStockQty :: PurchaseStockQty+  , piCurrencyQty :: PurchaseCurrencyQty+  } deriving Show++purchaseInfo+  :: SelloffStock+  -> SelloffCurrency+  -> ([L.SubAccount], L.Balance)+  -> Err PurchaseInfo+purchaseInfo sStock sCurr (ss, bal) = do+  dateSub <- case ss of+    s1:[] -> return s1+    _ -> Ex.throw $ NotThreePurchaseSubAccounts ss+  date <- Ex.mapException BadPurchaseDate+          . Ex.fromEither+          . Parsec.parse CP.dateTime ""+          . L.text+          $ dateSub+  (stockQty, currQty) <- purchaseQtys sStock sCurr bal+  return $ PurchaseInfo (PurchaseDate date) stockQty currQty++purchaseQtys+  :: SelloffStock+  -> SelloffCurrency+  -> L.Balance+  -> Err (PurchaseStockQty, PurchaseCurrencyQty)+purchaseQtys (SelloffStock sStock) (SelloffCurrency sCurr) bal = do+  let m = L.unBalance bal+  when (M.size m /= 2) $ Ex.throw BadPurchaseBalance+  let toPair (cy, bl) = case bl of+        Bal.Zero -> Nothing+        Bal.NonZero col -> Just (cy, col)+      ps = mapMaybe toPair . M.toList $ m+      findBal dc = Ex.fromMaybe BadPurchaseBalance+                    . find ((== dc) . Bal.drCr . snd)+                    $ ps+  (cyStock, (Bal.Column _ qtyStock)) <- findBal L.Credit+  (cyCurr, (Bal.Column _ qtyCurr)) <- findBal L.Debit+  when (cyStock /= L.commodity sStock) $ Ex.throw BadPurchaseBalance+  when (cyCurr /= L.commodity sCurr) $ Ex.throw BadPurchaseBalance+  return (PurchaseStockQty qtyStock, PurchaseCurrencyQty qtyCurr)+++newtype RealizedStockQty+  = RealizedStockQty { unRealizedStockQty :: L.Qty }+  deriving (Eq, Show)++newtype RealizedCurrencyQty+  = RealizedCurrencyQty { unRealizedCurrencyQty :: L.Qty }+  deriving (Eq, Show)++newtype CostSharesSold+  = CostSharesSold { unCostSharesSold :: L.Qty }+  deriving (Eq, Show)++newtype StillToRealize+  = StillToRealize { unStillToRealize :: L.Qty }+  deriving (Eq, Show)++data BasisRealiztn = BasisRealiztn+  { brStockQty :: RealizedStockQty+  , brCurrencyQty :: RealizedCurrencyQty+  } deriving Show++-- | Realize an individual purchase account's basis. Fails if the+-- basis cannot be allocated.+stRealizeBasis+  :: PurchaseInfo+  -> Ex.ExceptionalT Error+     (St.State (Maybe CostSharesSold, Maybe StillToRealize))+     (Maybe (PurchaseInfo, BasisRealiztn))+stRealizeBasis p = do+  mayTr <- lift $ St.gets snd+  case mayTr of+    Nothing -> return Nothing+    Just (StillToRealize tr) -> do+      let sq = unPurchaseStockQty . piStockQty $ p+          pcq = unPurchaseCurrencyQty . piCurrencyQty $ p+      mayCss <- lift $ St.gets fst+      case L.difference tr sq of++        L.LeftBiggerBy tr' -> do+          let br = BasisRealiztn (RealizedStockQty sq)+                   (RealizedCurrencyQty pcq)+              css' = case mayCss of+                Nothing -> CostSharesSold pcq+                Just (CostSharesSold css) ->+                  CostSharesSold (L.add pcq css)+          lift $ St.put (Just css', Just (StillToRealize tr'))+          return (Just (p, br))++        L.RightBiggerBy unsoldStockQty -> do+          let alloced = L.allocate pcq (sq :| [unsoldStockQty])+              basisSold = case alloced of+                x :| (_ : []) -> x+                _ -> error "stRealizeBasis: error"+          let css' = case mayCss of+                Nothing -> CostSharesSold basisSold+                Just (CostSharesSold css) ->+                  CostSharesSold (L.add basisSold css)+              br = BasisRealiztn (RealizedStockQty tr)+                 (RealizedCurrencyQty basisSold)+          lift $ St.put (Just css', Nothing)+          return (Just (p, br))++        L.Equal -> do+          let br = BasisRealiztn (RealizedStockQty sq)+                (RealizedCurrencyQty pcq)+              css' = case mayCss of+                Nothing -> CostSharesSold pcq+                Just (CostSharesSold css) ->+                  CostSharesSold (L.add css pcq)+          lift $ St.put (Just css', Nothing)+          return (Just (p, br))++realizeBases+  :: SelloffStock+  -> [PurchaseInfo]+  -> Err ([(PurchaseInfo, BasisRealiztn)], CostSharesSold)+realizeBases sellStck ps = do+  let stReal = Just . StillToRealize . L.qty+               . unSelloffStock $ sellStck+      (exRs, (mayCss, mayTr)) = St.runState+        (Ex.runExceptionalT (mapM stRealizeBasis ps))+        (Nothing, stReal)+  rs <- exRs+  when (isJust mayTr) $ Ex.throw InsufficientSharePurchases+  css <- Ex.fromMaybe ZeroCostSharesSold mayCss+  return (catMaybes rs, css)++newtype CapitalChange = CapitalChange { unCapitalChange :: L.Qty }+  deriving Show++data WithCapitalChanges+  = WithCapitalChanges [(PurchaseInfo, BasisRealiztn, CapitalChange)]+      GainOrLoss+  | NoChange [(PurchaseInfo, BasisRealiztn)]+  deriving Show++data GainOrLoss = Gain | Loss deriving (Eq, Show)++capitalChange+  :: CostSharesSold+  -> SelloffCurrency+  -> [(PurchaseInfo, BasisRealiztn)]+  -> Err WithCapitalChanges+capitalChange css sc ls =+  let sellCurrQty = L.qty . unSelloffCurrency $ sc+      costQty = unCostSharesSold css+      mayGainLoss =+        case L.difference sellCurrQty costQty of+          L.LeftBiggerBy q -> Just (q, Gain)+          L.RightBiggerBy q -> Just (q, Loss)+          L.Equal -> Nothing+  in case mayGainLoss of+    Nothing -> return . NoChange $ ls+    Just (qt, gl) -> do+      nePurchs <- Ex.fromMaybe NoPurchaseInformation+                  . NE.nonEmpty $ ls+      let qtys = fmap (unPurchaseCurrencyQty . piCurrencyQty . fst)+                 nePurchs+          alloced = L.allocate qt qtys+      let mkCapChange (p, br) q = (p, br, CapitalChange q)+          r = toList $ NE.zipWith mkCapChange nePurchs alloced+      return $ WithCapitalChanges r gl++memo :: SaleDate -> L.Memo+memo (SaleDate sd) =+  let dTxt = CR.dateTime sd+      txt = pack "transaction created by penny-selloff for sale on "+            `X.append` dTxt+  in L.Memo [txt]++payee :: L.Payee+payee = L.Payee . pack $ "Realize gain or loss"++topLine :: SaleDate -> U.TopLine+topLine sd = (U.emptyTopLine (unSaleDate sd))+             { U.tPayee = Just payee+             , U.tMemo = Just . memo $ sd+             }++basisOffsets+  :: SelloffInfo+  -> PurchaseDate+  -> BasisRealiztn+  -> (U.Posting, U.Posting)+basisOffsets s pd p = (po enDr, po enCr)+  where+    ac = L.Account [basis, grp, dt]+    grp = unGroup . siGroup $ s+    dt = dateToSubAcct . unPurchaseDate $ pd+    enDr = L.Entry L.Debit+           (L.Amount (unRealizedStockQty . brStockQty $ p)+              (L.commodity . unSelloffStock . siStock $ s)+              (Just L.CommodityOnLeft) (Just L.SpaceBetween))+    enCr = L.Entry L.Credit+           (L.Amount (unRealizedCurrencyQty . brCurrencyQty $ p)+              (L.commodity . unSelloffCurrency . siCurrency $ s)+              (Just L.CommodityOnLeft) (Just L.SpaceBetween))+    po en = (U.emptyPosting ac)+            { U.pEntry = Just en }++dateToSubAcct :: L.DateTime -> L.SubAccount+dateToSubAcct = L.SubAccount . CR.dateTime++income :: L.SubAccount+income = L.SubAccount .  pack $ "Income"++capGain :: L.SubAccount+capGain = L.SubAccount . pack $ "Capital Gain"++expense :: L.SubAccount+expense = L.SubAccount . pack $ "Expenses"++capLoss :: L.SubAccount+capLoss = L.SubAccount . pack $ "Capital Loss"++capChangeAcct+  :: GainOrLoss+  -> SelloffInfo+  -> PurchaseInfo+  -> L.Account+capChangeAcct gl si p = L.Account $ case gl of+  Gain -> [income, capGain, grp, sd, pd]+  Loss -> [expense, capLoss, grp, sd, pd]+  where+    grp = unGroup . siGroup $ si+    sd = dateToSubAcct . unSaleDate . siSaleDate $ si+    pd = dateToSubAcct . unPurchaseDate . piDate $ p++capChangeEntry+  :: GainOrLoss+  -> SelloffCurrency+  -> CapitalChange+  -> L.Entry+capChangeEntry gl sc cc = L.Entry dc (L.Amount qt cy sd sb)+  where+    dc = case gl of+      Gain -> L.Credit+      Loss -> L.Debit+    cy = L.commodity . unSelloffCurrency $ sc+    qt = unCapitalChange cc+    sd = Just L.CommodityOnLeft+    sb = Just L.SpaceBetween++capChangePstg+  :: SelloffInfo+  -> GainOrLoss+  -> CapitalChange+  -> PurchaseInfo+  -> U.Posting+capChangePstg si gl cc p =+  (U.emptyPosting ac)+  { U.pEntry = Just en }+  where+    ac = capChangeAcct gl si p+    en = capChangeEntry gl (siCurrency si) cc++proceeds :: L.SubAccount+proceeds = L.SubAccount . pack $ "Proceeds"++proceedsPstgs+  :: SelloffInfo+  -> (U.Posting, U.Posting)+proceedsPstgs si = (po dr, po cr)+  where+    po en = (U.emptyPosting ac) { U.pEntry = Just en }+    ac = L.Account [proceeds, gr, dt]+    gr = unGroup . siGroup $ si+    dt = dateToSubAcct . unSaleDate . siSaleDate $ si+    dr = L.Entry L.Debit (unSelloffCurrency . siCurrency $ si)+    cr = L.Entry L.Credit (unSelloffStock . siStock $ si)+++mkTxn+  :: SelloffInfo+  -> WithCapitalChanges+  -> L.Transaction+mkTxn si wcc = Ex.resolve err exTxn+  where+    err = const $ error "mkTxn: making transaction failed"+    exTxn = L.transaction $ L.Family tl p1 p2 ps+    tl = topLine . siSaleDate $ si+    (p1, p2) = proceedsPstgs si+    ps = case wcc of+      NoChange infoRlzns -> concatMap f infoRlzns+        where+          f (p, br) =+            let (b1, b2) = basisOffsets si (piDate p) br+            in [b1, b2]+      WithCapitalChanges trips gl -> concatMap f trips+        where+          f (p, br, cc) = [b1, b2, c]+            where+              (b1, b2) = basisOffsets si (piDate p) br+              c = capChangePstg si gl cc p++makeOutput+  :: ProceedsAcct+  -> [(L.Filename, Cop.FileContents)]+  -> Err X.Text+makeOutput pa ps = do+  bals <- fmap calcBalances $ parseFiles ps+  si <- selloffInfo pa bals+  let basisAccts = findBasisAccounts (siGroup si) bals+  purchInfos <- mapM (purchaseInfo (siStock si) (siCurrency si))+                basisAccts+  (purchBases, css) <- realizeBases (siStock si) purchInfos+  wcc <- capitalChange css (siCurrency si) purchBases+  return+    . (`X.snoc` '\n')+    . fromMaybe (error "makeOutput: transaction did not render")+    . CR.transaction groupingSpec+    . mkTxn si+    $ wcc+++main :: IO ()+main =+  MA.getArgs+  >>= Ex.switch (error . show) handleParseResult . parseCommandLine+++handleParseResult :: ParseResult -> IO ()+handleParseResult pr = case pr of+  NeedsHelp -> putStrLn help+  ParseResult pa fs ->+    mapM loadFile fs+    >>= Ex.switch (error . show) TIO.putStr . makeOutput pa
− penny.1
@@ -1,599 +0,0 @@-.TH penny 1--.SH NAME-penny - report on postings in financial ledger--.SH SYNOPSIS-penny [posting filter expression] report name [report options] FILE...--.SH DESCRIPTION--The-.B penny-program prepares reports based upon the-.B postings-in your ledger file. Each transaction has at least two postings. After-.B penny-verifies that your ledger file is-.I balanced-(that is, every transaction has debits and credits that are equal)-.B penny-splits each transaction into its component postings. After this point,-.B penny-for the most part deals only with postings, not with transactions.--You may specify a-.I filter expression-that determines which postings are ultimately used for the report. The-filter expression may also contain additional options that perform-tasks other than filtering. For example, you may specify how to-perform text matching (for instance, you may want to use regular-expressions) or you may specify that you want to sort the postings (by-default, they are not sorted and are left in the order in which they-were found in the input ledger).--Next you must specify a-.IR report .-Currently there are two reports: -.BR postings ,-which shows information about each posting, like a checkbook register, and-.BR balance ,-which shows you information about the balance of each account. The-report is based only upon the postings that the filter expression-returned.--You may then specify options pertaining to the report. The-.BR postings-report takes many options that format its output and that specify-which postings are shown in the report; however, postings that the-filter expression returned that are not shown in the report still-affect the running balance. This can be useful if, for example, you-want to see the total balance in your bank account and how some-transactions affect it but you do not want to see all transactions-since the beginning of time. The-.BR balance-report takes many fewer options.--Finally you may specify one or more files from which to draw the-data. If you do not specify a file, standard input is used.--The report is always printed to standard output. By default,-.BR penny-sees whether its standard output is a terminal. If so,-.BR penny 's-output is sized to fit the width of the-.BR COLUMNS-environment variable (if it is set) and is printed in color. If-standard output is not a terminal, by default the output is not in-color and will be 80 characters wide. This behavior can be overridden-on the command line.--Entering the data into the ledger files is your responsibility;-.B penny-will never modify this data. To see how to enter data into the file,-see the file-.BR samplePennyFile ,-which is included inside the package for-.BR penny-bin .--.SH COMPARERS--Many options perform comparisons; for example, the-.B --date-option compares postings to a date you specify to determine-which postings to keep and which to reject. Where-.I comparer-appears below, you must supply one of the following strings.-You will need to quote many of them, because many of these-characters will have special meaning for your shell.--.TS-tab(:);-l l l-- - --lB lB l.-Primary form:Alternate form:Comparison performed-<::Less than-<=::Less than or equal to-==:T{-=-T}:Equals->::Greater than->=::Greater than or equal to-/=:!=:Not equal to-.TE--.SH POSTING FILTERS-.SS Dates--.TP-.BI "--date | -d " "comparer timespec"--The date of the posting must be within the time frame given.-.BR date-is the same format as dates in the-.B penny-file and-is either a date alone, such as-.BR 2012-04-25 ,-or a date and a time, such as-.BR "2012-04-25 14:25 -0400" .--.TP-.B --current-Same as-.BI "--date <= " "right now"--.SS Serials-These perform matching based on serials. For more on serials,-see the section-.B SERIALS-below. Each option takes the form-.IR "option comparer number" .-.TP-.B --globalTransaction-.TQ-.B --revGlobalTransaction-.TQ-.B --globalPosting-.TQ-.B --revGlobalPosting-.TQ-.B --fileTransaction-.TQ-.B --revFileTransaction-.TQ-.B --filePosting-.TQ-.B --revFilePosting--.SS Pattern matching--These options allow you to filter postings by specifying a pattern-that must match a particular component of the posting. By default the simple-.I within-matcher is used, and matches are case-insensitive.--.TP-.BI "--account | -a " pattern-The sub-accounts of the account are separated with colons, and the-match succeeds if the pattern matches this entire colon-separated-name.--.TP-.BI "--account-level " "number pattern"-The account is separated into sub-accounts, which are numbered-beginning at zero. The match succeeds if the posting has a sub-account-numbered at the given level, and if that sub account matches the given-pattern.--.TP-.BI --account-any " pattern"-The account is separated into sub-accounts. The match succeeds if the-pattern matches any of a posting's sub-accounts.--.TP-.BI  "--payee | -p " pattern-Succeeds if the pattern matches the posting's payee. If the posting-has no payee, the payee of the parent transaction is used (if there is-one).--.TP-.BI "--tag | -t " pattern-Succeeds if any one of the posting's tags matches the given pattern.--.TP-.BI --number " pattern"-Succeeds if the posting's number matches the given pattern. (This is-the number that you specify in parentheses in your ledger file, not-the line number or any of the serial numbers.)  If the posting has no-number, the number of the parent transaction is used (if there is-one).--.TP-.BI --flag " pattern"-Succeeds if the posting's flag matches the given pattern. If the-posting has no number, the flag of the parent transaction is used-(if there is one).--.TP-.BI --commodity " pattern"-The commodity is separated into colon-separated sub-commodities, and-the test succeeds if the pattern matches the full colon-separated-commodity name.--.TP-.BI "--commodity-level " "number pattern"-The commodity is separated into sub-commodities, which are numbered-beginning at zero. The match succeeds if the posting has a sub-commodity-numbered at the given level, and if that sub-commodity matches the given-pattern.--.TP-.BI "--commodity-any " pattern-The commodity is separated into sub-commodities. The match succeeds if-the pattern matches any of a posting's sub-commodities.--.TP-.BI "--posting-memo " pattern-The posting memo must match the given pattern.--.TP-.BI "--transaction-memo " pattern-The transaction memo must match the given pattern.--.SS Other posting characteristics--.B --debit-The entry must be a debit.--.B --credit-The entry must be a credit.--.BI --qty " comparer number"-The entry's quantity must fall within the given range.--.SS Operators--Each of the options above is a single operand. If you have multiple-operands, you must join them together using operators.  These-operators are specified below, from highest to lowest precedence. All-operators are left associative.--.TP-.BI "--open " expr " --close"-Force precedence using parentheses. Enclose a complete expression-between the-.B --open-and-.B --close-options.--.TP-.BI "--not " expr-True if-.I expr-is false.--.TP-.IB expr1 " --and " expr2-True if-.I expr1-and-.I expr2-are both true.--.TP-.IB expr1 " --or " expr2-True if-.I expr1-or-.I expr2-is true.--.SS Options affecting patterns--These options affect how patterns are interpreted. The order of the-.B penny-command line is significant; each of these options only affects-patterns that appear after it on the command line.--.TP-.B "-i | --case-insensitive"-Patterns are case insensitive (default)--.TP-.B "-I | --case-sensitive"-Patterns are case sensitive--.TP-.B --within-Use the "within" matcher (default), which matches if the pattern given-appears anywhere within the target text. This is a simple-letter-for-letter match, not a regular expression, though its case-sensitivity is affected by the-.B --case-insensitive-and-.B --case-sensitive-options.--.TP-.B --pcre-Use the "pcre" matcher, which uses Perl-compatible regular expressions (see-.BR pcresyntax "(1) and " pcrepattern (1))--.TP-.B --posix-Use the "posix" matcher, which uses POSIX regular expressions (see-.BR regex (7))--.TP-.B --exact-Use the "exact" matcher, which matches if the given pattern is a-letter-for-letter match of the target text, with case sensitivity-determined by the-.B --case-insensitive-and-.B --case-sensitive-options.--.SH Removing postings after sorting and filtering--.TP-.BI "--head " n-Keep only the first-.I n-postings.--.TP-.BI "--tail " n-Keep only the last-.I n-postings.--.SH Sorting--.TP-.BI "--sort | -s " key-Sorts postings according to a key. Use multiple-.B --sort-options to sort by more than one key. Valid keys are: payee, date,-flag, number, account, drCr, qty, commodity, postingMemo,-transactionMemo.--The postings are sorted in ascending order if the first letter of the-key is lowercase; descending order if the first letter of the key is-uppercase.--.SH POSTINGS REPORT--The-.B postings-report, or-.B pos-for short, shows postings in order with a running balance. This report-takes all the options shown above in the categories from "Posting-filters" to "Removing postings after sorting and filtering." These-options affect which postings are shown in the report. Postings that-are not shown in the report but which were not filtered out in the-filtering stage still affect the report's running balance.--Additional options for the-.B postings-report:--.SS Additional serial filtering options-These options affect which postings are shown. Postings that were not-filtered in the filtering stage but that are not shown still affect-the running balance. In addition to using the same options that are-used for filtering, these additional options are available that are-based on some additional serials. They take the form-.IR "option comparer number " .-For more information on serials, see the-.B SERIALS-section below.--.TP-.B --filtered-filtered serial, forward component-.TQ-.B --revFiltered-filtered serial, reverse component-.TQ-.B --sorted-sorted serial, forward component-.TQ-.B --revSorted-sorted serial, reverse component--.SS Other additional options for the balance report--.TP-.BR --color " yes|no|auto|256"-Controls which colors are shown.-.B yes-always shows 8 colors.-.B no-never shows colors.-.B auto-first examines whether standard output is a terminal. If standard output is not a terminal,-.B penny-does not show any colors. If standard output is a terminal, examines-the value of the TERM environment variable. If TERM is-"xterm-256color", uses 256 colors. If TERM is any other value, or if-TERM is not set, uses 8 colors.--.TP-.BR --background " light|dark"-Pick a color scheme for a light or dark terminal background (by-default, a dark color scheme is used.)--.TP-.BI --width " num"-Gives a hint for roughly how wide the report should be, in-columns. Sometimes the report will be narrower than the specified-width; however, it will never be wider than the specified width.--.TP-.BI "--show" " field"-.TQ-.BI "--hide" " field"-Show or hide fields from the displayed report. Fields are displayed in-a fixed order, which is the same as the order that the table below is-in. Fields with an asterisk are shown by default.--.\" Do not precede the empty fields in the table with any spaces. This-.\" will cause GNU tbl to segfault.-.\" See-.\" http://lists.gnu.org/archive/html/groff/2010-11/msg00014.html-.\" Apparently the bug has been fixed but who knows how long that will-.\" take to propogate.--.TS-tab(:);-lB lB l.-:globalTransaction:globalTransaction serial, forward component-:revGlobalTransaction:globalTransaction serial, reverse component-:globalPosting:globalPosting serial, forward component-:revGlobalPosting:globalPosting serial, reverse component-:fileTransaction:fileTransaction serial, forward component-:revFileTransaction:fileTransaction serial, reverse component-:filePosting:filePosting serial, forward component-:revFilePosting:filePosting serial, reverse component-:filtered:filtered serial, forward component-:revFiltered:revFiltered serial, reverse component-:sorted:sorted serial, forward component-:revSorted:sorted serial, reverse component-:visible:visible serial, forward component-:revVisible:visible serial, reverse component-:lineNum:line number (starting from 1)-*:date:transaction's date-:flag:posting or transaction flag-:number:posting or transaction's number-*:payee:posting or transaction's payee-*:account:posting's account-*:postingDrCr:whether the posting is a debit or credit-*:postingCmdty:posting's commodity-*:postingQty:posting's quantity-*:totalDrCr:whether the running total is a debit or credit-*:totalCommodity:commodity of the running total-*:totalQty:quantity of the running total-:tags:posting's tags-:memo:the posting and transaction memo-:filename:filename where the posting came from-.TE--.TP-.B --show-all-Show all fields--.TP-.B --hide-all-Hide all fields--.TP-.B --show-zero-balances-Shows the balance of all commodities in the-.IR totalDrCr ", " totalCommodity ", and " totalQty-fields, even if that balance is zero.--.TP-.B --hide-zero-balances-Hides the balance of commodities in the-.IR totalDrCr ", " totalCommodity ", and " totalQty-fields if they are zero (default)--.SH BALANCE REPORT-The-.B balance-report summarizes the balances in each account that is represented in-the postings that remain after the filtering specifications are-carried out. You can use sorting specifications with the balance-report, but they will have no effect. The accounts are shown-hierarchically. This report is also useful for converting from one-commodity to another.--The-.B balance-report accepts the following options:--.TP-.BI --color " yes|no|auto|256"-Controls how many colors to display:--.TS-tab(:);-lB l.-yes:always show 8 colors-no:never show any colors-auto:T{-show 8 or 256 colors, but only if standard output is a terminal.-If standard output is a terminal, shows 256 colors if the value-of the TERM environment variable is \fIxterm-256color\fR;-otherwise, shows 8 colors.-T}-256:always show 256 colors-.TE--.TP-.BI --background " light|dark"-Use appropriate color scheme for the given terminal background--.TP-.BI --convert " commodity dateTime"-Convert all commodities to the given commodity using their price as of-the given date and, optionally, time.-.IR commodity " and " dateTime-follow the same format as the respective items in a ledger file. If-any commodity does not have a matching price,-.B penny-will fail and give you an error message.--.TP-.BI -c " commodity"-Same as-.BI "--convert " "commodity [right now]"--.SH SERIALS--Each posting is assigned several-.IR serials ,-each of which is a pair of ordinal numbers. The first number in the-pair, or-.IR "forward component" ,-is assigned by numbering the transactions or postings from-beginning to end beginning at zero, while the second number in the-pair, or-.IR "reverse component" ,-is assigned by numbering the transactions or postings from end to-beginning, beginning at zero. Here are all the serials that are-assigned to each posting.--.TP-.B globalTransaction-All transactions are numbered in order, beginning with those in the-first file specified on the command line and ending with the last-file. Occurs before the transactions are split into postings.--.TP-.B fileTransaction-Like-.IR globalTransaction ,-but numbering restarts with each new file.--.TP-.B globalPosting-All postings are numbered in order, beginning with those in the first-file specified on the command line and ending with the last file.--.TP-.B filePosting-Like-.IR globalPosting ,-but numbering restarts with each new file.--.TP-.B filtered-Postings are numbered after first removing the postings as specified-by the filtering options specified on the command line.--.TP-.B sorted-Postings are numbered in order after the sorting options have been-applied, which occurs after the filtering options have been applied.--.TP-.B visible-Postings are numbered in order after removing the postings as-specified by the options to the-.I postings-report. (Does not apply to the-.I balance-report.)--.SH SEE ALSO-The file-.B samplePennyFile-in the tarball for the-.B penny-bin-package shows you how to write a ledger file.
penny.hs view
@@ -2,6 +2,294 @@  import Penny +-- | This type contains settings for all the reports, as well as+-- default settings for the global options. Some of these can be+-- overridden on the command line.+defaults :: Runtime -> Defaults+defaults rt = Defaults+  { caseSensitive = False+    -- ^ Whether the matcher is case sensitive by default++  , matcher = Within+    -- ^ Which matcher to use. Your choices:+    -- Within+    -- Exact+    -- TDFA (regular expressions, POSIX semantics)+    -- PCRE (regular expressions, PCRE semantics)++  , colorToFile = False+    -- ^ Use colors when standard output is not a terminal?++  , defaultScheme = Just schemeDark+    -- ^ Default color scheme. If Nothing, there is no default color+    -- scheme. If there is no default color scheme and the user does+    -- not pick one on the command line, no colors will be used.++  , additionalSchemes = [schemeDark, schemeLight, schemePlain]+    -- ^ Additional color schemes the user can pick from on the+    -- command line.++  , sorter = [(Date, Ascending)]+    -- ^ Postings are sorted in this order by default. For example, if+    -- the first pair is (Date, Ascending), then postings are first+    -- sorted by date in ascending order. If the second pair is+    -- (Payee, Ascending), then postings with the same date are then+    -- sorted by payee.+    --+    -- 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.++  , balanceShowZeroBalances = False+    -- ^ Show zero balances in the balance report? If True, show them;+    -- if False, hide them.++  , balanceOrder = Ascending+    -- ^ Whether to sort the accounts in ascending or descending order+    -- by account name in the balance report. Your choices: Ascending+    -- or Descending.++  , convertShowZeroBalances = False+    -- ^ Show zero balances in the convert report? If True, show them;+    -- if False, hide them.++  , convertTarget = AutoTarget+    -- ^ The commodity to which to convert the commodities in the+    -- convert report. Your choices:+    --+    -- AutoTarget - selects a target commodity automatically, based on+    -- which commodity is the most common target commodity in the+    -- prices in your ledger files. If there is a tie for most common+    -- target commodity, the target that appears later in your ledger+    -- files is used.+    --+    -- ManualTarget CMDTY_NAME - always use the given commodity.++  , convertOrder = Ascending+    -- ^ Sort the convert report in ascending or descending order.++  , convertSortBy = SortByName+    -- ^ Sort by account or by quantity in the convert report. Your+    -- choices:+    --+    -- 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.++  , postingsWidth = widthFromRuntime rt+    -- ^ The postings report is roughly this wide by default. Use+    -- @widthFromRuntime rt@ if you want to use the current width of+    -- your terminal.++  , postingsShowZeroBalances = False+    -- ^ Show zero balances in the postings report? If True, show+    -- them; if False, hide them.++  , 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+    -- necessary in order to help the report fit within the allotted+    -- width (see postingsWidth). Account names are only shortened as+    -- much as is necessary for them to fit; however, each sub-account+    -- name will not be shortened any more than the amount given here.+    --+    -- This number should be a non-negative integer.++  , postingsPayeeAllocation = 40+    -- ^ postingsPayeeAllocation and postingsAccountAllocation+    -- determine how much space is allotted to the payee and account+    -- fields in the postings report. These fields are variable+    -- width. After space for most other fields is allotted, space is+    -- allotted for these two fields. The two fields divide the space+    -- proportionally depending on postingsPayeeAllocation and+    -- postingsAccountAllocation. For example, if+    -- postingsPayeeAllocation is 60 and postingsAccountAllocation is+    -- 40, then the payee field gets 60 percent of the leftover space+    -- and the account field gets 40 percent of the leftover space.+    --+    -- Both postingsPayeeAllocation and postingsAccountAllocation+    -- must be positive integers; if either one is less than 1, your+    -- program will crash at runtime.++  , postingsAccountAllocation = 60+    -- ^ See postingsPayeeAllocation above for an explanation++  , postingsSpacers = spacers+    -- ^ Determines the number of spaces that appears to the right of+    -- each named field; for example, sPayee indicates how many spaces+    -- will appear to the right of the payee field. Each field of the+    -- Spacers should be a non-negative integer (although currently+    -- the absolute value of the field is taken.)+  }++-- | Controls which fields appear in the report by default.+fields :: Fields Bool+fields = Fields+  { fGlobalTransaction = False+  , fRevGlobalTransaction = False+  , fGlobalPosting = False+  , fRevGlobalPosting = False+  , fFileTransaction = False+  , fRevFileTransaction = False+  , fFilePosting = False+  , fRevFilePosting = False+  , fFiltered = False+  , fRevFiltered = False+  , fSorted = False+  , fRevSorted = False+  , fVisible = False+  , fRevVisible = False+  , fLineNum = False+  , fDate = True+  , fFlag = False+  , fNumber = False+  , fPayee = True+  , fAccount = True+  , fPostingDrCr = True+  , fPostingCmdty = True+  , fPostingQty = True+  , fTotalDrCr = True+  , fTotalCmdty = True+  , fTotalQty = True+  , fTags = False+  , fMemo = False+  , fFilename = False+  }++-- | Controls how many spaces appear to the right of each named field.+spacers :: Spacers Int+spacers = Spacers+  { sGlobalTransaction = 1+  , sRevGlobalTransaction = 1+  , sGlobalPosting = 1+  , sRevGlobalPosting = 1+  , sFileTransaction = 1+  , sRevFileTransaction = 1+  , sFilePosting = 1+  , sRevFilePosting = 1+  , sFiltered = 1+  , sRevFiltered = 1+  , sSorted = 1+  , sRevSorted = 1+  , sVisible = 1+  , sRevVisible = 1+  , sLineNum = 1+  , sDate = 1+  , sFlag = 1+  , sNumber = 1+  , sPayee = 4+  , sAccount = 1+  , sPostingDrCr = 1+  , sPostingCmdty = 1+  , sPostingQty = 1+  , sTotalDrCr = 1+  , sTotalCmdty = 1+  }++-- | The light color scheme. You can change various values below to+-- affect the color scheme.+schemeLight :: Scheme+schemeLight = Scheme "light" "for light background terminals"+              lightLabels++lightLabels :: Labels (EvenAndOdd TextSpec)+lightLabels = Labels+  { debit = EvenAndOdd { eoEven = lightDebit lightEvenTextSpec+                       , eoOdd = lightDebit lightOddTextSpec }+  , credit = EvenAndOdd { eoEven = lightCredit lightEvenTextSpec+                        , eoOdd = lightCredit lightOddTextSpec }+  , zero = EvenAndOdd { eoEven = lightZero lightEvenTextSpec+                      , eoOdd = lightZero lightOddTextSpec }+  , other = EvenAndOdd { eoEven = lightEvenTextSpec+                       , eoOdd = lightOddTextSpec }+  }++lightEvenTextSpec :: TextSpec+lightEvenTextSpec = defaultTextSpec++lightOddTextSpec :: TextSpec+lightOddTextSpec = switchBackground color8_b_default+                   color256_b_255 lightEvenTextSpec++lightDebit :: TextSpec -> TextSpec+lightDebit = switchForeground color8_f_magenta color256_f_52++lightCredit :: TextSpec -> TextSpec+lightCredit = switchForeground color8_f_cyan color256_f_21++lightZero :: TextSpec -> TextSpec+lightZero = switchForeground color8_f_black color256_f_0++-- | The dark color scheme. You can change various values below to+-- affect the color scheme.+schemeDark :: Scheme+schemeDark = Scheme "dark" "for dark background terminals"+              darkLabels++darkLabels :: Labels (EvenAndOdd TextSpec)+darkLabels = Labels+  { debit = EvenAndOdd { eoEven = darkDebit darkEvenTextSpec+                       , eoOdd = darkDebit darkOddTextSpec }+  , credit = EvenAndOdd { eoEven = darkCredit darkEvenTextSpec+                        , eoOdd = darkCredit darkOddTextSpec }+  , zero = EvenAndOdd { eoEven = darkZero darkEvenTextSpec+                      , eoOdd = darkZero darkOddTextSpec }+  , other = EvenAndOdd { eoEven = darkEvenTextSpec+                       , eoOdd = darkOddTextSpec }+  }++darkEvenTextSpec :: TextSpec+darkEvenTextSpec = defaultTextSpec++darkOddTextSpec :: TextSpec+darkOddTextSpec = switchBackground color8_b_default+                   color256_b_235 darkEvenTextSpec++darkDebit :: TextSpec -> TextSpec+darkDebit = switchForeground color8_f_magenta color256_f_208++darkCredit :: TextSpec -> TextSpec+darkCredit = switchForeground color8_f_cyan color256_f_45++darkZero :: TextSpec -> TextSpec+darkZero = switchForeground color8_f_white color256_f_15++-- | Plain scheme has no colors at all.+schemePlain :: Scheme+schemePlain = Scheme "plain" "uses default terminal colors"+              plainLabels++plainLabels :: Labels (EvenAndOdd TextSpec)+plainLabels = Labels+  { debit = EvenAndOdd defaultTextSpec defaultTextSpec+  , credit = EvenAndOdd defaultTextSpec defaultTextSpec+  , zero = EvenAndOdd defaultTextSpec defaultTextSpec+  , other = EvenAndOdd defaultTextSpec defaultTextSpec+  }+ main :: IO ()-main = defaultPenny utcDefault periodComma+main = runPenny defaults 
− samplePennyFile
@@ -1,300 +0,0 @@-# Sample Penny file. Penny is a double-entry accounting-# system. Penny uses traditional accounting terminology,-# which means that every transaction will have "debits"-# and "credits". In every single transaction, debits-# must equal credits, or this file will not parse-# and Penny will quit with an error.--# Penny has no concept of negative numbers. All numbers-# are positive or zero.--# To learn double-entry accounting, pick up a used accounting-# textbook at your favorite online used book retailer.-# They can be had for under 10 U.S. dollars, as college-# students cast aside old editions that are perfectly good.-# This website is also excellent and is free:-# http://www.principlesofaccounting.com/--# As you can see, single line comments begin with-# a hash mark, like a shell script. Currently there-# is no way to make a multi-line comment.--# Here is a sample transaction to start things off.--2011/12/15 Salts Deluxe-    Assets:Checking                Dr $2000.00-        'Payday-    Income:Salary                  Cr $1500.00-    Expenses:Taxes                 Cr $500.00--; This is a transaction memo. It is associated-; with the transaction, and the penny program lets you-; search their contents. Transaction memos appear immediately-; before a transaction and their lines begin with-; semicolons.-2012/01/01 Payee Name-    Assets:Checking                 Cr $5.00-    Expenses:Food--# Tags are only one word. Precede each tag with an asterisk.-# Postings can have one or more "tags". These allow you to-# classify postings by tag as well as by account. For example-# you might take a vacation. Various postings might be scattered-# across multiple accounts (Expenses:Food, Expenses:Gas, etc.)-# but you can give them all a *vacation tag.--# Transactions and postings can have an optional flag.-# The flag is one or more letters.-# It is wrapped in square brackets.-# For instance, this can be used to indicate a posting-# is cleared.--# Penny uses spaces to separate different parts of the-# transaction. If you want to include spaces in the name-# of an account, you must enclose it in curly braces-# as shown in the next transaction.--2012/01/02 [C] (302) GreenGas-    {Liabilities:Credit Card}             Cr $5.00-    Expenses:Car *gas *trips--# Though the posting lines in this file are nicely indented for-# readability, Penny does not require this.--; This transaction has a posting with a different payee.-; You can do this by enclosing the payee in greater than-; and less than signs as shown.-; The last posting has a memo.-; These begin with an apostrophe. As with posting lines, the-; indentation is nice for readability, but it is not required.-; (It does have to be on a new line though.)-2012/01/03 Flathead-    [R] (905) <Luvapet Vet> Expenses:Pet    Dr $10.00-    Assets:Checking               Cr $5.00-    [*] (2313) {Liabilities:Credit Card}       Cr $5.00-       'This interest rate is too high--# Flags can be more than one letter. Actually they can include-# any character except ']'.-2012/01/06 [Flagger] Flagged Up Transaction-    Expenses:Flags                      Dr $200.00-    Assets:Checking                     Cr $200.00---# By default, the digit grouping character is the comma and-# the radix point character is the period. You can configure these-# (somewhat) easily by making changes to the source code (not-# as bad as it sounds, believe it or not.) The digit grouping-# character is really for your benefit; Penny mostly ignores-# it. The radix point is of course quite significant.--# Commodity names may be before or after the commodity amount.-# They can be separated from the amount by one optional space.--# To use multiple commodities, use a trading account.-# The best explanation of trading accounts is here:-# http://www.mscs.dal.ca/~selinger/accounting/tutorial.html-# For this transaction, the debits and credits all add up to zero,-# as they always must. If they do not, parsing of the ledger-# file will fail.--2012/01/05 Stockbroker-    Assets:Brokerage              Dr 1,000 LUV-    Assets:Current:Checking       Cr $ 2,000.00-    {Income:Brokerage Trading Account}  Cr 1,000 LUV-    {Income:Brokerage Trading Account}  Dr $ 2,000.00--# Prices allow you to specify how to convert one commodity-# to another. Prices do not affect the way any of your-# transactions are parsed. Even if you specify prices,-# you still must ensure that the debits equal the credits-# in every transaction; if they do not, the parser will fail.--# The balance report uses price information for the --convert and -c-# options. This allows you to convert all commodities in the file to a-# single commodity. Typically you would use this to see what the-# balance of all your accounts is in your home currency.--# Prices specify that ONE unit of the given commodity-# equals the given quantity of the other commodity. Here-# are some examples.--@ 2012/01/05 17:55:55 -0500 LUV $9.62-@ 2012/01/05 Gold $1738.35--# Prices only work one way. For instance the first-# price tells you how many dollars one euro is worth, and allows-# reports to convert from euro to dollars. To convert from-# dollars to euro, you must specify another price.-@ 2012/01/05 € $1.3207-@ 2012/01/05 $ €0.7572-@ 2012/01/05 ¥ 0.6418 INR--; Commodities can have several components in their names,-; just as accounts can. Subsequent parts can be viewed as-; sub-commodities. This can be handy if different commodities-; have something in common and you want to group them together.-; For example, let's say you bought shares in T in-; 2007 and in 2012. For tax purposes you want to track them separately-; in your reports (this can matter for capital gains taxes) yet-; shares in T have the same price whether you bought them in 2007-; or in 2012. So you can have two different symbols, as the following-; two transactions show:-2007/02/01 Bought stock-    Assets:Brokerage               Dr 100 T:2007-      '$37.75 per share-    Assets:Checking                Cr $3775-    {Income:Brokerage Trading}       Cr 100 T:2007-    {Income:Brokerage Trading}       Dr $3775--2012/01/12 Bought stock-    Assets:Brokerage               Dr 100 T:2012-      '$30.12 per share-    Assets:Checking                Cr $3012-    {Income:Brokerage Trading}       Cr 100 T:2012-    {Income:Brokerage Trading}       Dr $3012--# and some prices to go along with the T example. Because no-# different prices are specified for T:2012 and for T:2007,-# the balance report will use the price for T for both these-# commodities if you use the --convert or -c options.-@ 2007/06/01 T $40.53-@ 2012/01/27 T $29.16--# As the examples above show with the currency symbols,-# Penny works fine with Unicode.-# Your ledger file should be encoded in your system's default-# encoding. On my system this is UTF-8, as it is on most-# recent Linux systems, but some systems use other encodings.-# I know that Slackware was recently using ISO-8859-1. Non-Unicode-# encodings should work fine as well, as Penny will convert them-# to Unicode internally.--# If your ledger file is encoded in UTF-8-# and you still have Unicode problems,-# it is a bug. Please report it to me.--# If you have no idea what all this encoding stuff is about,-# the first thing to remember is that there is no such thing-# as plain text. Read this:-# http://www.joelonsoftware.com/articles/Unicode.html--# The Penny program will not change your input file. It cannot-# change your input file as it is opened read-only.--##############################################################--# Dates - as one example with the prices suggested, dates-# need not be just a date. They can also include a time.-# Internally, Penny records all dates as both dates and times.-# If you give a date but do not give a time, the time is assumed-# to be midnight.-# Of course you cannot have times without timezones. Penny-# assumes each time is in the DefaultTimeZone, which you can configure-# or which you can configure as being in whatever time zone your-# system is set to use. Or of course you can specify the time-# zone by specifying the number of hours and minutes the-# time differs from UTC. To specify UTC, use +0000 or -0000. In the-# default configuration, the DefaultTimeZone is UTC.--; Transaction that specifies a time. The time zone is the-; DefaultTimeZone, which you can configure. Times must be specified-; using 24 hour time. Leading zeroes are optional for the hours;-; they are mandatory for the minutes and seconds.-2012/01/28 09:58 King Arthur Flour-    Expenses:Food                  Dr $5.25-       'Sourdough starter!-    Assets:Current:Checking--; Transaction specifying a local time zone-2012/01/28 17:26:44 -0500 Exactamundo-    Expenses:Amusements           Dr $400-    {Liabilities:Credit Card}       Cr $300-    Assets:Current:Checking       Cr $100--; Transaction specifying UTC. Also, you can always-; spell out "Debit" or "Credit" if you want to.-2012/01/28 23:30 +0000 Pacific Dispatch-    Expenses:Newspapers          Debit $1.25-    Assets:Cash                  Credit $1.25---# There are many different ways to write an amount. First, some-# terminology. Some of these terms are specific to Penny; if these-# terms are inconsistent with usual accounting terminology or if-# they don't make sense, please let me know.-#-# A commodity is a thing of value you track in Penny. Typically this-# will be a currency (like $ or €, but it can be whatever you like,-# such as a ticker symbol or some letters to represent your house.--# A quantity is a number, such as 3 or 65.2 or 33.80. Notice that-# it does not have a commodity.--# An amount is a quantity and a commodity.--# An entry is an amount combined with an indication of Debit-# or Credit.--# You can write commodities and quantities in one of four ways:-#-# * Commodity first, without a separating space. E.g. $100, F25, €59,-# In this case the commodity name CANNOT have any digits.-#-# * Commodity first, with a separating space. E.g. $ 100, € 59.-#-# * Commodity last, without separating space. E.g. 59€.-#-# * Commodity last, with separating space. E.g. 59 €.-#-# In the last three cases, the commodity name can include digits.-# If you want to include spaces in the name of a commodity, surround-# it with "quotes".-# In all cases the commodity name can use sub-commodities by-# using colons.--# A commodity's identity is determined by the characters you use-# when you type the commodity. Thus EUR 100 and 100 EUR mean the-# same thing, except for the fact that they are formatted differently.--# The Penny parser does remember the order in which you specified-# the commodity and the quantity for every posting, but currently-# nothing is done with this information. For instance, in the-# postings report, the commodity is always shown first.--# Here's an example. Note that the transaction balances-# because the commodity is the same whether written before or after-# the quantity.--2012/01/30 Benny Broker-    Assets:Brokerage                 Dr MSFT100-    Assets:Current:Checking          Cr 2959 $-    {Income:Stock Trading Account}     Cr 100 MSFT-    {Income:Stock Trading Acccount}      Dr $2959---# What's the difference between transaction memos, posting-# memos, and comments? Here I will draw a distinction between-# penny (lower case), which is the command-line executable-# program, and the Penny parser (upper case), which is the Haskell-# module that parses your Penny ledger file. Thus penny lower-case-# is the binary; Penny is broader and encompasses the library-# as well as the program.--# So, the Penny parser does see and remember transaction memos,-# posting memos, and comments. However, it does not pass-# comments along to the rest of the penny command-line program.-# The penny command-line program only deals with transactions-# and postings; transactions and postings can have-# memos associated with them.--# Therefore, using the penny command-line program, you can only-# search memos, not comments. However, the fact that the Penny-# parser does see comments might be useful if you are going to-# write your own Haskell code using the parser. For instance,-# you might want to transform your Penny ledger (maybe to-# automatically reconcile your checking account statement).-# By writing your own code using the Penny parser, you can-# do transformations like this while still keeping your comments-# intact. Pretty neat.-