Detect whether STDOUT is a tty unless --force-oolor is given
[infodrom.org/service.infodrom.org] / src / InfoCon / buch / vorsteueranmeldung
1 #! /usr/bin/perl
2
3 #  infocon - Administration tool for InfoCon
4 #  Copyright (c) 2000-2016  Joey Schulze <joey@infodrom.org>
5 #
6 #  This program is free software; you can redistribute it and/or modify
7 #  it under the terms of the GNU General Public License as published by
8 #  the Free Software Foundation; either version 2 of the License, or
9 #  (at your option) any later version.
10 #
11 #  This program is distributed in the hope that it will be useful,
12 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #  GNU General Public License for more details.
15 #
16 #  You should have received a copy of the GNU General Public License
17 #  along with this program; if not, write to the Free Software
18 #  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
19
20 use strict;
21 use warnings;
22
23 use DBI;
24 use Switch;
25 use Getopt::Long;
26 use Config::Simple;
27 use Term::ANSIColor;
28 use Scalar::Util qw/looks_like_number/;
29
30 use constant CONFIG => '/etc/infocon.cfg';
31
32 my $opt_mode = 'month';
33 my $opt_nocolor = 0;
34 my $opt_forcecolor = 0;
35 my $opt_start;
36 my $opt_end;
37 my $cfg;
38 my $dbh;
39
40 sub help()
41 {
42     print <<EOT;
43 vorsteueranmeldung [-h|--help] [-q|--quarter] [-m|--month] [year quarter|month] [--no-color]
44 EOT
45     exit;
46 }
47
48 sub cprint
49 {
50     print color $_[0] unless $opt_nocolor;
51     print $_[1];
52     print color 'reset' unless $opt_nocolor;
53 }
54
55 sub calculate_start_end_quarter
56 {
57     my $year;
58     my $month;
59
60     if (scalar @_ == 2) {
61         $year = $_[0];
62         switch ($_[1]) {
63             case 1 { $month = 4; }
64             case 2 { $month = 7; }
65             case 3 { $month = 10; }
66             case 4 { $month = 13; }
67         }
68     } else {
69         my @now = localtime;
70         $year = $now[5] + 1900;
71         $month = $now[4] + 1;
72     }
73
74     if ($month % 3 == 1) {
75         if ($month < 13 ) {
76             $opt_start = sprintf('%04d%02d01', $year, $month - 3);
77             $opt_end = sprintf('%04d%02d01', $year, $month);
78         } else {
79             $opt_start = sprintf('%04d1001', $year);
80             $opt_end = sprintf('%04d0101', $year + 1);
81         }
82     } else {
83         die "No automatic quarter calculation possible\n";
84     }
85 }
86
87 sub calculate_start_end_month
88 {
89     my $year;
90     my $month;
91
92     if (scalar @_ == 2) {
93         $year = $_[0];
94         $month = $_[1];
95     } else {
96         my @now = localtime;
97         $year = $now[5] + 1900;
98         $month = $now[4];
99     }
100
101     if ($month > 0 && $month < 13) {
102         if ($month < 12 ) {
103             $opt_start = sprintf('%04d%02d01', $year, $month);
104             $opt_end = sprintf('%04d%02d01', $year, $month + 1);
105         } else {
106             $opt_start = sprintf('%04d%02d01', $year, $month);
107             $opt_end = sprintf('%04d0101', $year + 1);
108         }
109     } else {
110         die "No automatic month calculation possible\n";
111     }
112 }
113
114 sub calculate_start_end
115 {
116     calculate_start_end_quarter @_ if $opt_mode eq 'quarter';
117     calculate_start_end_month @_ if $opt_mode eq 'month';
118 }
119
120 sub print_table
121 {
122     my $dbh = shift;
123     my $sql = shift;
124     my @length;
125
126     return unless $sql =~ /SELECT\s+(.*?)\s+FROM/is;
127     my @columns = split /,/, $1;
128     my $i=0; while ($i < @columns) {
129         $columns[$i] = $1 if $columns[$i] =~ /.*\sAS\s(.*)/is;
130         $i++;
131     }
132
133     push @length, length $_ foreach @columns;
134
135     my $rows = $dbh->selectall_arrayref($sql);
136
137     return unless scalar @$rows && scalar @{$rows->[0]} && defined $rows->[0][0];
138
139     foreach my $row (@$rows) {
140         $i=0; while ($i < @$row) {
141             $row->[$i] = '' unless defined $row->[$i];
142             $length[$i] = length $row->[$i] if length $row->[$i] > $length[$i];
143             $i++;
144         }
145     }
146
147     my $formatstr = '';
148     my $formatlen = 0;
149     my $tablesep = '';
150     $i=0; while ($i < @columns) {
151         if (length $formatstr) {
152             $formatstr .= '|';
153             $formatlen += 1;
154             $tablesep .= '+';
155         }
156         $formatstr .= sprintf(' %%%s%ds ', (looks_like_number $rows->[0][$i]?'':'-'), $length[$i]);
157         $formatlen += 2 + $length[$i];
158         $tablesep .= '-' x (2 + $length[$i]);
159         $i++;
160     }
161     $formatstr .= "\n";
162     $tablesep .= "\n";
163
164     printf $formatstr, @columns;
165     print $tablesep;
166     printf $formatstr, @$_ foreach @$rows;
167     print "\n";
168 }
169
170 sub print_income
171 {
172     my $dbh = shift;
173     my $sql;
174
175     cprint 'bold yellow', 'Lieferungen (Einnahmen)';
176     print "\n" . "~" x 23 . "\n\n";
177
178     $sql = qq{
179 SELECT date,billing_date AS billing,description,tax_percent,tax_assigned,price
180         FROM sales
181 WHERE billing_date >= '$opt_start' AND billing_date < '$opt_end'
182         AND price > 0.0 AND visible = 1
183         ORDER BY date,nr};
184     print_table $dbh, $sql;
185
186     $sql = qq{
187 SELECT tax_percent as percent,sum(price) AS brutto, sum(price) - sum(tax_assigned) AS netto, sum(tax_assigned) AS tax
188         FROM sales
189 WHERE billing_date >= '$opt_start' AND billing_date < '$opt_end'
190         AND price > 0.0 AND visible = 1
191         GROUP BY tax_percent
192         ORDER BY tax_percent};
193     print_table $dbh, $sql;
194
195     $sql = qq{
196 SELECT sum(price) AS brutto, sum(price) - sum(tax_assigned) AS netto, sum(tax_assigned) AS tax
197         FROM sales
198 WHERE billing_date >= '$opt_start' AND billing_date < '$opt_end'
199         AND price > 0.0 AND visible = 1};
200     print_table $dbh, $sql;
201 }
202
203 sub print_outgoing
204 {
205     my $dbh = shift;
206     my $sql;
207
208     cprint 'bold yellow', 'Abziehbare Vorsteuerbeträge (Ausgaben)';
209     print "\n" . "~" x 38 . "\n\n";
210
211     $sql = qq{
212 SELECT date,billing_date AS billing,description,tax_percent,tax_assigned * -1 as tax_assigned,price * -1 as price
213         FROM sales
214 WHERE billing_date >= '$opt_start' AND billing_date < '$opt_end'
215         AND price < 0.0 AND visible = 1
216         AND category <> 'privat'
217         AND category <> 'tax'
218         ORDER BY date,nr};
219     print_table $dbh, $sql;
220
221     $sql = qq{
222 SELECT tax_percent AS percent,sum(price) * -1 AS brutto, sum(price) * -1 - sum(tax_assigned) * -1 AS netto, sum(tax_assigned) * -1 AS tax
223         FROM sales
224 WHERE billing_date >= '$opt_start' AND billing_date < '$opt_end'
225         AND price < 0.0 AND visible = 1
226         AND category <> 'privat'
227         AND category <> 'tax'
228         GROUP BY tax_percent
229         ORDER BY tax_percent};
230     print_table $dbh, $sql;
231
232     $sql = qq{
233 SELECT sum(price) * -1 AS brutto, sum(price) * -1 - sum(tax_assigned) * -1 AS netto, sum(tax_assigned) * -1 AS tax
234         FROM sales
235 WHERE billing_date >= '$opt_start' AND billing_date < '$opt_end'
236         AND price < 0.0 AND visible = 1
237         AND category <> 'privat'
238         AND category <> 'tax'};
239     print_table $dbh, $sql;
240 }
241
242 sub print_summary
243 {
244     my $dbh = shift;
245     my $sql;
246
247     cprint 'bold yellow', 'Umsatzsteuer-Voranmeldung';
248     print "\n" . "~" x 25 . "\n\n";
249
250     $sql = qq{
251 SELECT '1 Einnahmen' AS type, sum(price) AS brutto, sum(price) - sum(tax_assigned) AS netto, sum(tax_assigned) AS tax
252         FROM sales
253 WHERE billing_date >= '$opt_start' AND billing_date < '$opt_end'
254         AND price > 0.0 AND visible = 1
255 UNION
256 SELECT '2 Ausgaben' AS type, sum(price) * -1 AS brutto, sum(price) * -1 - sum(tax_assigned) * -1 AS netto, sum(tax_assigned) * -1 AS tax
257         FROM sales
258 WHERE billing_date >= '$opt_start' AND billing_date < '$opt_end'
259         AND price < 0.0 AND visible = 1
260         AND category <> 'privat'
261         AND category <> 'tax'
262 UNION
263 SELECT '3 Summe' AS type, sum(price) AS brutto, sum(price) - sum(tax_assigned) AS netto, sum(tax_assigned) AS tax
264         FROM sales
265 WHERE billing_date >= '$opt_start' AND billing_date < '$opt_end'
266         AND visible = 1
267         AND category <> 'privat'
268         AND category <> 'tax'
269 ORDER BY type
270 };
271     print_table $dbh, $sql;
272
273 }
274
275 GetOptions ('help|h' => \&help,
276             'quarter|q' => sub {$opt_mode = 'quarter'},
277             'month|m' => sub {$opt_mode = 'month'},
278             'no-color|nocolor|raw' => \$opt_nocolor,
279             'force-color' => \$opt_forcecolor,
280             'start' => \$opt_start,
281             'end' => \$opt_end)
282     or die("Error in command line arguments\n");
283
284 $cfg = new Config::Simple(CONFIG);
285
286 die "Cannot read config file\n" unless $cfg;
287
288 $opt_nocolor = !(-t STDOUT) unless $opt_forcecolor;
289
290 if (!defined $opt_start || !defined $opt_end) {
291     calculate_start_end @ARGV;
292 }
293
294 die "Unknown date format (yyyymmdd required)\n" unless length $opt_start == 8 && length $opt_end == 8;
295
296 $dbh = DBI->connect('dbi:Pg:dbname='.$cfg->param('local.database'), $cfg->param('local.username'));
297
298 print_income $dbh;
299 print_outgoing $dbh;
300 print_summary $dbh;
301
302 $dbh->disconnect;