From f01e09e5196127fa12e7291f3fc029ffa99eaef8 Mon Sep 17 00:00:00 2001 From: adamsrl Date: Wed, 22 Aug 2012 18:00:42 -0500 Subject: Created contrib/raw for examples to be shared among advanced Ledger users and made generic to enable code reuse. --- contrib/raw/README | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 contrib/raw/README diff --git a/contrib/raw/README b/contrib/raw/README new file mode 100644 index 00000000..82ae74e1 --- /dev/null +++ b/contrib/raw/README @@ -0,0 +1,5 @@ +These scripts are from my (rladams) local ledger customizations. + +They are intended as examples for features that can be made generic to benefit other Ledger users. + +As they become refined, the raw files will be removed and replaced by suitable sources in contrib. -- cgit v1.2.3 From 6de14e08677f6cb0c10c9d34c3024918bf979d68 Mon Sep 17 00:00:00 2001 From: adamsrl Date: Wed, 22 Aug 2012 18:27:23 -0500 Subject: Added raw copies of my custom ledger scripts and elisp additions. Next need to isolate features and make them more generic for reuse by other Ledger users. --- contrib/raw/GenerateLatexExpeneseReport.pl | 429 +++++++++++++++++++++++++ contrib/raw/MetadataExample.dat | 111 +++++++ contrib/raw/VerifyImages.sh | 14 + contrib/raw/dotemacs.el | 201 ++++++++++++ contrib/raw/ledger-matching.el | 212 ++++++++++++ contrib/raw/ledger-shell-environment-functions | 90 ++++++ 6 files changed, 1057 insertions(+) create mode 100755 contrib/raw/GenerateLatexExpeneseReport.pl create mode 100644 contrib/raw/MetadataExample.dat create mode 100755 contrib/raw/VerifyImages.sh create mode 100644 contrib/raw/dotemacs.el create mode 100644 contrib/raw/ledger-matching.el create mode 100644 contrib/raw/ledger-shell-environment-functions diff --git a/contrib/raw/GenerateLatexExpeneseReport.pl b/contrib/raw/GenerateLatexExpeneseReport.pl new file mode 100755 index 00000000..670d9f21 --- /dev/null +++ b/contrib/raw/GenerateLatexExpeneseReport.pl @@ -0,0 +1,429 @@ +#!/usr/bin/perl + +use warnings; +use strict; +use Getopt::Long; # Options processing +use Smart::Comments -ENV, "###"; # Ignore my dividers, and use + # Smart_Comments=1 to activate +use Cwd; +use File::Basename; +use 5.10.0; +use POSIX qw(strftime); +use Date::Calc qw(Add_Delta_Days); + +use Template; +my $TT = Template->new( { POST_CHOMP => 1 } ); + +###################################################################### +# TODO +# +# DONE Meal summaries are broken for multi-week reports + +###################################################################### +# Options + +# Is this an internal report? +my $ExpenseReportCode = undef; +my $Internal = undef; +my $SuppressMeals = 0; +my $ViewAfter = 0; +my $ImageDir = "."; +my $Anonymize = 0; +my $Help = undef; + +GetOptions( 'c' => \$Internal, + 'm' => \$SuppressMeals, + 'v' => \$ViewAfter, + 'a' => \$Anonymize, + 'I' => \$ImageDir, + 'e:s' => \$ExpenseReportCode, + 'h|help' => \$Help + ); + +# Help + +defined $Help && do { + print <" unless defined $ExpenseReportCode; + +###################################################################### +# Report items + +my @ItemizedExpenses; +my $ItemizedTotal = 0.00; + +my @ItemizedReceipts; + +my @MealsReport; + +###################################################################### +# Gather required data about this expense report from the directory name +# ie: ./AISER0015 20090419-20090425 AGIL2078 Shands HACMP/ +# +# ExpenseReportCode = AISER0015 +# DateRange = 20090419-20090425 +# ProjectCode = AGIL2078 +# Description = Shands HACMP + + +###################################################################### +# Remaining options + +# Where is the ledger binary? +my $LedgerBin = "./ledger -f ./.ledger -V"; + +# -E Show empty accounts +# -S d Sort by date +# -V Convert mileage to $ +my $LedgerOpts = "--no-color -S d"; + +my $LedgerAcct = "^Dest:Projects"; + +my $LedgerCriteria = "%" . "ER=$ExpenseReportCode"; + +# Internal report? + +if ( $Internal ) { + + # No mileage on an internal report + # $LedgerCriteria .= "&!/Mileage/"; # This shouldn't matter, just don't put metadata for ER on mileage + $LedgerAcct = "^Dest:Internal"; + +} + +my $CmdLine = "$LedgerBin reg $LedgerOpts -E \"$LedgerCriteria\" and ^Stub " + . "--format \"%(tag('ER'))~%(tag('PROJECT'))~%(tag('NOTE'))\n\""; +### $CmdLine + +my @TempLine = `$CmdLine`; + +# Match all remaining items +$TempLine[0] =~ m,^(?.*?)~ + (?.*?)~ + (?.*?)\s*$,x; + +my $ProjectCode= $+{'Project'}; +my $Description= $+{'Note'}; + +### $ExpenseReportCode +### $ProjectCode +### $Description +### $LedgerAcct +### $Internal +### $Anonymize +### $LedgerAcct +### $LedgerOpts +### $LedgerCriteria + + +###################################################################### +# Pull main ledger report of line items for the expense report +# Using ~ as a delimiter +# +# Example: +# '2009/04/25~AR:Projects:AGIL2078:PersMealsLunch~:AISER0015: PILOT 00004259 MIDWAY, FL~ 8.68~Receipts/AGIL2078/20090425_Pilot_8_68_21204.jpg\n' +# +#./ledger --no-color reg %ER=AISER0040 and ^Projects -y "%Y/%m/%d" -V --format "%(date)~%(account)~%(payee)~%(amount)~%(tag('NOTE'))\n" + +$CmdLine = "$LedgerBin reg $LedgerOpts \"$LedgerCriteria\" and \"$LedgerAcct\" " + . "-y \"%Y/%m/%d\" " + . "--format \"%(date)~%(tag('CATEGORY'))~%(payee)~%(display_amount)~%(tag('NOTE'))~%(tag('RECEIPT'))\\n\""; +### $CmdLine +my @MainReport = `$CmdLine`; + + +### MainReport: @MainReport + +# Remove any project codes and linefeeds +#map { chomp(); s/(:AISER[0-9][0-9][0-9][0-9])+://g; } @MainReport; # No need, thats now metadata + +foreach my $line (@MainReport) { ### Processing Main Report... done + + # Remove bad chars (#&) + $line =~ tr/#&//d; + + # Match all remaining items + $line =~ m,^(?[0-9]{4}/[0-9]{2}/[0-9]{2})~ + (?.*?)~ + (?.*?)~ + (?.*?)~ + (?.*?)~ + (?.*?)\s*$,x; + my %Record = %+; + + $Record{'Amount'}=~tr/$,//d; + + foreach (keys %Record) { + $Record{$_} =~ s/^\s+//g; + } + + + # Grab images from <> + my $ImageList = $Record{'Receipts'}; + $ImageList //= ''; + my @Images = split( /,/, $ImageList ); + + # Cleanup + # Take last word of account name as category + $Record{'Category'} = ( split( /:/, $Record{'Category'} ) )[-1]; + + # If no images, italicise the line item. + $Record{'Italics'} = 1; + + # Test images + foreach my $Image (@Images) { + + # Turn off italics because there is an image + $Record{'Italics'} = 0; + + if (! -r $ImageDir . "/" . $Image) { + print STDERR "Missing $ImageDir/$Image\n"; + } + } + + # Add to itemized expenses to be printed + push( @ItemizedExpenses, \%Record ); + $ItemizedTotal += $Record{'Amount'}; + + # Add to itemized reciepts for printing + push( @ItemizedReceipts, { 'Vendor' => $Record{'Vendor'}, + 'Amount' => $Record{'Amount'}, + 'Date' => $Record{'Date'}, + 'Images' => \@Images } ) + if $ImageList; + +} + +### @ItemizedExpenses + +###################################################################### +# Meals report + +# Summarize total spent on meals by day +$CmdLine = "$LedgerBin reg $LedgerOpts " + . "\"$LedgerCriteria\" and \"$LedgerAcct\" and \%CATEGORY=Meals " + . "-D -n " + . "--format \"%(account)~%(payee)~%(display_amount)~%(total)\n\" " + . "| grep -v ''"; + +### $CmdLine +my @MealsOutput = `$CmdLine`; + +### @MealsOutput + +foreach my $line (@MealsOutput) { + + # Match all remaining items + $line =~ m,^(?.*?)~ + (?.*?)~\$ + (?\s*[0-9]+\.[0-9]+)~\$ + (?.*?)\s*$,x; + my %TRecord=%+; + $TRecord{'Account'}=~s/^Projects://g; + $TRecord{'DOW'}=~s/^- //g; + + # Add to itemized expenses to be printed + push( @MealsReport, \%TRecord ); + +} + +###################################################################### +# Total by category + +$CmdLine = "$LedgerBin bal $LedgerOpts " + . "\"$LedgerCriteria\" and \"$LedgerAcct\" '--account=tag(\"CATEGORY\")' " + . "--format \"%(account)~%(display_total)\\n\""; + +### $CmdLine +my @CategoryOutput = `$CmdLine`; + +### @CategoryOutput + +my @CategoryReport; + +foreach my $line (@CategoryOutput) { + + chomp $line; + $line =~ tr/\$,//d; + + # Match all remaining items + my @Temp = split(/~/,$line); + + my %TRecord= ( 'Category' => $Temp[0], + 'Amount' => $Temp[1]); + + if ($TRecord{'Category'} eq '') { + $TRecord{'Category'} = '\\hline \\bf Total'; + } + + # Cleanup + # Take last word of account name as category + $TRecord{'Category'} = ( split( /:/, $TRecord{'Category'} ) )[0]; + + # Add to itemized expenses to be printed + push( @CategoryReport, \%TRecord ); + +} + +### @CategoryReport + + +###################################################################### +# Output +###################################################################### + +my $TTVars = { + 'Internal' => $Internal, + 'SuppressMeals' => $SuppressMeals, + 'ExpenseReportCode' => $ExpenseReportCode, + 'ProjectCode' => $ProjectCode, + 'Description' => $Description, + 'ItemizedExpenses' => \@ItemizedExpenses, + 'ItemizedTotal' => $ItemizedTotal, + 'MealsReport' => \@MealsReport, + 'CategoryReport' => \@CategoryReport, + 'ItemizedReceipts' => \@ItemizedReceipts, + 'ImageDir' => $ImageDir, + 'Anonymize' => $Anonymize +}; + +#### $TTVars + +my $LatexTemplate = <process( \$LatexTemplate, $TTVars, "./tmp/" . $LatexFN ) || do { + my $error = $TT->error(); + print "error type: ", $error->type(), "\n"; + print "error info: ", $error->info(), "\n"; + die $error; +}; + + +my $LatexOutput = `pdflatex -interaction batchmode -output-directory ./tmp "$LatexFN"`; +### $LatexOutput + + $LatexOutput = `pdflatex -interaction batchmode -output-directory ./tmp "$LatexFN"`; +### $LatexOutput + +if ($ViewAfter) { + my $ViewFN = $LatexFN; + $ViewFN =~ s/\.tex$/.pdf/; + `acroread "./tmp/$ViewFN"`; +} + diff --git a/contrib/raw/MetadataExample.dat b/contrib/raw/MetadataExample.dat new file mode 100644 index 00000000..791eaf77 --- /dev/null +++ b/contrib/raw/MetadataExample.dat @@ -0,0 +1,111 @@ +; TAG key: value + +2009/09/27 * (09/28/2009) HUDSON NEWS HOUSTN HBB HOUSTON, TX + Source:Visa -$6.55 + Projects:Meals + ; ER: AISER0033 + ; PROJECT: PROJXXXX + +2009/09/27 * (09/28/2009) PEET'S COFFEE & TEA KINGWOOD, TX + Source:Visa -$2.44 + Projects:Meals + ; ER: AISER0033 + ; PROJECT: PROJXXXX + +2009/09/28 * (09/29/2009) FUSIA NEW YORK, NY + Source:Visa -$15.25 + Projects:Meals + ; ER: AISER0033 + ; PROJECT: PROJXXXX + + +2009/09/29 * (09/30/2009) BALUCHI'S NEW YORK, NY + Source:Visa + Projects:Meals $20.00 + ; ER: AISER0033 + ; PROJECT: PROJXXXX + Internal:Travel $10.44 + ; ER: AISER0036 + ; PROJECT: Internal + + +2009/10/01 * Reimbursing AISER0036 + Bank:AISChecking + ; ER: AISER0036 + ; PROJECT: Internal + Source:Visa $10.44 + + +---------- + +$ ./ledger -Ef AISER0033.dat bal '--account=tag("ER")' + $-44.24 + $44.24 AISER0033 + 0 AISER0036 +-------------------- + 0 + +$ ./ledger -Ef AISER0033.dat bal + $-10.44 Bank:AISChecking + $10.44 Internal:Travel + $44.24 Projects:Meals + $-44.24 Source:Visa +-------------------- + 0 + +$ ./ledger -Ef AISER0033.dat bal '--account=tag("PROJECT")' + $-44.24 + 0 Internal + $44.24 PROJXXXX +-------------------- + 0 + +$ ./ledger -f AISER0033.dat reg '--account=tag("PROJECT")' +09-Sep-27 HUDSON NEWS HOUSTN .. $-6.55 $-6.55 +09-Sep-27 HUDSON NEWS HOUSTN .. PROJXXXX $6.55 0 +09-Sep-27 PEET'S COFFEE & TEA.. $-2.44 $-2.44 +09-Sep-27 PEET'S COFFEE & TEA.. PROJXXXX $2.44 0 +09-Sep-28 FUSIA NEW YORK, NY $-15.25 $-15.25 +09-Sep-28 FUSIA NEW YORK, NY PROJXXXX $15.25 0 +09-Sep-29 BALUCHI'S NEW YORK,.. $-30.44 $-30.44 +09-Sep-29 BALUCHI'S NEW YORK,.. PROJXXXX $20.00 $-10.44 +09-Sep-29 BALUCHI'S NEW YORK,.. Internal $10.44 0 +09-Oct-01 Reimbursing AISER0036 Internal $-10.44 $-10.44 +09-Oct-01 Reimbursing AISER0036 $10.44 0 + +$ ./ledger -f AISER0033.dat reg '--account=tag("ER")' +09-Sep-27 HUDSON NEWS HOUSTN .. $-6.55 $-6.55 +09-Sep-27 HUDSON NEWS HOUSTN .. AISER0033 $6.55 0 +09-Sep-27 PEET'S COFFEE & TEA.. $-2.44 $-2.44 +09-Sep-27 PEET'S COFFEE & TEA.. AISER0033 $2.44 0 +09-Sep-28 FUSIA NEW YORK, NY $-15.25 $-15.25 +09-Sep-28 FUSIA NEW YORK, NY AISER0033 $15.25 0 +09-Sep-29 BALUCHI'S NEW YORK,.. $-30.44 $-30.44 +09-Sep-29 BALUCHI'S NEW YORK,.. AISER0033 $20.00 $-10.44 +09-Sep-29 BALUCHI'S NEW YORK,.. AISER0036 $10.44 0 +09-Oct-01 Reimbursing AISER0036 AISER0036 $-10.44 $-10.44 +09-Oct-01 Reimbursing AISER0036 $10.44 0 + + +$ ./ledger -f AISER0033.dat reg %ER=AISER0033 +09-Sep-27 HUDSON NEWS HOUSTN .. Projects:Meals $6.55 $6.55 +09-Sep-27 PEET'S COFFEE & TEA.. Projects:Meals $2.44 $8.99 +09-Sep-28 FUSIA NEW YORK, NY Projects:Meals $15.25 $24.24 +09-Sep-29 BALUCHI'S NEW YORK,.. Projects:Meals $20.00 $44.24 + + +$ ./ledger -f AISER0033.dat reg %ER=AISER0036 +09-Sep-29 BALUCHI'S NEW YORK,.. Internal:Travel $10.44 $10.44 +09-Oct-01 Reimbursing AISER0036 Bank:AISChecking $-10.44 0 + +$ ./ledger -f AISER0033.dat reg %PROJECT=PROJXXXX +09-Sep-27 HUDSON NEWS HOUSTN .. Projects:Meals $6.55 $6.55 +09-Sep-27 PEET'S COFFEE & TEA.. Projects:Meals $2.44 $8.99 +09-Sep-28 FUSIA NEW YORK, NY Projects:Meals $15.25 $24.24 +09-Sep-29 BALUCHI'S NEW YORK,.. Projects:Meals $20.00 $44.24 + +$ ./ledger -f AISER0033.dat --prepend-format='%(tag("IMG")) ' reg %ER=0033 +image1.jpg 09-Sep-27 HUDSON NEWS HOUSTN .. Projects:Meals $6.55 $6.55 +image2.jpg 09-Sep-27 PEET'S COFFEE & TEA.. Projects:Meals $2.44 $8.99 +image3.jpg 09-Sep-28 FUSIA NEW YORK, NY Projects:Meals $15.25 $24.24 +image4.jpg 09-Sep-29 BALUCHI'S NEW YORK,.. Projects:Meals $20.00 $44.24 diff --git a/contrib/raw/VerifyImages.sh b/contrib/raw/VerifyImages.sh new file mode 100755 index 00000000..5975f7cf --- /dev/null +++ b/contrib/raw/VerifyImages.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +grep -h '; RECEIPT: ' \ + *.dat \ + */*.dat \ + | sed 's,\W*; RECEIPT: ,,g' \ + | tr , '\n' \ + | sort -u \ + | while read X +do + [ -f "$X" ] \ + && echo OK $X \ + || echo XX $X +done diff --git a/contrib/raw/dotemacs.el b/contrib/raw/dotemacs.el new file mode 100644 index 00000000..b270042e --- /dev/null +++ b/contrib/raw/dotemacs.el @@ -0,0 +1,201 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Ledger + +;; Maybe later add this to the expense repo once it settles +(add-to-list 'load-path "/home/adamsrl/.emacs.d/addons/ledger") + +(add-to-list 'load-path "/home/adamsrl/AdamsInfoServ/BusinessDocuments/Ledger/AdamsRussell/bin") +(autoload 'ledger-mode "ldg-new" nil t) +(add-to-list 'auto-mode-alist '("\\.dat$" . ledger-mode)) + +(add-hook 'ledger-mode-hook + (lambda () + (setq truncate-lines 1) + (url-handler-mode 1) ; Enable hyperlinks + (require 'ledger-matching) ; Requires ldg-report anyway + (load-file "/home/adamsrl/.emacs.d/addons/ledger/ldg-xact.el") + (let ((map (current-local-map))) + (define-key map (kbd "\C-c o") 'find-file-at-point) ; Open images + (define-key map (kbd "") 'ledger-expense-shortcut) + (define-key map (kbd "M-i") 'ledger-expense-internal) + (define-key map (kbd "M-o") 'ledger-expense-personal) + (define-key map (kbd "M-'") 'ledger-expense-split) + (define-key map (kbd "M-n") '(lambda () + (interactive) + (ledger-post-next-xact) + (recenter) + (when (get-buffer "*Receipt*") + (ledger-expense-show-receipt)))) + (define-key map (kbd "M-p") '(lambda () (interactive) + (ledger-post-prev-xact) + (recenter) + (when (get-buffer "*Receipt*") + (ledger-expense-show-receipt)))) + (local-unset-key [tab]) ; Ideally this turns off pcomplete + (local-unset-key [(control ?i)]) ; Ideally this turns off pcomplete + ) + + ;(defface ledger-report-face-account-ok '((t (:foreground "Cyan"))) "Derp") + ;(defface ledger-report-face-account-bad '((t (:foreground "Red"))) "Derp") + + (font-lock-add-keywords + 'ledger-mode + '(("Unassigned\\|Unknown\\|; RECEIPT:$" 0 'highlight prepend))) )) + +;; My customizations to make receipt image matching work with ledger-report mode +(add-hook 'ledger-report-mode-hook + (lambda () + (hl-line-mode 1) + (local-set-key (kbd "") 'ledger-report-visit-source) ; Make return jump to the right txn + (local-set-key (kbd "") 'ledger-report-visit-source) ; Make tab jump to the right txn + (local-set-key (kbd "n") '(lambda () + (interactive) + (save-selected-window + (next-line) + (ledger-report-visit-source)))) ; Update a txn window but keep focus + (local-set-key (kbd "p") '(lambda () + (interactive) + (save-selected-window + (previous-line) + (ledger-report-visit-source)))) ; Update a txn window but keep focus + + + (local-set-key (kbd "M-r") 'ledger-receipt-matching) ; Link receipt to current item + (local-set-key (kbd "M-l") 'ledger-matching-tie-receipt-to-txn) ; Link receipt to current item + (local-set-key (kbd "M-n") '(lambda () + (interactive) + (ledger-matching-image-offset-adjust 1))) ; Next receipt image + (local-set-key (kbd "M-p") '(lambda () + (interactive) + (ledger-matching-image-offset-adjust -1))) ; prev receipt image + (local-set-key (kbd "M-s") '(lambda () + (interactive) + (ledger-receipt-skip))) ; Skip receipt image + (local-set-key (kbd "C-c C-e") '(lambda () (interactive) + (save-selected-window + (ledger-report-visit-source) + (ledger-toggle-current-entry) ))) ; Toggle entry + )) + +(defvar *ledger-expense-shortcut-ER* + "Current expense report number, just last four digits (ie: 1234 results in AISER1234).") + +(defvar *ledger-expense-shortcut-split-ER* + "Split (ie: internal) expense report number, just last four digits (ie: 1234 results in AISER1234).") + +(defvar *ledger-expense-shortcut-Proj* "" + "Current export report project code (ie: AGIL1292)") + +(defun ledger-expense-shortcut-ER-format-specifier () *ledger-expense-shortcut-ER*) + +(defun ledger-expense-shortcut-setup (ER Split Proj) + "Sets the variables expanded into the transaction." + (interactive "MER Number (4 digit number only): \nMSplit ER Number (4 digit number only): \nMProject: ") + (setq *ledger-expense-shortcut-ER* + (concatenate 'string "AISER" ER)) + (setq *ledger-expense-shortcut-split-ER* + (concatenate 'string "AISER" Split)) + (setq *ledger-expense-shortcut-Proj* Proj) + (setq ledger-matching-project Proj) + (message "Set Proj to %s and ER to %s, split to %s" + *ledger-expense-shortcut-Proj* + *ledger-expense-shortcut-ER* + *ledger-expense-shortcut-split-ER*)) + +(defun ledger-expense-shortcut () + "Updates the ER and Project metadata with the current values of the shortcut variables." + (interactive) + (when (eq major-mode 'ledger-mode) + (if (or (eql *ledger-expense-shortcut-ER* "") + (eql *ledger-expense-shortcut-Proj* "")) + (message "Run ledger-expense-shortcut-setup first.") + (save-excursion + (search-forward "; ER:") + (kill-line nil) + (insert " " *ledger-expense-shortcut-ER*)) + (save-excursion + (search-forward "; PROJECT:") + (kill-line nil) + (insert " " *ledger-expense-shortcut-Proj*))))) + +(defun ledger-expense-split () + "Splits the current transaction between internal and projects." + (interactive) + (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode + (save-excursion + (end-of-line) + (re-search-backward "^[0-9]\\{4\\}/") + (re-search-forward "^ +Dest:Projects") + (move-beginning-of-line nil) + (let ((begin (point)) + (end (re-search-forward "^$"))) + (goto-char end) + (insert (buffer-substring begin end)) + (goto-char end) + (re-search-forward "^ Dest:Projects") + (replace-match " Dest:Internal") + (re-search-forward "; ER: +[A-Za-z0-9]+") + (replace-match (concat "; ER: " *ledger-expense-shortcut-split-ER*) t) + (when (re-search-forward "; CATEGORY: Meals" (save-excursion (re-search-forward "^$")) t) + (replace-match "; CATEGORY: Travel" t)))) + (re-search-backward "^[0-9]\\{4\\}/") + (re-search-forward "^ +Dest:Projects") + (insert-string " $") )) + +(defun ledger-expense-internal () + "Makes the expense an internal one." + (interactive) + (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode + (save-excursion + (end-of-line) + (re-search-backward "^[0-9]\\{4\\}/") + (let ((begin (point)) + (end (save-excursion (re-search-forward "^$")))) + (when (re-search-forward "^ Dest:Projects" end t) + (replace-match " Dest:Internal") ) + (when (re-search-forward "; CATEGORY: Meals" (save-excursion (re-search-forward "^$")) t) + (replace-match "; CATEGORY: Travel" t)))))) + +(defun ledger-expense-personal () + "Makes the expense an personal one, eliminating metadata and receipts." + (interactive) + (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode + (save-excursion + (end-of-line) + (re-search-backward "^[0-9]\\{4\\}/") + (let ((begin (point)) + (end (save-excursion (re-search-forward "^$")))) + (when (re-search-forward "^ Dest:Projects" end t) + (replace-match " Other:Personal")) + (goto-char begin) + (save-excursion + (when (re-search-forward "^ +; ER:" end t) + (beginning-of-line) + (kill-line 1))) + (save-excursion + (when (re-search-forward "^ +; PROJECT:" end t) + (beginning-of-line) + (kill-line 1))) + (save-excursion + (when (re-search-forward "^ +; CATEGORY:" end t) + (beginning-of-line) + (kill-line 1))) + (save-excursion + (when (re-search-forward "^ +; RECEIPT:" end t) + (beginning-of-line) + (kill-line 1))) + (ledger-toggle-current-entry))))) + +(defun ledger-expense-show-receipt () + "Uses the Receipt buffer to show the receipt of the txn we're on." + (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode + (save-excursion + (end-of-line) + (re-search-backward "^[0-9]\\{4\\}/") + (let ((begin (point)) + (end (save-excursion (re-search-forward "^$")))) + (save-excursion + (when (re-search-forward "^\\( +; RECEIPT: +\\)\\([^,]+?.jpg\\).*$" end t) + (ledger-matching-display-image + (concat "/home/adamsrl/AdamsInfoServ/BusinessDocuments/Ledger/AdamsRussell/" + (match-string 2))) )))))) diff --git a/contrib/raw/ledger-matching.el b/contrib/raw/ledger-matching.el new file mode 100644 index 00000000..d12c3937 --- /dev/null +++ b/contrib/raw/ledger-matching.el @@ -0,0 +1,212 @@ +;; This library is intended to allow me to view a receipt on one panel, and tie it to ledger transactions in another + +(require 'ldg-report) + +(defgroup ledger-matching nil + "Ledger image matching") + +(defcustom ledger-matching-sourcedir "~/AdamsInfoServ/BusinessDocuments/Ledger/Incoming" + "Source directory for images to process, ie: the incoming queue of images." + :group 'ledger-matching) + +(defcustom ledger-matching-destdir "~/AdamsInfoServ/BusinessDocuments/Ledger/AdamsRussell/Receipts" + "Destination directory for images when matched, will still have a project directory appended to it." + :group 'ledger-matching) + +(defcustom ledger-matching-relative-receipt-dir "Receipts" + "Relative directory root for destination images used in Ledger entries, will have the project directory appended and receipt filename." + :group 'ledger-matching) + +(defcustom ledger-matching-convert-binary "/usr/bin/convert" + "Path to the Imagemagick convert command." + :group 'ledger-matching) + +(defcustom ledger-matching-scale 50 + "Scaling parameter to Imagemagick's convert to resize an image for viewing." + :group 'ledger-matching) + +(defcustom ledger-matching-rotation 0 + "Rotation parameter to Imagemagick's convert to rotate an image for viewing. Images on disk should always be upright for reading." + :group 'ledger-matching) + + +(defconst ledger-matching-image-buffer "*Receipt*" + "Buffer name we load images into. Created if it doesn't exist, and persists across image loads.") + + +(defvar ledger-matching-project "Internal" + "The directory appended to the destination for the project code where receipts will be stored.") + +(defvar ledger-matching-image-offset 0 + "The index of the current file from the SORTED source directory contents.") + +(defvar ledger-matching-image-name nil + "The filename only of the current image.") + + +(defun ledger-matching-display-image (image-filename) + "Resize the image and load it into our viewing buffer." + + ;; Create our viewing buffer if needed, and set it. Do NOT switch, + ;; this buffer isn't the primary. Let the user leave it where they + ;; place it. + (unless (get-buffer ledger-matching-image-buffer) + (get-buffer-create ledger-matching-image-buffer)) + (set-buffer ledger-matching-image-buffer) + (erase-buffer) + (goto-char (point-min)) + (insert-string image-filename "\n") + + ;; Convert the source to the temporary dest applying resizing and rotation + (let* ((source (expand-file-name image-filename ledger-matching-sourcedir)) + (dest (make-temp-file "ledger-matching-" nil ".jpg")) + (result (call-process ledger-matching-convert-binary nil (get-buffer "*Messages*") nil + source + "-scale" (concat (number-to-string ledger-matching-scale) "%") + "-rotate" (number-to-string ledger-matching-rotation) + dest))) + + (if (/= 0 result) + + ;; Bomb out if the convert fails + (message "Error running convert, see *Messages* buffer for details.") + + ;; Insert scaled image into the viewing buffer, replacing + ;; current contents Temp buffer is to force sync reading into + ;; memory of the jpeg due to async race condition with display + ;; and file deletion + (let ((image (create-image (with-temp-buffer + (insert-file-contents-literally dest) + (string-as-unibyte (buffer-string))) + 'jpeg t))) + (insert-image image) + (goto-char (point-min)) + + ;; Redisplay is required to prevent a race condition between displaying the image and the deletion. Apparently its async. + ;; Either redisplay or the above string method work, both together can't hurt. + (redisplay) + )) + + ;; Delete our temporary file + (delete-file dest))) + + + +(defun ledger-matching-update-current-image () + "Grab the image from the source directory by offset and display" + + (let* ((file-listing (directory-files ledger-matching-sourcedir nil "\.jpg$" nil)) + (len (safe-length file-listing))) + + ;; Ensure our offset doesn't exceed the file list + (cond ((= len 0) + (message "No files found in source directory.")) + + ((< len 0) + (message "Error, list of files should never be negative. Epic fail.")) + + ((>= ledger-matching-image-offset len) + (message "Hit end of list. Last image.") + (setq ledger-matching-image-offset (1- len))) + + ((< ledger-matching-image-offset 0) + (message "Beginning of list. First image.") + (setq ledger-matching-image-offset 0))) + + ;; Get the name for the offset + (setq ledger-matching-image-name (nth ledger-matching-image-offset file-listing)) + + (ledger-matching-display-image ledger-matching-image-name))) + + + +(defun ledger-matching-image-offset-adjust (amount) + "Incr/decr the offset and update the receipt buffer." + + (setq ledger-matching-image-offset (+ ledger-matching-image-offset amount)) + (ledger-matching-update-current-image)) + + + +(defun ledger-receipt-matching () + "Open the receipt buffer and start with the first image." + (interactive) + (setq ledger-matching-image-offset 0) + (ledger-matching-update-current-image)) + + + +(defun ledger-matching-tie-receipt-to-txn () + (interactive) + (save-selected-window + (ledger-report-visit-source) + + ;; Assumes we're in a narrowed buffer with ONLY this txn + (backward-paragraph) + (beginning-of-line) + + ;; Update the ER and Project while I'm there + (save-excursion + (search-forward "; ER:") + (kill-line nil) + (insert " " *ledger-expense-shortcut-ER*)) + (save-excursion + (search-forward "; PROJECT:") + (kill-line nil) + (insert " " *ledger-expense-shortcut-Proj*)) + + ;; Goto the receipt line, unless their isn't one then add one + (unless (search-forward "RECEIPT:" nil t) + + ;; Still at date line if that failed + (next-line) + (newline) + (insert-string " ; RECEIPT:")) + + ;; Point immediately after : on tag + + ;; Check for existing jpg file + (if (search-forward ".jpg" (line-end-position) t) + + ;; if present make it a comma delimited list + (insert-string ",") + + ;; otherwise just add a space to pad + (insert-string " ")) + + ;; Add our relative filename as the value of the RECEIPT tag + (insert-string (concat ledger-matching-relative-receipt-dir "/" + ledger-matching-project "/" + ledger-matching-image-name)) + + ;; Create the destination project dir if it doesn't exist. + (let ((full-destination (concat ledger-matching-destdir "/" ledger-matching-project ))) + (unless (file-accessible-directory-p full-destination) + (make-directory full-destination t))) + + ;; Rename the file from the source directory to its permanent home + (rename-file (concat ledger-matching-sourcedir "/" + ledger-matching-image-name) + (concat ledger-matching-destdir "/" + ledger-matching-project "/" + ledger-matching-image-name)) + + ;; Update the receipt screen + (ledger-matching-update-current-image) )) + + + +(defun ledger-receipt-skip () + "Move the current image to the Skip directory because its not relevant." + + (rename-file (concat ledger-matching-sourcedir "/" + ledger-matching-image-name) + (concat ledger-matching-sourcedir "/Skip/" + ledger-matching-image-name)) + + ;; Update the receipt screen at the same offset + (ledger-matching-update-current-image)) + + + +(provide 'ledger-matching) diff --git a/contrib/raw/ledger-shell-environment-functions b/contrib/raw/ledger-shell-environment-functions new file mode 100644 index 00000000..7746dc41 --- /dev/null +++ b/contrib/raw/ledger-shell-environment-functions @@ -0,0 +1,90 @@ +# Environment for ledger expenses + +[ $(whoami) == "adamsrl" ] \ + && export LEDGER_HOME="/home/adamsrl/AdamsInfoServ/BusinessDocuments/Ledger/AdamsRussell" \ + || export LEDGER_HOME="/home/Heather/AdamsRussell" + +[ $(hostname) == "cardamom" ] \ + && export LEDGER_BIN="${LEDGER_HOME}/ledger" \ + || export LEDGER_BIN="${LEDGER_HOME}/ledger.exe" + +[ $(whoami) == "andersonll" ] \ + && export LEDGER_HOME="/home/andersonll/AdamsInfoServ/Expenses" \ + && export LEDGER_BIN="${LEDGER_HOME}/ledger" + +# Common reports + +alias ledger='${LEDGER_BIN} -f "${LEDGER_HOME}/.ledger" -VE ' +alias ERSummary='ledger --pivot ER bal | egrep "AIS(ER|IN)[0-9]+|Unassigned"' + +function ERTxns() { + [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return + + ledger reg "%ER=${1}" +} + +function ERCategorySummary() { + [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return + + ledger bal --pivot CATEGORY "%ER=${1}" +} + +function ERMealSummary() { + [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return + + ledger reg "%ER=${1}" and %CATEGORY=Meals -D +} + +function ERMeals() { + [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return + + ledger reg "%ER=${1}" and %CATEGORY=Meals +} + +function ERUncleared() { + [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return + + ledger reg "%ER=${1}" -U +} + +function ERMissingReceipts() { + [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return + + ledger reg "%ER=${1}" and not %RECEIPT +} + +function ERVerify() { + [ -z "$1" ] && echo "Please specify an ER number (ie: AISER0042)." && return + + echo "========== Uncleared txns below ==========" + ERUncleared "$1" + echo "========== Missing receipts below (miles and stubs ok) ==========" + ERMissingReceipts "$1" + echo "========== Category Summary (airline? mileage? car? hotel? ==========" + ERCategorySummary "$1" + echo "========== Meal summary (<\$50 / day unless otherwise specified) ==========" + ERMealSummary "$1" + echo "========== Account Verification (Internal vs Project ER should be ONE type) ==========" + echo $1 | grep AISIN >/dev/null 2>&1 \ + || { ledger reg "%ER=${1}" | grep Dest:Internal ; } \ + && { ledger reg "%ER=${1}" | grep Dest:Projects ; } + echo "========== Project Verification (only one project code should be listed) ==========" + ledger print "%ER=${1}" | grep PROJECT | sort -u + echo "========== Receipts missing ==========" + ledger print "%ER=${1}" | grep -h '; RECEIPT: ' \ + | sed 's,\W*; RECEIPT: ,,g' \ + | tr , '\n' \ + | sort -u \ + | while read X ; do + [ -f "${LEDGER_HOME}/${X}" ] \ + || echo XX $X + done +} + +function ERListing() { + ledger reg Stub --register-format="%(tag('ER')) %(tag('NOTE'))\n" | sort -u +} + +function ERQueue() { + ledger reg %ER=Unassigned --prepend-format="%(filename) " +} -- cgit v1.2.3 From 7b11dad404b4e689393a5725c93ea84507fc2221 Mon Sep 17 00:00:00 2001 From: adamsrl Date: Sun, 14 Oct 2012 16:43:56 -0500 Subject: Added back my patches for johns ldg-mode --- lisp/ldg-report.el | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lisp/ldg-report.el b/lisp/ldg-report.el index f9c6afca..e0744100 100644 --- a/lisp/ldg-report.el +++ b/lisp/ldg-report.el @@ -259,15 +259,16 @@ the default." (insert (format "Report: %s\n" ledger-report-name) (format "Command: %s\n" cmd) (make-string (- (window-width) 1) ?=) - "\n") - (let ((register-report (string-match " reg\\(ister\\)? " cmd)) + "\n\n") + (let ((data-pos (point)) + (register-report (string-match " reg\\(ister\\)? " cmd)) files-in-report) (shell-command (if register-report (concat cmd " --prepend-format='%(filename):%(beg_line):'") cmd) t nil) (when register-report - (goto-char (point-min)) + (goto-char data-pos) (while (re-search-forward "^\\([^:]+\\)?:\\([0-9]+\\)?:" nil t) (let ((file (match-string 1)) (line (string-to-number (match-string 2)))) @@ -283,20 +284,30 @@ the default." ledger-report-patch-alist)) (add-to-list 'files-in-report fullpath))) - (dolist (path files-in-report) - (let ((buf (get-file-buffer path))) - (if (and buf (buffer-live-p buf)) - (ledger-report-patch-reports buf)))))))) + ;; Disable john's "monkey patching" because it didn't work + ;; (dolist (path files-in-report) + ;; (let ((buf (get-file-buffer path))) + ;; (if (and buf (buffer-live-p buf)) + ;; (ledger-report-patch-reports buf)))))))) + ) + (goto-char data-pos) ))) (defun ledger-report-visit-source () (interactive) (let ((prop (get-text-property (point) 'ledger-source))) (destructuring-bind (file . line-or-marker) prop (find-file-other-window file) + (widen) (if (markerp line-or-marker) (goto-char line-or-marker) (goto-char (point-min)) - (forward-line (1- line-or-marker)))))) + (forward-line (1- line-or-marker)) + (re-search-backward "^[0-9]+") + (beginning-of-line) + (let ((start-of-txn (point))) + (forward-paragraph) + (narrow-to-region start-of-txn (point)) + (backward-paragraph)))))) (defun ledger-report-goto () "Goto the ledger report buffer." -- cgit v1.2.3 From 6094c202c152b282098de1002be54b3bcd7ab50c Mon Sep 17 00:00:00 2001 From: adamsrl Date: Mon, 15 Oct 2012 11:47:29 -0500 Subject: Adding goto EOL to fix issue where re-search-forward was including ledger report data in the text properties. --- lisp/ldg-report.el | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lisp/ldg-report.el b/lisp/ldg-report.el index e0744100..9fdf55d2 100644 --- a/lisp/ldg-report.el +++ b/lisp/ldg-report.el @@ -275,6 +275,7 @@ the default." (delete-region (match-beginning 0) (match-end 0)) (set-text-properties (line-beginning-position) (line-end-position) (list 'ledger-source (cons file line))) + (end-of-line) (let* ((fullpath (expand-file-name file)) (entry (assoc fullpath ledger-report-patch-alist))) (if entry @@ -282,15 +283,13 @@ the default." (push (cons (expand-file-name file) (list (current-buffer))) ledger-report-patch-alist)) - (add-to-list 'files-in-report fullpath))) - - ;; Disable john's "monkey patching" because it didn't work - ;; (dolist (path files-in-report) - ;; (let ((buf (get-file-buffer path))) - ;; (if (and buf (buffer-live-p buf)) - ;; (ledger-report-patch-reports buf)))))))) - ) - (goto-char data-pos) ))) + (add-to-list 'files-in-report fullpath)))) + (dolist (path files-in-report) + (let ((buf (get-file-buffer path))) + (if (and buf (buffer-live-p buf)) + (ledger-report-patch-reports buf))))) + (goto-char data-pos))) + (defun ledger-report-visit-source () (interactive) -- cgit v1.2.3 From 9173190a8f1d93bebf14ff795bee46b619fd845a Mon Sep 17 00:00:00 2001 From: adamsrl Date: Mon, 15 Oct 2012 13:29:22 -0500 Subject: Made marker logic for jumping from hyperlinked reports to source files work by simplifying. --- lisp/ldg-report.el | 46 +++++++++------------------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/lisp/ldg-report.el b/lisp/ldg-report.el index 9fdf55d2..9a964195 100644 --- a/lisp/ldg-report.el +++ b/lisp/ldg-report.el @@ -231,28 +231,6 @@ the default." (ledger-reports-custom-save)) report-cmd)) -(defvar ledger-report-patch-alist nil) - -(defun ledger-report-patch-reports (buf) - (when ledger-report-patch-alist - (let ((entry (assoc (expand-file-name (buffer-file-name buf)) - ledger-report-patch-alist))) - (when entry - (dolist (b (cdr entry)) - (if (buffer-live-p b) - (with-current-buffer b - (save-excursion - (goto-char (point-min)) - (while (not (eobp)) - (let ((record (get-text-property (point) 'ledger-source))) - (if (and record (not (markerp (cdr record)))) - (setcdr record (with-current-buffer buf - (save-excursion - (goto-char (point-min)) - (forward-line (cdr record)) - (point-marker)))))) - (forward-line 1)))))))))) - (defun ledger-do-report (cmd) "Run a report command line." (goto-char (point-min)) @@ -271,23 +249,17 @@ the default." (goto-char data-pos) (while (re-search-forward "^\\([^:]+\\)?:\\([0-9]+\\)?:" nil t) (let ((file (match-string 1)) - (line (string-to-number (match-string 2)))) + (line (string-to-number (match-string 2)))) (delete-region (match-beginning 0) (match-end 0)) (set-text-properties (line-beginning-position) (line-end-position) - (list 'ledger-source (cons file line))) - (end-of-line) - (let* ((fullpath (expand-file-name file)) - (entry (assoc fullpath ledger-report-patch-alist))) - (if entry - (nconc (cdr entry) (list (current-buffer))) - (push (cons (expand-file-name file) - (list (current-buffer))) - ledger-report-patch-alist)) - (add-to-list 'files-in-report fullpath)))) - (dolist (path files-in-report) - (let ((buf (get-file-buffer path))) - (if (and buf (buffer-live-p buf)) - (ledger-report-patch-reports buf))))) + (list 'ledger-source (cons file (save-window-excursion + (save-excursion + (find-file file) + (widen) + (goto-char (point-min)) + (forward-line (1- line)) + (point-marker)))))) + (end-of-line)))) (goto-char data-pos))) -- cgit v1.2.3 From 2b7f7b7f90e182b52219a5fb824e3463a3b8a220 Mon Sep 17 00:00:00 2001 From: adamsrl Date: Mon, 15 Oct 2012 13:41:20 -0500 Subject: Moved over some shortcut functions --- contrib/raw/ledger-matching.el | 142 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 6 deletions(-) diff --git a/contrib/raw/ledger-matching.el b/contrib/raw/ledger-matching.el index d12c3937..7c568126 100644 --- a/contrib/raw/ledger-matching.el +++ b/contrib/raw/ledger-matching.el @@ -145,11 +145,12 @@ (backward-paragraph) (beginning-of-line) - ;; Update the ER and Project while I'm there - (save-excursion - (search-forward "; ER:") - (kill-line nil) - (insert " " *ledger-expense-shortcut-ER*)) + ;; ;; Update the ER and Project while I'm there + ;; (save-excursion + ;; (search-forward "; ER:") + ;; (kill-line nil) + ;; (insert " " *ledger-expense-shortcut-ER*)) + ;; Just do the project for now. (save-excursion (search-forward "; PROJECT:") (kill-line nil) @@ -192,7 +193,9 @@ ledger-matching-image-name)) ;; Update the receipt screen - (ledger-matching-update-current-image) )) + (ledger-matching-update-current-image) + + (message "Filed %s to project %s" ledger-matching-image-name ledger-matching-project))) @@ -207,6 +210,133 @@ ;; Update the receipt screen at the same offset (ledger-matching-update-current-image)) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Items below are speed entry macros, and should eventually migrate to their own file. + +(defvar *ledger-expense-shortcut-ER* + "Current expense report number, just last four digits (ie: 1234 results in AISER1234).") + +(defvar *ledger-expense-shortcut-split-ER* + "Split (ie: internal) expense report number, just last four digits (ie: 1234 results in AISER1234).") + +(defvar *ledger-expense-shortcut-Proj* "" + "Current export report project code (ie: AGIL1292)") + +(defun ledger-expense-shortcut-ER-format-specifier () *ledger-expense-shortcut-ER*) + +(defun ledger-expense-shortcut-Project-format-specifier () *ledger-expense-shortcut-Proj*) + +(defun ledger-expense-shortcut-setup (ER Split Proj) + "Sets the variables expanded into the transaction." + (interactive "MER Number (ER or IN and 4 digit number only): \nMSplit ER Number (ER or IN and 4 digit number only): \nMProject: ") + (setq *ledger-expense-shortcut-ER* + (concatenate 'string "AIS" ER)) + (setq *ledger-expense-shortcut-split-ER* + (concatenate 'string "AIS" Split)) + (setq *ledger-expense-shortcut-Proj* Proj) + (setq ledger-matching-project Proj) + (message "Set Proj to %s and ER to %s, split to %s" + *ledger-expense-shortcut-Proj* + *ledger-expense-shortcut-ER* + *ledger-expense-shortcut-split-ER*)) + +(defun ledger-expense-shortcut () + "Updates the ER and Project metadata with the current values of the shortcut variables." + (interactive) + (when (eq major-mode 'ledger-mode) + (if (or (eql *ledger-expense-shortcut-ER* "") + (eql *ledger-expense-shortcut-Proj* "")) + (message "Run ledger-expense-shortcut-setup first.") + (save-excursion + (search-forward "; ER:") + (kill-line nil) + (insert " " *ledger-expense-shortcut-ER*)) + (save-excursion + (search-forward "; PROJECT:") + (kill-line nil) + (insert " " *ledger-expense-shortcut-Proj*))))) + +(defun ledger-expense-split () + "Splits the current transaction between internal and projects." + (interactive) + (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode + (save-excursion + (end-of-line) + (re-search-backward "^[0-9]\\{4\\}/") + (re-search-forward "^ +Dest:Projects") + (move-beginning-of-line nil) + (let ((begin (point)) + (end (re-search-forward "^$"))) + (goto-char end) + (insert (buffer-substring begin end)) + (goto-char end) + (re-search-forward "^ Dest:Projects") + (replace-match " Dest:Internal") + (re-search-forward "; ER: +[A-Za-z0-9]+") + (replace-match (concat "; ER: " *ledger-expense-shortcut-split-ER*) t) + (when (re-search-forward "; CATEGORY: Meals" (save-excursion (re-search-forward "^$")) t) + (replace-match "; CATEGORY: Travel" t)))) + (re-search-backward "^[0-9]\\{4\\}/") + (re-search-forward "^ +Dest:Projects") + (insert-string " $") )) + +(defun ledger-expense-internal () + "Makes the expense an internal one." + (interactive) + (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode + (save-excursion + (end-of-line) + (re-search-backward "^[0-9]\\{4\\}/") + (let ((begin (point)) + (end (save-excursion (re-search-forward "^$")))) + (when (re-search-forward "^ Dest:Projects" end t) + (replace-match " Dest:Internal") ) + (when (re-search-forward "; CATEGORY: Meals" (save-excursion (re-search-forward "^$")) t) + (replace-match "; CATEGORY: Travel" t)))))) + +(defun ledger-expense-personal () + "Makes the expense an personal one, eliminating metadata and receipts." + (interactive) + (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode + (save-excursion + (end-of-line) + (re-search-backward "^[0-9]\\{4\\}/") + (let ((begin (point)) + (end (save-excursion (re-search-forward "^$")))) + (when (re-search-forward "^ Dest:Projects" end t) + (replace-match " Other:Personal")) + (goto-char begin) + (save-excursion + (when (re-search-forward "^ +; ER:" end t) + (beginning-of-line) + (kill-line 1))) + (save-excursion + (when (re-search-forward "^ +; PROJECT:" end t) + (beginning-of-line) + (kill-line 1))) + (save-excursion + (when (re-search-forward "^ +; CATEGORY:" end t) + (beginning-of-line) + (kill-line 1))) + (save-excursion + (when (re-search-forward "^ +; RECEIPT:" end t) + (beginning-of-line) + (kill-line 1))) + (ledger-toggle-current-entry))))) + +(defun ledger-expense-show-receipt () + "Uses the Receipt buffer to show the receipt of the txn we're on." + (when (eq major-mode 'ledger-mode) ; I made this local now, should only trigger in ldg-mode + (save-excursion + (end-of-line) + (re-search-backward "^[0-9]\\{4\\}/") + (let ((begin (point)) + (end (save-excursion (re-search-forward "^$")))) + (save-excursion + (when (re-search-forward "^\\( +; RECEIPT: +\\)\\([^,]+?.jpg\\).*$" end t) + (ledger-matching-display-image + (concat "/home/adamsrl/AdamsInfoServ/BusinessDocuments/Ledger/AdamsRussell/" + (match-string 2))) )))))) (provide 'ledger-matching) -- cgit v1.2.3 From b9dbf54d9a5d0002e379263b4407450074e45211 Mon Sep 17 00:00:00 2001 From: Craig Earls Date: Tue, 29 Jan 2013 13:13:30 -0700 Subject: Improved emacs section. Now documents all behavior. --- doc/ledger3.texi | 66 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/doc/ledger3.texi b/doc/ledger3.texi index f60bb26e..2e0f9ed5 100644 --- a/doc/ledger3.texi +++ b/doc/ledger3.texi @@ -2325,7 +2325,9 @@ doing it. Journal files are simple free text files easily modified by any text editor. A special mode for EMACS is included with the source -distribution. +distribution. This mode provides syntax highlighting, a reconcile mode +and a report mode. This makes it quote possible to use ledger without +ever leaving EMACS. @cindex EMACS .emacs file @@ -2335,7 +2337,7 @@ Add the following line to your @file{.emacs} (or equivalent, (load "ldg-new") @end smallexample -Copy the several lisp files from the source lisp directory your your +Copy the several lisp files (@file{ldg-*.el}) from the source lisp directory your your @file{site-lisp} directory, or add the ledger lisp source directory to your EMACS load path by adding: @smallexample @@ -2352,7 +2354,8 @@ To enter ledger-mode on a new file, type @command{M-x ledger-mode}. Once you have loaded a Journal file into EMACS, you have several commands available to make entering, clearing and reconciling -transactions and producing reports: +transactions and producing reports (these are also available from the +menu if you can't remember the key combinations): @cindex EMACS commands @table @code @@ -2431,10 +2434,11 @@ will be interpreted as a new account by ledger. @code{C-c C-a} will run the @code{ledger entry} command (@pxref{entry and xact}) from within EMACS. When typed, the mini-buffer will appear with the current year and month, waiting for you to enter the day and -the payee. Ledger will generate a new entry based on the most recent -entry for that payee, using the amount and accounts from that -transaction. If you have a new amount simply type the amount after the -payee. For example, if your journal contains an entry +the payee, and optionally, a commoditized amount. Ledger will generate +a new entry based on the most recent entry for that payee, using the +amount and accounts from that transaction. If you have a new amount +simply type the amount after the payee. For example, if your journal +contains an entry @smallexample 2011/11/25 Viva Italiano Expenses:Food $12.45 @@ -2454,7 +2458,10 @@ Entry: 2011/11/28 viva food 34 tip 7 Liabilities:MasterCard @end smallexample @noindent Notice that the entry will appear at the correct place in the journal -ordered by date, not necessarily at the bottom of the file. +ordered by date, not at the bottom of the file. If you need to include +spaces in the payee name, then surrond the name of the payee with double +quotes, otherwise ledger will interpret the second part of the name as +an account. @node Clearing Transactions, , Automagically Adding new entries, Working with entries @subsubsection Clearing Transactions and Postings @cindex clearing transactions in EMACS @@ -2483,21 +2490,46 @@ toggled. @node Reconciling accounts, Generating Reports, Working with entries, Using EMACS @subsection Reconciling accounts -In the reconcile buffer, use SPACE to toggle the cleared status of a -transaction, C-x C-s to save changes (to the ledger file as well). +Enter the reconcile mode using the menu entry, or @code{C-c C-r}. Emacs +will prompt you for an account name, then display a second buffer with +all of the uncleared transactions. The reconcile buffer has several functions: +@table @code + @item SPACE + toggles the cleared status of a transaction, and show cleared balance inthe minibuffer + @item RETURN + moves the cursor to that transaction in the ledger. + @item C-x C-s + to save changes (to the ledger file as well). + @item q + quite the reconcile mode + @item n p + next line or previous line + @item A + add entry + @item D + delete entry + @item C-l + refresh display +@end table @node Generating Reports, , Reconciling accounts, Using EMACS @subsection Generating Reports The ledger reports command asks the user to select a report to run then creates a report buffer containing the results of running the associated -command line. Its' behavior is modified by a prefix argument which, -when given, causes the generated command line that will be used to -create the report to be presented for editing before the report is -actually run. Arbitrary unnamed command lines can be run by specifying -an empty name for the report. The command line used can later be named -and saved for future use as a named report from the generated reports -buffer. +command line. This allows you to run frequently used reports without +retyping the command line, or writing shell scripts for simple one line +commands. + +To generate a report, select the @code{Run Reports} menu, or type +@code{C-c C-o C-r}. Emacs will prompt for a report name. If it +recognizes the name it will run the report again. If it is a new name, +or blank it will respond by giving you an example command line to edit. +Hitting return willrun the report and present it in a new buffer. + +If you have given it a new name, then @code{s} will save the report for +future use. + In a report buffer, the following keys are available: @table @code -- cgit v1.2.3 From 2ecb03878fe948167b1e3049f35e77c750714852 Mon Sep 17 00:00:00 2001 From: Craig Earls Date: Tue, 29 Jan 2013 13:26:31 -0700 Subject: Documented the tags command. Documented that the account and commodities command now sort. --- doc/ledger3.texi | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/doc/ledger3.texi b/doc/ledger3.texi index 2e0f9ed5..377d740c 100644 --- a/doc/ledger3.texi +++ b/doc/ledger3.texi @@ -2574,7 +2574,7 @@ kill the report buffer * Lot dates:: * Lot notes:: * Lot value expressions:: -* Automated Transactions:: +* Automated Transactions:: @end menu @node Basic format, Eliding amounts, Transactions , Transactions @@ -4776,6 +4776,7 @@ database files. @menu * accounts:: * commodities:: +* tags:: * entry and xact:: * payees:: @end menu @@ -4785,15 +4786,27 @@ database files. The @command{accounts} reports all of the accounts in the journal. Following the command with a regular expression will limit the output to -accounts matching the regex. +accounts matching the regex. The output is sorted by name. Using the +@code{--count} option will tell you haw many entries use each account. -@node commodities, entry and xact, accounts, Reports about your Journals +@node commodities, tags, accounts, Reports about your Journals @subsection @command{commodities} -Report all commodities present in the journals under consideration. +Report all commodities present in the journals under consideration. The + output is sorted by name. Using the @code{--count} option will tell + you haw many entries use each commodity. +@node tags, entry and xact, commodities, Reports about your Journals +@subsection @command{tags} -@node entry and xact, payees, commodities, Reports about your Journals +The @command{tags} reports all of the tags in the journal. The output +is sorted by name. Using the @code{--count} option will tell you haw +many entries use each tag. Using the @code{--values} option will report +the values used by each tag. + + + +@node entry and xact, payees, tags, Reports about your Journals @subsection @command{draft}, @command{entry} and @command{xact} The @code{draft}, @code{entry} and @command{xact} commands simplify the -- cgit v1.2.3