Revision 5ef8b310
Von Kivitendo Admin vor etwa 11 Jahren hinzugefügt
bin/mozilla/vk.pl | ||
---|---|---|
31 | 31 |
# |
32 | 32 |
#====================================================================== |
33 | 33 |
|
34 |
# TODO |
|
35 |
# if qty or weight is shown add unit |
|
36 |
|
|
34 | 37 |
use POSIX qw(strftime); |
35 | 38 |
use List::Util qw(sum first); |
36 | 39 |
|
... | ... | |
90 | 93 |
$main::lxdebug->leave_sub(); |
91 | 94 |
} |
92 | 95 |
|
96 |
sub search_invoice_top { |
|
97 |
$main::lxdebug->enter_sub(); |
|
98 |
$main::auth->assert('general_ledger | invoice_edit'); |
|
99 |
|
|
100 |
my $form = $main::form; |
|
101 |
my %myconfig = %main::myconfig; |
|
102 |
my $locale = $main::locale; |
|
103 |
|
|
104 |
my ($customer, $department); |
|
105 |
|
|
106 |
# setup customer selection |
|
107 |
$form->all_vc(\%myconfig, "customer", "AR"); |
|
108 |
|
|
109 |
$form->{title} = $locale->text('Sales Report Top'); |
|
110 |
|
|
111 |
$form->get_lists("projects" => { "key" => "ALL_PROJECTS", "all" => 1 }, |
|
112 |
"departments" => "ALL_DEPARTMENTS", |
|
113 |
"business_types" => "ALL_BUSINESS_TYPES", |
|
114 |
"salesmen" => "ALL_SALESMEN", |
|
115 |
'employees' => 'ALL_EMPLOYEES', |
|
116 |
'partsgroup' => 'ALL_PARTSGROUPS', |
|
117 |
"customers" => "ALL_VC"); |
|
118 |
$form->{CUSTOM_VARIABLES_IC} = CVar->get_configs('module' => 'IC'); |
|
119 |
($form->{CUSTOM_VARIABLES_FILTER_CODE_IC}, |
|
120 |
$form->{CUSTOM_VARIABLES_INCLUSION_CODE_IC}) = CVar->render_search_options('variables' => $form->{CUSTOM_VARIABLES_IC}, |
|
121 |
'include_prefix' => 'l_', |
|
122 |
'include_value' => 'Y'); |
|
123 |
|
|
124 |
$form->{CUSTOM_VARIABLES_CT} = CVar->get_configs('module' => 'CT'); |
|
125 |
($form->{CUSTOM_VARIABLES_FILTER_CODE_CT}, |
|
126 |
$form->{CUSTOM_VARIABLES_INCLUSION_CODE_CT}) = CVar->render_search_options('variables' => $form->{CUSTOM_VARIABLES_CT}, |
|
127 |
'include_prefix' => 'l_', |
|
128 |
'include_value' => 'Y'); |
|
129 |
$form->{vc_keys} = sub { "$_[0]->{name}--$_[0]->{id}" }; |
|
130 |
$form->{employee_labels} = sub { $_[0]->{"name"} || $_[0]->{"login"} }; |
|
131 |
$form->{salesman_labels} = $form->{employee_labels}; |
|
132 |
|
|
133 |
$form->header; |
|
134 |
print $form->parse_html_template('vk/search_invoice_top', { %myconfig }); |
|
135 |
|
|
136 |
$main::lxdebug->leave_sub(); |
|
137 |
} |
|
138 |
|
|
93 | 139 |
sub invoice_transactions { |
94 | 140 |
$main::lxdebug->enter_sub(); |
95 | 141 |
|
... | ... | |
99 | 145 |
my %myconfig = %main::myconfig; |
100 | 146 |
my $locale = $main::locale; |
101 | 147 |
|
148 |
my @data_to_be_sorted; |
|
149 |
# debug mode for finding differences in Umsatz as calculated by database and perl |
|
150 |
# set to "Y" to turn on |
|
151 |
$form->{"l_sellpricelinetotal"} = ""; |
|
152 |
|
|
102 | 153 |
my ($callback, $href, @columns); |
103 | 154 |
|
155 |
# sort_by_total mode |
|
156 |
if ( $form->{l_sort_by_total} ) { |
|
157 |
# uncheck Mainheadings |
|
158 |
# check both subheadings |
|
159 |
# set subsort = mainsort |
|
160 |
$form->{l_headers_mainsort} = ''; |
|
161 |
$form->{l_subtotal_mainsort} = "Y"; |
|
162 |
$form->{l_headers_subsort} = ''; |
|
163 |
$form->{l_subtotal_subsort} = "Y"; |
|
164 |
$form->{mainsort} = $form->{topsortgroup}; |
|
165 |
$form->{subsort} = $form->{mainsort}; # trigger subsort = mainsort |
|
166 |
}; |
|
167 |
|
|
104 | 168 |
# can't currently be configured from report, empty line between main sortings |
105 | 169 |
my $addemptylines = 1; |
106 | 170 |
|
... | ... | |
139 | 203 |
VK->invoice_transactions(\%myconfig, \%$form); |
140 | 204 |
|
141 | 205 |
|
206 |
|
|
207 |
|
|
142 | 208 |
if ( $form->{mainsort} eq 'month' or $form->{subsort} eq 'month' ) { |
143 | 209 |
|
144 | 210 |
# Data already comes out of SELECT statement in correct month order, but |
... | ... | |
155 | 221 |
|
156 | 222 |
@columns = |
157 | 223 |
qw(description invnumber transdate customernumber customername partnumber partsgroup country business transdate qty parts_unit weight sellprice sellprice_total discount lastcost lastcost_total marge_total marge_percent employee salesman); |
224 |
push(@columns, "sellpricelinetotal") if $form->{"l_sellpricelinetotal"}; |
|
225 |
|
|
226 |
# sellpricelinetotal is qty*sellprice as calculated by database |
|
158 | 227 |
|
159 | 228 |
my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs_ic }, @{ $cvar_configs_ct }; |
160 | 229 |
my @searchable_custom_variables = grep { $_->{searchable} } @{ $cvar_configs_ic }, @{ $cvar_configs_ct }; |
... | ... | |
169 | 238 |
my @hidden_variables = (qw(l_headers_mainsort l_headers_subsort l_subtotal_mainsort l_subtotal_subsort l_total l_parts l_customername l_customernumber transdatefrom transdateto decimalplaces customer customername customer_id department partnumber partsgroup country business description project_id customernumber salesman employee salesman_id employee_id business_id partsgroup_id mainsort subsort), |
170 | 239 |
"$form->{db}number", |
171 | 240 |
map({ "cvar_$_->{name}" } @searchable_custom_variables), |
172 |
map { "l_$_" } @columns |
|
241 |
map { "l_$_" } @columns,
|
|
173 | 242 |
); |
243 |
push(@hidden_variables, qw(topsortgroup topsorttype l_sort_by_total)); # hidden variables for top |
|
244 |
|
|
174 | 245 |
my @hidden_nondefault = grep({ $form->{$_} } @hidden_variables); |
175 | 246 |
# Variablen werden dann als Hidden Variable mitgegeben, z.B. |
176 | 247 |
# <input type="hidden" name="report_generator_hidden_transdateto" value="21.05.2010"> |
... | ... | |
200 | 271 |
'marge_percent' => { 'text' => $locale->text('Sales margin %'), }, |
201 | 272 |
'customernumber' => { 'text' => $locale->text('Customer Number'), }, |
202 | 273 |
'customername' => { 'text' => $locale->text('Customer Name'), }, |
274 |
'sellpricelinetotal' => { 'text' => $locale->text('sellpricelinetotal'), }, # debug control |
|
203 | 275 |
# add 3 more column_defs so we have a translation for top_info_text |
204 | 276 |
'customer' => { 'text' => $locale->text('Customer'), }, |
205 | 277 |
'part' => { 'text' => $locale->text('Part'), }, |
... | ... | |
207 | 279 |
%column_defs_cvars, |
208 | 280 |
); |
209 | 281 |
|
210 |
map { $column_defs{$_}->{visible} = $form->{"l_$_"} eq 'Y' } @columns; |
|
282 |
if ( not defined $form->{"l_parts"} ) { |
|
283 |
# the columns for sellprice, lastcost and marge_percent will be averages in subtotal and total lines |
|
284 |
map { $column_defs{$_}->{text} = $locale->text('Average symbol') . " " . $column_defs{$_}->{text} } qw(sellprice lastcost marge_percent); |
|
285 |
}; |
|
211 | 286 |
|
212 |
my %column_alignment = map { $_ => 'right' } qw(lastcost sellprice sellprice_total lastcost_total parts_unit discount marge_total marge_percent qty weight);
|
|
287 |
map { $column_defs{$_}->{visible} = $form->{"l_$_"} eq 'Y' } @columns;
|
|
213 | 288 |
|
289 |
my %column_alignment = map { $_ => 'right' } qw(lastcost sellprice sellprice_total lastcost_total parts_unit discount marge_total marge_percent qty weight sellpricelinetotal ); |
|
214 | 290 |
|
215 | 291 |
# so now the check-box "Description" is only used as switch for part description in invoice-mode |
216 | 292 |
# always fill the column "Description" if we are in Zwischensummenmode |
217 |
$form->{"l_description"} = "Y" if not defined $form->{"l_parts"};; |
|
293 |
$form->{"l_description"} = "Y" if not defined $form->{"l_parts"}; |
|
294 |
|
|
218 | 295 |
map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns; |
219 | 296 |
|
220 | 297 |
my @options; |
... | ... | |
240 | 317 |
|
241 | 318 |
my $report = SL::ReportGenerator->new(\%myconfig, $form); |
242 | 319 |
|
320 |
if ( not $form->{l_sort_by_total} ) { |
|
243 | 321 |
$report->set_options('top_info_text' => join("\n", $locale->text('Main sorting') . ' : ' . $column_defs{$form->{mainsort}}->{text} , $locale->text('Secondary sorting') . ' : ' . $column_defs{$form->{'subsort'}}->{text}, @options), |
244 | 322 |
'output_format' => 'HTML', |
245 | 323 |
'title' => $form->{title}, |
246 | 324 |
'attachment_basename' => $locale->text('Sales Report') . strftime('_%Y%m%d', localtime time), |
247 | 325 |
); |
326 |
} else { |
|
327 |
$report->set_options('top_info_text' => join("\n", $locale->text('Grouping') . ' : ' . $column_defs{$form->{topsortgroup}}->{text} , $locale->text('Sort By') . ' : ' . $column_defs{$form->{'topsorttype'}}->{text}, @options), |
|
328 |
'output_format' => 'HTML', |
|
329 |
'title' => $form->{title}, |
|
330 |
'attachment_basename' => $locale->text('Sales Report') . strftime('_%Y%m%d', localtime time), |
|
331 |
); |
|
332 |
}; |
|
333 |
|
|
248 | 334 |
$report->set_options_from_form(); |
249 | 335 |
$locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv'; |
250 | 336 |
|
... | ... | |
253 | 339 |
|
254 | 340 |
$report->set_export_options('invoice_transactions', @hidden_variables, qw(mainsort sortdir)); |
255 | 341 |
|
256 |
$report->set_sort_indicator($form->{mainsort}, $form->{sortdir}); |
|
342 |
$report->set_sort_indicator($form->{mainsort}, $form->{sortdir}) unless $form->{"l_sort_by_total"}; |
|
343 |
|
|
257 | 344 |
|
258 | 345 |
CVar->add_custom_variables_to_report('module' => 'CT', |
259 | 346 |
'trans_id_field' => 'customerid', |
... | ... | |
284 | 371 |
# escape callback for href |
285 | 372 |
$callback = $form->escape($href); |
286 | 373 |
|
287 |
my @subtotal_columns = qw(qty weight sellprice sellprice_total lastcost lastcost_total marge_total marge_percent discount); |
|
374 |
my @subtotal_columns = qw(qty weight sellprice sellprice_total lastcost lastcost_total marge_total marge_percent discount sellpricelinetotal);
|
|
288 | 375 |
# Total sum: |
289 | 376 |
# sum of sellprice_total, lastcost_total and marge_total |
290 | 377 |
# average of marge_percent |
291 |
my @total_columns = qw(sellprice_total lastcost_total marge_total marge_percent ); |
|
378 |
# absolutes: qty, weight, sellprice_total, lastcost_total, marge_total |
|
379 |
# averages: sellprice, lastcost, marge_percent |
|
380 |
my @total_columns = qw(sellprice_total lastcost_total marge_total marge_percent sellpricelinetotal); |
|
292 | 381 |
|
293 | 382 |
my %totals = map { $_ => 0 } @total_columns; |
294 | 383 |
my %subtotals1 = map { $_ => 0 } @subtotal_columns; |
... | ... | |
396 | 485 |
map { $ar->{$_} = $form->format_amount(\%myconfig, $ar->{$_}, 2) } qw(marge_percent qty); |
397 | 486 |
map { $ar->{$_} = $form->format_amount(\%myconfig, $ar->{$_}, 3) } qw(weight); |
398 | 487 |
map { $ar->{$_} = $form->format_amount(\%myconfig, $ar->{$_}, $form->{"decimalplaces"} )} qw(lastcost sellprice sellprice_total lastcost_total marge_total); |
488 |
map { $ar->{$_} = $form->format_amount(\%myconfig, $ar->{$_}, $form->{"decimalplaces"} )} qw(sellpricelinetotal) if $form->{"l_sellpricelinetotal"}; |
|
399 | 489 |
|
400 | 490 |
# Einzelzeilen nur zeigen wenn l_parts gesetzt ist, nützlich, wenn man nur |
401 | 491 |
# Subtotals und Totals sehen möchte |
492 |
# these lines are only added to report in individual mode |
|
493 |
|
|
402 | 494 |
if ($form->{l_parts}) { |
403 | 495 |
my %row = ( |
404 | 496 |
map { ($_ => { data => $ar->{$_}, align => $column_alignment{$_} }) } @columns |
... | ... | |
414 | 506 |
|
415 | 507 |
# hier wird bei l_subtotal nicht differenziert zwischen mainsort und subsort |
416 | 508 |
# macht man l_subtotal_mainsort aus wird l_subtotal_subsort auch nicht ausgeführt |
509 |
# create a subtotal line if we are at the last element or either the subsort or mainsort element has changed |
|
417 | 510 |
if ( ($form->{l_subtotal_mainsort} eq 'Y') |
418 | 511 |
&& ($form->{l_subtotal_subsort} eq 'Y') |
419 | 512 |
&& (($idx == (scalar @{ $form->{AR} } - 1)) # last element always has a subtotal |
... | ... | |
421 | 514 |
|| ($ar->{ $form->{'mainsort'} } ne $form->{AR}->[$idx + 1]->{ $form->{'mainsort'} }) |
422 | 515 |
)) { # if value that is sorted by changes, print subtotal |
423 | 516 |
|
424 |
$report->add_data(create_subtotal_row_invoice(\%subtotals2, \@columns, \%column_alignment, \@subtotal_columns, $form->{l_parts} ? 'listsubtotal' : undef, $ar->{ $form->{'subsort'} })); |
|
425 |
$report->add_data({ %empty_row }) if $form->{l_parts} and $addemptylines; |
|
426 |
} |
|
517 |
|
|
518 |
# stick subtotal_row_invoice in temporary variable |
|
519 |
# if TOP mode store it in @data_to_be_sorted so we can sort the lines after all lines have been added |
|
520 |
# otherwise just add to $report to be shown in defualt |
|
521 |
my $tmpobject = create_subtotal_row_invoice(\%subtotals2, \@columns, \%column_alignment, \@subtotal_columns, $form->{l_parts} ? 'listsubtotal' : undef, $ar->{ $form->{'subsort'} }); |
|
522 |
if ( $form->{l_sort_by_total} and not $form->{l_parts} ) { |
|
523 |
# add subtotal data to temporary array that can later be sorted |
|
524 |
push(@data_to_be_sorted, $tmpobject); |
|
525 |
} else { |
|
526 |
$report->add_data( $tmpobject); |
|
527 |
}; |
|
528 |
$report->add_data({ %empty_row }) if $form->{l_parts} and $addemptylines; |
|
529 |
|
|
530 |
}; |
|
427 | 531 |
|
428 | 532 |
# if last mainsort is reached or mainsort has changed, add mainsort subtotal and empty row |
429 | 533 |
if ( ($form->{l_subtotal_mainsort} eq 'Y') |
... | ... | |
440 | 544 |
|
441 | 545 |
$idx++; |
442 | 546 |
} |
547 |
|
|
548 |
|
|
549 |
if ( $form->{l_sort_by_total} ) { |
|
550 |
# filter allowed parameters for sorttype |
|
551 |
my @allowed_fields = qw(sellprice_total marge_total qty weight); |
|
552 |
my ($sorttype) = grep { /^$form->{topsorttype}$/ } @allowed_fields; |
|
553 |
die "illegal parameter for topsorttype" unless $sorttype; |
|
554 |
|
|
555 |
# add marker to column being sorted |
|
556 |
$report->set_sort_indicator($sorttype); |
|
557 |
|
|
558 |
# add subtotal lines to report in numerical order of sorttype |
|
559 |
@data_to_be_sorted = reverse sort { $a->{$sorttype}{unformatted} <=> $b->{$sorttype}{unformatted} } @data_to_be_sorted; |
|
560 |
foreach( @data_to_be_sorted ) { |
|
561 |
# to limit output, e.g. top 100, one could add a counter here |
|
562 |
# that only adds a certain number of entries |
|
563 |
# the total at the end would still be over all entries though |
|
564 |
$report->add_data( $_ ); |
|
565 |
}; |
|
566 |
}; |
|
567 |
|
|
443 | 568 |
if ( $form->{l_total} eq "Y" ) { |
444 | 569 |
$report->add_separator(); |
445 | 570 |
$report->add_data(create_subtotal_row_invoice(\%totals, \@columns, \%column_alignment, \@total_columns, 'listtotal', 'l_total')) |
446 | 571 |
}; |
447 |
|
|
572 |
|
|
448 | 573 |
$report->generate_with_headers(); |
574 |
|
|
449 | 575 |
$main::lxdebug->leave_sub(); |
450 | 576 |
} |
451 | 577 |
|
... | ... | |
470 | 596 |
$row->{description}->{data} = $locale->text('Total') . ' ' . $name; |
471 | 597 |
}; |
472 | 598 |
|
599 |
map { $row->{$_}->{unformatted} = $totals->{$_} } qw(sellprice_total lastcost_total marge_total qty weight); |
|
473 | 600 |
map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, 2) } qw(marge_total marge_percent qty); |
474 | 601 |
map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, 3) } qw(weight); |
475 |
map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, $form->{decimalplaces}) } qw(lastcost sellprice sellprice_total lastcost_total); |
|
476 |
|
|
602 |
map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, $form->{decimalplaces}) } qw(lastcost sellprice sellprice_total lastcost_total sellpricelinetotal); |
|
477 | 603 |
|
478 | 604 |
map { $totals->{$_} = 0 } @{ $subtotal_columns }; |
479 | 605 |
|
Auch abrufbar als: Unified diff
Neue Verkaufsberichtvariante mit Umsatz-Sortierung
Es gibt einen neuen Menüeintrag "Verkaufsbericht Top", wo man nach den
gleichen Kategorien wie beim normalen Verkaufsbericht gruppieren kann,
aber wo man das Ergebnis nach Umsatz, Marge, Menge oder Gewicht
vorsortieren kann, was beim Standardverkaufsbericht nicht möglich war.
Dadurch kann man sich z.B. eine nach Umsatz sortierte Liste der Kunden
für einen Zeitraum anzeigen lassen. Es wird weiterhin nur auf Daten aus
"invoice" zurückgegriffen.
Es wird bei dieser Variante auf den gleichen Code zurückgegriffen,
allerdings wurde aus Gründen der Übersicht ein neuer Menüpunkt
eingeführt, in Zukunft könnte man dies vielleicht mit eigenen Reitern
besser machen.