#!/usr/bin/perl

#  This package is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.

use POSIX qw(locale_h); # for setlocale
use strict;
use warnings;
use XML::QOFQSF qw(QSFParse QSFWrite);
use Date::Parse;
use Date::Format;
use Data::Dumper;
use Config::Auto;
use File::HomeDir;
use Number::Format;
use Text::FormatTable;
use Locale::gettext;
use vars qw / $amount $year $date $month $expenses 
$basefile $category $mileage %rates %branches $time
$template $filter @dates $datestr /;

# depends on pilot-qof

setlocale(LC_MESSAGES, "");
textdomain("datafreedom");
my $home = File::HomeDir->my_home;
# safeguard, just in case.
$home = "/tmp" if (!$home);
my $path =  "$home/.datafreedom";
mkdir $path if (! -d $path);
$mileage = 0;
my $cfile = $path . "/invoice.conf";
if (! -f $cfile)
{
	open (CFG, ">$cfile");
	print CFG _g("\# config file for datafreedom-perl invoices\n");
	print CFG _g("\# used by dfinvoice*\n");
	print CFG _g("\# use a [General] section:\n");
	print CFG "\# [General]\n";
	print CFG "\# category=mybusiness\n";
	print CFG "\# basefile=/home/user/pilot/data.xml\n";
	print CFG _g("\# Use one section per branch/location.\n");
	print CFG "\# [Acacia Avenue]\n";
	print CFG "\# code=1234\n";
	print CFG "\# rate=24\n";
	print CFG _g("\# Omit the currency symbols when specifying the rate\n");
	print CFG _g("\# and use . as the decimal point (i.e. locale C).\n");
	print CFG _g("\# e.g. for a rate of \$15.45\n");
	print CFG _g("\# rate=15.45\n");
	print CFG "[General]\n";
	print CFG "category=\n";
	print CFG "basefile=\n";
	print CFG "mileage=0\n";
	print CFG "default_rate=0\n";
	print CFG "default_code=0\n";
	close (CFG);
	die _g("Cannot create config file %s"). $cfile . ": $!\n" if (! -f $cfile);
}

my $config = Config::Auto::parse("$cfile", format => "ini");
$mileage = $config->{'General'}->{'mileage'}
	if (exists $config->{'General'}->{'mileage'});
$category = $config->{'General'}->{'category'}
	if (exists $config->{'General'}->{'category'});
$basefile = $config->{'General'}->{'basefile'}
	if (exists $config->{'General'}->{'basefile'});
if ((not defined $basefile) or (not -f $basefile))
{
	die "No basefile defined in $cfile.\n";
}
%rates = ();
%branches = ();
foreach my $f (sort keys %$config)
{
	next if ($f eq 'General');
	$rates{$f} = (defined $config->{$f}->{'rate'}) ? 
		$config->{$f}->{'rate'} : $config->{'General'}->{'default_rate'};
	$branches{$f} = (defined $config->{$f}->{'code'}) ?
		$config->{$f}->{'code'} : $config->{'General'}->{'default_code'};
}

$time = time;
my $advance = 1;

while( @ARGV ) {
	$_= shift( @ARGV );
	last if m/^--$/;
	if (!/^-/) {
		unshift(@ARGV,$_);
		last;
	}
	elsif (/^(-\?|-h|--help|--version)$/) {
		print _g("-m|--month - numbers of months to advance.");
		exit( 0 );
	}
	elsif (/^(-m|--month)$/) {
		$advance = shift;
	}
}

my $advmonth = `LC_ALL=C date -d+${advance}month +"%m"`;
chomp ($advmonth);
my $advyear = `LC_ALL=C date -d+${advance}month +"%Y"`;
chomp ($advyear);
$month = $advmonth;
$year = $advyear;
$template = "%Y-%m";
$filter = "${year}-${month}";
my $tmp = `mktemp /tmp/invoice.XXXXXX`;
chomp ($tmp);
my $catstr = ($category ne '') ? "-c $category" : "";
system ("pilot-qof $catstr -i $basefile -t $filter -d pilot_datebook -w $tmp");
my %obj = QSFParse("$tmp");
my $datebook = $obj{'pilot_datebook'};
%obj = ();
my %hdates=();
foreach my $d (@$datebook)
{
	$template = "%Y-%m-%d";
	my $date = time2str($template, $d->start_time);
	$hdates{$date} = $d->start_time;
}

