021761ee989823aef451c5ce54595ed6b768ee55
[kivitendo-erp.git] / bin / mozilla / vk.pl
1 #=====================================================================
2 # LX-Office ERP
3 # Copyright (C) 2004
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
6 #
7 #=====================================================================
8 # SQL-Ledger Accounting
9 # Copyright (c) 2001
10 #
11 #  Author: Dieter Simader
12 #   Email: dsimader@sql-ledger.org
13 #     Web: http://www.sql-ledger.org
14 #
15 #
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
20 #
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #======================================================================
29 #
30 # Sales report
31 #
32 #======================================================================
33
34 use POSIX qw(strftime);
35 use List::Util qw(sum first);
36
37 use SL::VK;
38 use SL::ReportGenerator;
39 use Data::Dumper;
40
41 require "bin/mozilla/arap.pl";
42 require "bin/mozilla/common.pl";
43 require "bin/mozilla/drafts.pl";
44 require "bin/mozilla/reportgenerator.pl";
45
46 use strict;
47
48
49 sub search_invoice {
50   $main::lxdebug->enter_sub();
51   $main::auth->assert('general_ledger | invoice_edit');
52
53   my $form     = $main::form;
54   my %myconfig = %main::myconfig;
55   my $locale   = $main::locale;
56   my $cgi      = $main::cgi;
57
58   my ($customer, $department);
59
60   # setup customer selection
61   $form->all_vc(\%myconfig, "customer", "AR");
62
63   $form->{title}    = $locale->text('Sales Report');
64   $form->{jsscript} = 1;
65
66   $form->get_lists("projects"     => { "key" => "ALL_PROJECTS", "all" => 1 },
67                    "departments"  => "ALL_DEPARTMENTS",
68                    "customers"    => "ALL_VC");
69
70   $form->{vc_keys}   = sub { "$_[0]->{name}--$_[0]->{id}" };
71
72   $form->header;
73   print $form->parse_html_template('vk/search_invoice', { %myconfig });
74
75   $main::lxdebug->leave_sub();
76 }
77
78 sub invoice_transactions {
79   $main::lxdebug->enter_sub();
80
81   $main::auth->assert('general_ledger | invoice_edit');
82
83   my $form     = $main::form;
84   my %myconfig = %main::myconfig;
85   my $locale   = $main::locale;
86
87   my ($callback, $href, @columns);
88
89   $form->{customer} = $form->unescape($form->{customer});
90   
91   ($form->{customername}, $form->{customer_id}) = split(/--/, $form->{customer});
92
93   # decimalplaces überprüfen oder auf Default 2 setzen
94   $form->{decimalplaces} = 2 unless $form->{decimalplaces} > 0 && $form->{decimalplaces} < 6;
95
96 #  report_generator_set_default_sort('transdate', 1);
97
98   VK->invoice_transactions(\%myconfig, \%$form);
99
100   # anhand von radio button die Sortierreihenfolge festlegen
101   if ($form->{sortby} eq 'artikelsort') {
102     $form->{'mainsort'} = 'parts_id';
103     $form->{'subsort'}  = 'name';
104   } else {
105     $form->{'mainsort'} = 'name';
106     $form->{'subsort'}  = 'parts_id';
107   };
108
109   $form->{title} = $locale->text('Sales Report');
110
111   @columns =
112     qw(description invnumber partnumber parts_id transdate qty unit sellprice sellprice_total discount lastcost lastcost_total marge_total marge_percent);
113
114   # hidden variables für pdf/csv export übergeben
115   # einmal mit l_ um zu bestimmen welche Spalten ausgegeben werden sollen
116   # einmal optionen für die Überschrift (z.B. transdatefrom, partnumber, ...)
117   my @hidden_variables  = (qw(l_headers l_subtotal l_total transdatefrom transdateto decimalplaces customer customername customer_id department partnumber description project_id), "$form->{db}number", map { "l_$_" } @columns);
118   my @hidden_nondefault = grep({ $form->{$_} } @hidden_variables);
119   # Variablen werden dann als Hidden Variable mitgegeben, z.B.
120   # <input type="hidden" name="report_generator_hidden_transdateto" value="21.05.2010">
121
122   $href = build_std_url('action=invoice_transactions', grep { $form->{$_} } @hidden_variables);
123   # href = vk.pl?action=invoice_transactions&l_headers=Y&l_subtotal=Y&l_total=Y&transdatefrom=04.03.2010 ...
124
125   my %column_defs = (
126     'description'             => { 'text' => $locale->text('Description'), },
127     'partnumber'              => { 'text' => $locale->text('Part Number'), },
128     'invnumber'               => { 'text' => $locale->text('Invoice Number'), },
129     'transdate'               => { 'text' => $locale->text('Invoice Date'), },
130     'qty'                     => { 'text' => $locale->text('Quantity'), },
131     'unit'                    => { 'text' => $locale->text('Unit'), },
132     'sellprice'               => { 'text' => $locale->text('Sales price'), },
133     'sellprice_total'         => { 'text' => $locale->text('Sales net amount'), },
134     'lastcost_total'          => { 'text' => $locale->text('Purchase net amount'), },
135     'discount'                => { 'text' => $locale->text('Discount'), },
136     'lastcost'                => { 'text' => $locale->text('Purchase price'), },
137     'marge_total'             => { 'text' => $locale->text('Sales margin'), },
138     'marge_percent'           => { 'text' => $locale->text('Sales margin %'), },
139   );
140   
141   my %column_alignment = map { $_ => 'right' } qw(lastcost sellprice sellprice_total lastcost_total unit discount marge_total marge_percent qty);
142
143   $form->{"l_type"} = "Y";
144   map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
145
146
147   my @options;
148   if ($form->{description}) {
149     push @options, $locale->text('Description') . " : $form->{description}";
150   }
151   if ($form->{customer}) {
152     push @options, $locale->text('Customer') . " : $form->{customername}";
153   }
154   if ($form->{department}) {
155     my ($department) = split /--/, $form->{department};
156     push @options, $locale->text('Department') . " : $department";
157   }
158   if ($form->{invnumber}) {
159     push @options, $locale->text('Invoice Number') . " : $form->{invnumber}";
160   }
161   if ($form->{invdate}) {
162     push @options, $locale->text('Invoice Date') . " : $form->{invdate}";
163   }
164   if ($form->{partnumber}) {
165     push @options, $locale->text('Part Number') . " : $form->{partnumber}";
166   }
167   if ($form->{ordnumber}) {
168     push @options, $locale->text('Order Number') . " : $form->{ordnumber}";
169   }
170   if ($form->{notes}) {
171     push @options, $locale->text('Notes') . " : $form->{notes}";
172   }
173   if ($form->{transaction_description}) {
174     push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
175   }
176   if ($form->{transdatefrom}) {
177     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1);
178   }
179   if ($form->{transdateto}) {
180     push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1);
181   }
182
183   my $report = SL::ReportGenerator->new(\%myconfig, $form);
184
185   $report->set_options('top_info_text'        => join("\n", @options),
186                        'output_format'        => 'HTML',
187                        'title'                => $form->{title},
188                        'attachment_basename'  => $locale->text('Sales Report') . strftime('_%Y%m%d', localtime time),
189     );
190   $report->set_options_from_form();
191
192   $report->set_columns(%column_defs);
193   $report->set_column_order(@columns);
194
195   $report->set_export_options('invoice_transactions', @hidden_variables, qw(mainsort sortdir));
196
197   $report->set_sort_indicator($form->{mainsort}, $form->{sortdir});
198
199   # add sort and escape callback, this one we use for the add sub
200   $form->{callback} = $href .= "&sort=$form->{mainsort}";
201
202   # escape callback for href
203   $callback = $form->escape($href);
204
205   my @subtotal_columns = qw(qty sellprice sellprice_total lastcost lastcost_total marge_total marge_percent discount);
206   # Gesamtsumme:
207   # Summe von sellprice_total, lastcost_total und marge_total
208   # Durchschnitt von marge_percent
209   my @total_columns = qw(sellprice_total lastcost_total marge_total marge_percent );
210
211   my %totals    = map { $_ => 0 } @total_columns;
212   my %subtotals1 = map { $_ => 0 } @subtotal_columns;
213   my %subtotals2 = map { $_ => 0 } @subtotal_columns;
214
215   my $idx = 0;
216
217   foreach my $ar (@{ $form->{AR} }) {
218
219     $ar->{price_factor} = 1 unless $ar->{price_factor};
220     # calculate individual sellprice
221     # discount was already accounted for in db sellprice
222     $ar->{sellprice} = $ar->{sellprice} / $ar->{price_factor};
223     $ar->{lastcost} = $ar->{lastcost} / $ar->{price_factor};
224     $ar->{sellprice_total} = $ar->{qty} * $ar->{sellprice};
225     $ar->{lastcost_total}  = $ar->{qty} * $ar->{lastcost}; 
226     # marge_percent wird neu berechnet, da Wert in invoice leer ist (Bug)
227     $ar->{marge_percent} = $ar->{sellprice_total} ? (($ar->{sellprice_total}-$ar->{lastcost_total}) / $ar->{sellprice_total}) : 0;
228     # marge_total neu berechnen
229     $ar->{marge_total} = $ar->{sellprice_total} ? $ar->{sellprice_total}-$ar->{lastcost_total}  : 0;
230     $ar->{discount} *= 100;  # für Ausgabe formatieren, 10% stored as 0.1 in db
231
232     # Anfangshauptüberschrift
233     if ( $form->{l_headers} eq "Y" && ( $idx == 0 or $ar->{ $form->{'mainsort'} } ne $form->{AR}->[$idx - 1]->{ $form->{'mainsort'} } )) {
234       my $name;
235       my $headerrow;
236       if ( $form->{mainsort} eq 'parts_id' ) {
237         $headerrow->{description}->{data} = "$ar->{description}";
238       } else {
239         $headerrow->{description}->{data} = "$ar->{name}";
240       };
241       $headerrow->{description}->{class} = "listmainsortheader";
242       my $headerrow_set = [ $headerrow ];
243       $report->add_data($headerrow_set);
244
245       # add empty row after main header
246 #      my $emptyheaderrow->{description}->{data} = ""; 
247 #      $emptyheaderrow->{description}->{class} = "listmainsortheader";
248 #      my $emptyheaderrow_set = [ $emptyheaderrow ];
249 #      $report->add_data($emptyheaderrow_set) if $form->{l_headers} eq "Y"; 
250     };
251
252     # subsort überschriften
253     if ( $idx == 0 
254       or $ar->{ $form->{'subsort'} }  ne $form->{AR}->[$idx - 1]->{ $form->{'subsort'} }
255       or $ar->{ $form->{'mainsort'} } ne $form->{AR}->[$idx - 1]->{ $form->{'mainsort'} }
256     ) {
257       my $headerrow;
258       my $name;
259       if ( $form->{subsort} eq 'parts_id' ) {
260         $name = 'description';
261         $headerrow->{description}->{data} = "$ar->{$name}";
262       } else {
263         $name = 'name';
264         $headerrow->{description}->{data} = "$ar->{$name}";
265       };
266       $headerrow->{description}->{class} = "listsubsortheader";
267       my $headerrow_set = [ $headerrow ];
268       $report->add_data($headerrow_set) if $form->{l_headers} eq "Y";
269     };
270
271     map { $subtotals1{$_} += $ar->{$_};
272           $subtotals2{$_} += $ar->{$_};
273         } @subtotal_columns;
274          
275     map { $totals{$_}    += $ar->{$_} } @total_columns;  
276
277     $subtotals2{sellprice} = $subtotals2{sellprice_total} / $subtotals2{qty} if $subtotals2{qty} != 0;
278     $subtotals1{sellprice} = $subtotals1{sellprice_total} / $subtotals1{qty} if $subtotals1{qty} != 0;
279     $subtotals2{lastcost} = $subtotals2{lastcost_total} / $subtotals2{qty} if $subtotals2{qty} != 0;
280     $subtotals1{lastcost} = $subtotals1{lastcost_total} / $subtotals1{qty} if $subtotals1{qty} != 0;
281
282     # Ertrag prozentual in den Summen: (summe VK - summe Ertrag) / summe VK
283     $subtotals1{marge_percent} = $subtotals1{sellprice_total} ? (($subtotals1{sellprice_total} - $subtotals1{lastcost_total}) / $subtotals1{sellprice_total}) : 0;
284     $subtotals2{marge_percent} = $subtotals2{sellprice_total} ? (($subtotals2{sellprice_total} - $subtotals2{lastcost_total}) / $subtotals2{sellprice_total}) : 0;
285
286     # Ertrag prozentual:  (Summe VK betrag - Summe EK betrag) / Summe VK betrag
287     # wird laufend bei jeder Position neu berechnet
288     $totals{marge_percent}    = $totals{sellprice_total}    ? ( ($totals{sellprice_total} - $totals{lastcost_total}) / $totals{sellprice_total}   ) : 0;
289
290     map { $ar->{$_} = $form->format_amount(\%myconfig, $ar->{$_}, 2) } qw(marge_total marge_percent);
291     map { $ar->{$_} = $form->format_amount(\%myconfig, $ar->{$_}, $form->{"decimalplaces"} )} qw(lastcost sellprice sellprice_total lastcost_total);
292
293     my $row = { };
294
295     foreach my $column (@columns) {
296       $row->{$column} = {
297         'data'  => $ar->{$column},
298         'align' => $column_alignment{$column},
299       };
300     }
301   
302    $row->{description}->{class} = 'listsortdescription';
303
304     $row->{invnumber}->{link} = build_std_url("script=is.pl", 'action=edit')
305       . "&id=" . E($ar->{id}) . "&callback=${callback}";
306
307     my $row_set = [ $row ];
308
309     if (($form->{l_subtotal} eq 'Y')
310         && (($idx == (scalar @{ $form->{AR} } - 1))   # last element always has a subtotal
311           || ($ar->{ $form->{'subsort'} } ne $form->{AR}->[$idx + 1]->{ $form->{'subsort'}   })
312           || ($ar->{ $form->{'mainsort'} } ne $form->{AR}->[$idx + 1]->{ $form->{'mainsort'} })
313           )) {   # if value that is sorted by changes, print subtotal
314       my $name;
315       if ( $form->{subsort} eq 'parts_id' ) {
316         $name = 'description';
317       } else {
318         $name = 'name';
319       };
320       
321       if ($form->{l_subtotal} eq 'Y' ) {
322         push @{ $row_set }, create_subtotal_row_invoice(\%subtotals2, \@columns, \%column_alignment, \@subtotal_columns, 'listsubsortsubtotal', $ar->{$name}) ;
323         push @{ $row_set }, insert_empty_row();
324       };
325     }
326
327     # if mainsort has changed, add mainsort subtotal and empty row 
328     if (($form->{l_subtotal} eq 'Y')
329         && (($idx == (scalar @{ $form->{AR} } - 1))   # last element always has a subtotal
330             || ($ar->{ $form->{'mainsort'} } ne $form->{AR}->[$idx + 1]->{ $form->{'mainsort'} })
331             )) {   # if value that is sorted by changes, print subtotal
332       my $name;
333       if ( $form->{mainsort} eq 'parts_id' ) {
334         $name = 'description';
335       } else {
336         $name = 'name';
337       };
338       if ($form->{l_subtotal} eq 'Y' ) {
339         push @{ $row_set }, create_subtotal_row_invoice(\%subtotals1, \@columns, \%column_alignment, \@subtotal_columns, 'listmainsortsubtotal', $ar->{$name});
340         push @{ $row_set }, insert_empty_row();
341       };
342     }
343   
344     $report->add_data($row_set);
345
346     $idx++;
347   }
348   if ( $form->{l_total} eq "Y" ) {
349     $report->add_separator();
350     $report->add_data(create_subtotal_row_invoice(\%totals, \@columns, \%column_alignment, \@total_columns, 'listtotal'))
351   };
352
353   $report->generate_with_headers();
354   $main::lxdebug->leave_sub();
355 }
356
357
358 sub insert_empty_row {
359     my $dummyrow;
360     $dummyrow->{description}->{data} = "";
361     my $dummyrowset = [ $dummyrow ];
362     return $dummyrow;
363 };
364
365
366
367 sub create_subtotal_row_invoice {
368   $main::lxdebug->enter_sub();
369
370   my ($totals, $columns, $column_alignment, $subtotal_columns, $class, $name) = @_;
371
372   my $form     = $main::form;
373   my %myconfig = %main::myconfig;
374
375   my $row = { map { $_ => { 'data' => '', 'class' => $class, 'align' => $column_alignment->{$_}, } } @{ $columns } };
376   
377   $row->{description}->{data} = "Summe " . $name;
378
379   map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, 2) } qw(marge_total marge_percent);
380   map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, 0) } qw(qty);
381   map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, $form->{decimalplaces}) } qw(lastcost sellprice sellprice_total lastcost_total);
382
383
384   map { $totals->{$_} = 0 } @{ $subtotal_columns };
385
386   $main::lxdebug->leave_sub();
387
388   return $row;
389 }
390
391 1;
392