#!/usr/bin/perl -w use strict; use YAML qw/LoadFile/; use XML::Simple; use Template; use DateTime; use Math::BigFloat; #use Data::Dump qw/dd/; my $config_file = '/usr/share/mga-treasurer/config'; my $config = LoadFile($ENV{MGATRES_CONF} ? $ENV{MGATRES_CONF} : $config_file); my $etc_config_file = '/etc/mga-treasurer.conf'; my $etc_config = LoadFile($etc_config_file); foreach my $k (keys %{$etc_config}) { $config->{$k} = $etc_config->{$k}; } sub donation_category { my $r = shift; my @c = grep {$_->{Na} eq 'Donation'} @{$r->{Category}}; return @c ? $c[0]->{Nb} : undef; } sub party_name { my ($r, $Nb) = @_; $Nb == 0 ? '' : (grep { $_->{Nb} == $Nb } @{$r->{Party}})[0]->{Na}; } sub cat_name { my ($r, $Nb) = @_; $Nb == 0 ? '' : (grep { $_->{Nb} == $Nb } @{$r->{Category}})[0]->{Na}; } sub subcat_name { my ($r, $Nb, $Nbc) = @_; (grep { $_->{Nb} == $Nb && $_->{Nbc} == $Nbc } @{$r->{Sub_category}})[0]->{Na}; } sub account_name { my ($r, $Ac) = @_; (grep { $_->{Number} == $Ac } @{$r->{Account}})[0]->{Name}; } sub epoch_date { my ($m, $d, $y) = split /\//, shift; DateTime->new(day => $d, month => $m, year => $y)->epoch; } sub transactions_list { my ($r, $cat) = @_; my @res; my @transactions = $cat ? grep { $_->{Ca} == $cat } @{$r->{Transaction}} : @{$r->{Transaction}}; foreach my $tra (@transactions) { my %d = ( nb => $tra->{Nb}, amount => (new Math::BigFloat $tra->{Am}), date => epoch_date($tra->{Dt}), pa => $tra->{Pa}, who => party_name($r, $tra->{Pa}), ac => $tra->{Ac}, account => account_name($r, $tra->{Ac}), $tra->{Trt} != 0 ? (trt => $tra->{Trt}) : (), $tra->{Sca} != 0 ? (sc_name => subcat_name($r, $tra->{Sca}, $tra->{Ca})) : (), $tra->{Ca} != 0 ? (c_name => cat_name($r, $tra->{Ca}), ca => $tra->{Ca}) : (), $tra->{No} ne '(null)' ? (notes => $tra->{No}) : (), ); push @res, \%d; } return \@res; } sub donations_infos { my $r = shift; my %res = ( donations => transactions_list($r, donation_category($r)), by_month => {}, by_year => {}, by_pa => {}, total_30 => 0, total => 0, sorted_pa_by_total => [], ); foreach my $don (@{$res{donations}}) { my $dt = DateTime->from_epoch(epoch => $don->{date}); my $month = $dt->year . '-' . sprintf("%02d", $dt->month); my $year = $dt->year; push @{$res{by_month}->{$month}->{donations}}, $don; $res{by_month}->{$month}->{year} = $year; $res{by_month}->{$month}->{month} = $dt->month; push @{$res{by_year}->{$year}->{donations}}, $don; push @{$res{by_year}->{$year}->{months}}, $month unless grep { $_ eq $month } @{$res{by_year}->{$year}->{months}}; push @{$res{by_pa}->{$don->{pa}}->{donations}}, $don; $res{by_pa}->{$don->{pa}}->{who} = $don->{who}; if (time - $don->{date} < '2592000') { # last 30 days $res{total_30} += $don->{amount}; } $res{total} += $don->{amount}; } foreach my $d ((values %{$res{by_month}}), (values %{$res{by_year}}), (values %{$res{by_pa}})) { foreach my $don (@{$d->{donations}}) { $d->{total} += $don->{amount}; $d->{type}->{$don->{sc_name}} += $don->{amount}; } } @{$res{sorted_pa_by_total}} = sort { $res{by_pa}->{$b}->{total} <=> $res{by_pa}->{$a}->{total} } keys %{$res{by_pa}}; return \%res; } sub transactions_infos { my $r = shift; my %res = ( transactions => transactions_list($r), by_month => {}, by_year => {}, by_pa => {}, by_ac => {}, by_ca => {}, balance => new Math::BigFloat 0, ); foreach my $tra (@{$res{transactions}}) { my $dt = DateTime->from_epoch(epoch => $tra->{date}); my $month = $dt->year . '-' . sprintf("%02d", $dt->month); my $year = $dt->year; push @{$res{by_month}->{$month}->{transactions}}, $tra; $res{by_month}->{$month}->{year} = $year; $res{by_month}->{$month}->{month} = $dt->month; $res{by_ac}->{$tra->{ac}}->{account} = $tra->{account}; push @{$res{by_year}->{$year}->{transactions}}, $tra; push @{$res{by_year}->{$year}->{months}}, $month unless grep { $_ eq $month } @{$res{by_year}->{$year}->{months}}; push @{$res{by_pa}->{$tra->{pa}}->{transactions}}, $tra; push @{$res{by_ac}->{$tra->{ac}}->{transactions}}, $tra; if ($tra->{ca}) { push @{$res{by_ca}->{$tra->{ca}}->{transactions}}, $tra; $res{by_ca}->{$tra->{ca}}->{c_name} = $tra->{c_name}; } $res{balance} += $tra->{amount}; } foreach my $by ('by_ac', 'by_year', 'by_month', 'by_ca', 'by_pa') { foreach my $t (values %{$res{$by}}) { $t->{balance} = new Math::BigFloat 0; $t->{revenues} = new Math::BigFloat 0; $t->{expenses} = new Math::BigFloat 0; foreach my $tra (@{$t->{transactions}}) { $t->{balance} += $tra->{amount}; next if $tra->{trt} && $by ne 'by_ac'; if ($tra->{amount} > 0) { $t->{revenues} += $tra->{amount}; } else { $t->{expenses} -= $tra->{amount}; } } } } foreach my $by ('by_year', 'by_month') { my ($prev, $start_balance); foreach my $b (sort keys %{$res{$by}}) { my $y = $res{$by}->{$b}; foreach my $tra (@{$y->{transactions}}) { next unless $tra->{ca}; $y->{ca_balance}->{$tra->{ca}} += $tra->{amount}; if ($tra->{amount} > 0) { $y->{ca_revenues}->{$tra->{ca}} += $tra->{amount}; } else { $y->{ca_expenses}->{$tra->{ca}} -= $tra->{amount}; } } $y->{start_balance} = $start_balance ? $start_balance : new Math::BigFloat 0; $y->{end_balance} = $y->{balance} + new Math::BigFloat $y->{start_balance}; $start_balance = $y->{end_balance}; $y->{prev} = $prev if $prev; $prev = $b; } } return \%res; } sub process_template { my ($template, $src, $vars, $dest) = @_; foreach my $extension (@{$config->{output_format}}) { next unless -f "$config->{tmpl_dir}/$src.$extension"; $template->process("$src.$extension", $vars, "$dest.$extension") || die $template->error(), "\n"; } } sub output_pages { my ($r, $donations, $transactions) = @_; my $template = Template->new( INCLUDE_PATH => $config->{tmpl_dir}, OUTPUT_PATH => $config->{out_dir}, ); my $last_update = (stat $config->{grisbi_file})[9]; foreach my $month (keys %{ { %{$donations->{by_month}}, %{$transactions->{by_month}} } }) { my $vars = { config => $config, month => $month, donations => $donations, transactions => $transactions, }; process_template($template, 'donations_by_month', $vars, "donations_m_$month") if $donations->{by_month}->{$month}; process_template($template, 'transactions_by_month', $vars, "m_$month") if $transactions->{by_month}->{$month}; } foreach my $year (keys %{ { %{$donations->{by_year}}, %{$transactions->{by_year}} } }) { my $vars = { config => $config, year => $year, donations => $donations, transactions => $transactions, }; process_template($template, 'donations_by_year', $vars, "donations_y_$year") if $donations->{by_year}->{$year}; process_template($template, 'transactions_by_year', $vars, "y_$year") if $transactions->{by_year}->{$year}; } foreach my $pa (keys %{ { %{$donations->{by_pa}}, %{$transactions->{by_pa}} } }) { my $vars = { config => $config, pa => $pa, who => party_name($r, $pa), donations => $donations, transactions => $transactions, }; process_template($template, 'donations_by_pa', $vars, "donations_p_$pa") if $donations->{by_pa}->{$pa}; process_template($template, 'transactions_by_pa', $vars, "p_$pa") if $transactions->{by_pa}->{$pa}; } foreach my $ac (keys %{$transactions->{by_ac}}) { my $vars = { config => $config, ac => $ac, account => $transactions->{by_ac}->{$ac}->{account}, donations => $donations, transactions => $transactions, }; process_template($template, 'transactions_by_ac', $vars, "a_$ac"); } foreach my $ca (keys %{$transactions->{by_ca}}) { my $vars = { config => $config, ca => $ca, c_name => $transactions->{by_ca}->{$ca}->{c_name}, donations => $donations, transactions => $transactions, }; process_template($template, 'transactions_by_ca', $vars, "c_$ca"); } my $vars = { config => $config, donations => $donations, transactions => $transactions, last_update => $last_update, }; process_template($template, 'donations', $vars, 'donations'); process_template($template, 'donations_all', $vars, 'donations_all'); process_template($template, 'donations_who', $vars, 'donations_who'); process_template($template, 'transactions', $vars, 'transactions'); process_template($template, 'index', $vars, 'index'); } my $r = XMLin($config->{grisbi_file}); my $donations = donations_infos($r); my $transactions = transactions_infos($r); output_pages($r, $donations, $transactions);