@dates = sort keys %hdates;
printf _g("Found %d working days in %s.\n"), scalar @dates, $filter;
unlink ($tmp);
my %payment = ();
my %invoiced = ();
my $template = "%A, %o %B %Y";
my $x = new Number::Format;
my $grand = 0;
my $table = Text::FormatTable->new('|20l|5l|12l|34l|10r|');
print "\n";
$table->rule('-');
$table->head(_g("Branch Name"), "#", _g("Date"), _g("Details"), _g("Amount"));
my $total_amount = 0;
$table->rule('=');
my %db = ();
my %guid=();
foreach my $d (@$datebook)
{
	$template = "%Y-%m-%d";
	$date = time2str($template, $d->start_time);
	$template = "%a %e %b";
	$datestr = time2str($template, $d->start_time);
	my $hours = ($d->end_time - $d->start_time)/(60*60);
	next if (exists $guid{$d->guid});
	$guid{$d->guid}++;
	my $thisrate = (exists $rates{$d->description}) ?
		$rates{$d->description} : $config->{'General'}->{'default_rate'};
	# bug - description needs 0.05
	$total_amount += $thisrate * $hours;
	$amount = $x->format_price(($thisrate * $hours), 2, 'CURRENCY_SYMBOL');
	$thisrate = $x->format_price($thisrate, 2, 'CURRENCY_SYMBOL');
	chomp ($amount);
	my %c = ('description', $d->description,
		'date', $datestr,
		'hours', $hours,
		'rate', $thisrate,
		'amount', $amount,);
	$db{$d->start_time} = \%c;
}
foreach my $d (sort keys %db)
{
	my $code = (not exists $branches{$db{$d}{'description'}}) ?
		"" : $branches{$db{$d}{'description'}};
	$table->row($db{$d}{'description'}, $code,
		$db{$d}{'date'}, $db{$d}{'hours'}." hours at ". $db{$d}{'rate'}.
		" per hour", $db{$d}{'amount'});
}
$amount = $x->format_price($total_amount, 2, 'CURRENCY_SYMBOL');
$table->rule('-');
$table->row(_g("Total"), "", "", _g("(without expenses)"), $amount);
$grand += $total_amount;

$table->rule('-');
print $table->render(20);
$amount = $x->format_price($grand, 2, 'CURRENCY_SYMBOL');
# Translators: vars are: year, month, amount
printf _g("Estimated total for %d-%d: %s"), $year, $month, ": ".$amount."\n\n";

sub _g {
	return gettext(shift);
}

=head1 NAME

dfinvoice-future - parse a QSF XML file show future bookings

=head1 SYNOPSIS

 dfinvoice-future [-m|--month INTEGER]

=head1 Summary

Using a configuration file in the user home directory,
F</home/user/.datafreedom/invoice.conf>, the script reads the
Palm data synchronised using C<pilot-qof --invoice-city>, combines the
data with location and rate details in the configuration file and
outputs a table summarising the invoices and costs.

Example configuration:

 [General]
 mileage=0.30
 default_rate=24
 default_code=0
 basefile=/home/user/pilot-qof/offline.xml
 category=mybusiness

 [Acacia Avenue]
 rate=25
 code=1234

This configuration sets a mileage rate (expenses charged for personal
transport for business purposes, per unit distance) and a default
hourly rate for the work itself. Other expenses are read in from the
C<pilot-qof> data in the F<basefile>. If C<category> is set, the string
is passed to the C<--category> option of C<pilot-qof> to isolate
specific categories of appointments, expenses and contacts from the rest
of your data. (Only one category is supported.)

For places where the rates differ, location-specific sections can be
added - the hourly rate overrides the default rate and the code is
meant to be a shorthand for the location itself. e.g. if Head Office
etc. puts a code in the invoice payment notice to represent the location
or the cost code and similar, to help you identify the appropriate line
of content in the notice itself.

=head1 Issues

The simple table formatting used in the output appears to have a minor
issue with UTF-8 - if the currency symbol for your locale uses UTF-8,
the rows using the symbol will be shortened by one character per use
of the UTF-8 symbol, causing the column markers to be mis-aligned.

=head1 DATAFREEDOM

These scripts developed from the 'pilot-qof' package but now include
support for other packages and formats and will continue to be extended
along the lines of http://www.data-freedom.org/ - liberating user data 
from the application. Therefore, the datafreedom scripts use a 'df' prefix.

The scripts continue to be developed within the pilot-qof CVS until such
time as the scripts are sufficiently cohesive to form a new source package.

Please feel free to contribute any of your own scripts, under the provisions
of the GNU General Public Licence v3 or later, via the QOF-devel mailing list.
http://lists.sourceforge.net/lists/listinfo/qof-devel

=head1 VERSION

Version 0.0.1

=head1 OBJECTS

pilot_expenses is part of pilot-qof.
Can also be used with gpe-expenses - compatibility with the
default SQLite gpe-expenses backend is pending.

L<http://qof.sourceforge.net/>

L<http://pilot-qof.sourceforge.net/>

L<http://gpe-expenses.sourceforge.net/>

=head1 AUTHOR

Neil Williams, C<< <codehelp at debian.org> >>

=head1 BUGS

Please report bugs via the datafreedom-perl package
in the Debian BTS or via the pilot-qof project and the
SourceForge trackers.
