From a521b29b7f07b940afb392ccbd22f6373dfa0b7e Mon Sep 17 00:00:00 2001 From: "G. Richardson" Date: Tue, 12 Jun 2012 15:08:34 +0200 Subject: [PATCH] =?utf8?q?Verkaufsberichtsortierung=20um=20Land,=20Warengr?= =?utf8?q?uppen,=20Kundentyp,=20Verk=C3=A4ufer=20und=20Monat=20erweitert?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Hauptsortierung und Untersortierung sind jetzt nicht mehr auf Ware und Kunde begrenzt, sondern man kann eine Kombinationen erstellen aus: * Kunde * Ware * Land * Warengruppe * Kundentyp * Verkäufer * Monat Es kann jetzt auch nach benutzerdefinierten Variablen gefiltert werden. Der Verkaufsbericht spaltet sich mit seinen Optionen langsam in zwei unterschiedliche Bereiche auf, den Artikelmodus, wo die einzelnen Zeilen aus invoice angezeigt werden, und den Rechnungsmodus, wo nur die Zeilen der Zwischensummen und Summen angezeigt werden, und die Detailinformationen aus invoice nur stören. Default ist Rechnungsmodus, den Artikelmodus kann man per Häkchen auswählen. Je nachdem auf welcher Ebene man sich befindet machen dann auch Informationen wie "Durchschnittsverkaufspreis" keinen Sinn mehr. Bei Zuordnungen wo die Sortierung keinen Wert hat (z.B. Sortierung nach Land, aber beim Kunden ist kein Land hinterlegt), erscheint als Überschrift "leer", und alle leeren Werte werden als eine Gruppe zusammengefasst. --- SL/VK.pm | 85 +++++++- bin/mozilla/vk.pl | 209 +++++++++++++----- locale/de/all | 16 ++ templates/webpages/vk/search_invoice.html | 246 ++++++++++++++++------ 4 files changed, 430 insertions(+), 126 deletions(-) diff --git a/SL/VK.pm b/SL/VK.pm index ae3ff4dbe..63eecfd85 100644 --- a/SL/VK.pm +++ b/SL/VK.pm @@ -51,37 +51,72 @@ sub invoice_transactions { my @values; my $query = - qq|SELECT cus.name,cus.customernumber,ar.invnumber,ar.id,ar.transdate,p.partnumber,i.parts_id,i.qty,i.price_factor,i.discount,i.description,i.lastcost,i.sellprice,i.fxsellprice,i.marge_total,i.marge_percent,i.unit | . + qq|SELECT ct.id as customerid, ct.name as customername,ct.customernumber,ct.country,ar.invnumber,ar.id,ar.transdate,p.partnumber,pg.partsgroup,i.parts_id,i.qty,i.price_factor,i.discount,i.description as description,i.lastcost,i.sellprice,i.marge_total,i.marge_percent,i.unit,b.description as business,e.name as employee,e2.name as salesman, to_char(ar.transdate,'Month') as month | . qq|FROM invoice i | . - qq|join ar on (i.trans_id = ar.id) | . - qq|join parts p on (i.parts_id = p.id) | . - qq|join customer cus on (cus.id = ar.customer_id) |; + qq|JOIN ar on (i.trans_id = ar.id) | . + qq|JOIN parts p on (i.parts_id = p.id) | . + qq|LEFT JOIN partsgroup pg on (p.partsgroup_id = pg.id) | . + qq|LEFT JOIN customer ct on (ct.id = ar.customer_id) | . + qq|LEFT JOIN business b on (ct.business_id = b.id) | . + qq|LEFT JOIN employee e ON (ar.employee_id = e.id) | . + qq|LEFT JOIN employee e2 ON (ar.salesman_id = e2.id) |; my $where = "1 = 1"; + # if employee can only see his own invoices, make sure this also holds for sales report + # limits by employees (Bearbeiter), not salesmen! + if (!$main::auth->assert('sales_all_edit', 1)) { + $where .= " AND ar.employee_id = (select id from employee where login= ?)"; + push (@values, $form->{login}); + } + # Stornierte Rechnungen und Stornorechnungen in invoice rausfiltern + # was ist mit Gutschriften? $where .= " AND ar.storno is not true "; # Bestandteile von Erzeugnissen herausfiltern $where .= " AND i.assemblyitem is not true "; - my $sortorder = "cus.name,i.parts_id,ar.transdate"; - if ($form->{sortby} eq 'artikelsort') { - $sortorder = "i.parts_id,cus.name,ar.transdate"; + my $sortorder; + # sorting by month is a special case: + # Sorting by month, using salesman as an example: + # Sorting with month as mainsort: ORDER BY month,salesman,ar.transdate,ar.invnumber + # Sorting with month as subsort: ORDER BY salesman,ar.transdate,month,ar.invnumber + if ($form->{mainsort} eq 'month') { + $sortorder .= "ar.transdate,month," + } else { + $sortorder .= $form->{mainsort} . ","; }; + if ($form->{subsort} eq 'month') { + $sortorder .= "ar.transdate,month," + } else { + $sortorder .= $form->{subsort} . ","; + }; + $sortorder .= 'ar.transdate,' unless $form->{subsort} eq 'month'; + $sortorder .= 'ar.invnumber'; + +# $sortorder =~ s/month/ar.transdate/; if ($form->{customer_id}) { $where .= " AND ar.customer_id = ?"; push(@values, $form->{customer_id}); }; if ($form->{customernumber}) { - $where .= qq| AND cus.customernumber = ? |; + $where .= qq| AND ct.customernumber = ? |; push(@values, $form->{customernumber}); } if ($form->{partnumber}) { $where .= qq| AND (p.partnumber ILIKE ?)|; push(@values, '%' . $form->{partnumber} . '%'); } + if ($form->{partsgroup_id}) { + $where .= qq| AND (pg.id = ?)|; + push(@values, $form->{partsgroup_id}); + } + if ($form->{country}) { + $where .= qq| AND (ct.country ILIKE ?)|; + push(@values, '%' . $form->{country} . '%'); + } # nimmt man description am Besten aus invoice oder parts? if ($form->{description}) { $where .= qq| AND (i.description ILIKE ?)|; @@ -100,6 +135,15 @@ sub invoice_transactions { $where .= " AND ar.department_id = ?"; push(@values, $department_id); } + if ($form->{employee_id}) { + $where .= " AND ar.employee_id = ?"; + push @values, conv_i($form->{employee_id}); + } + + if ($form->{salesman_id}) { + $where .= " AND ar.salesman_id = ?"; + push @values, conv_i($form->{salesman_id}); + } if ($form->{project_id}) { $where .= qq|AND ((ar.globalproject_id = ?) OR EXISTS | . @@ -107,8 +151,31 @@ sub invoice_transactions { qq| WHERE i.project_id = ? AND i.trans_id = ar.id))|; push(@values, $form->{"project_id"}, $form->{"project_id"}); } + if ($form->{business_id}) { + $where .= qq| AND ct.business_id = ? |; + push(@values, $form->{"business_id"}); + } + + my ($cvar_where_ct, @cvar_values_ct) = CVar->build_filter_query('module' => 'CT', + 'trans_id_field' => 'ct.id', + 'filter' => $form); + + if ($cvar_where_ct) { + $where .= qq| AND ($cvar_where_ct)|; + push @values, @cvar_values_ct; + } + - $query .= " WHERE $where ORDER BY $sortorder"; + my ($cvar_where_ic, @cvar_values_ic) = CVar->build_filter_query('module' => 'IC', + 'trans_id_field' => 'p.id', + 'filter' => $form); + + if ($cvar_where_ic) { + $where .= qq| AND ($cvar_where_ic)|; + push @values, @cvar_values_ic; + } + + $query .= " WHERE $where ORDER BY $sortorder "; # LIMIT 5000"; my @result = selectall_hashref_query($form, $dbh, $query, @values); diff --git a/bin/mozilla/vk.pl b/bin/mozilla/vk.pl index b5c0931af..ef99388e2 100644 --- a/bin/mozilla/vk.pl +++ b/bin/mozilla/vk.pl @@ -46,7 +46,6 @@ require "bin/mozilla/reportgenerator.pl"; use strict; - sub search_invoice { $main::lxdebug->enter_sub(); $main::auth->assert('general_ledger | invoice_edit'); @@ -63,11 +62,27 @@ sub search_invoice { $form->{title} = $locale->text('Sales Report'); $form->{jsscript} = 1; - $form->get_lists("projects" => { "key" => "ALL_PROJECTS", "all" => 1 }, - "departments" => "ALL_DEPARTMENTS", - "customers" => "ALL_VC"); - + $form->get_lists("projects" => { "key" => "ALL_PROJECTS", "all" => 1 }, + "departments" => "ALL_DEPARTMENTS", + "business_types" => "ALL_BUSINESS_TYPES", + "salesmen" => "ALL_SALESMEN", + 'employees' => 'ALL_EMPLOYEES', + 'partsgroup' => 'ALL_PARTSGROUPS', + "customers" => "ALL_VC"); + $form->{CUSTOM_VARIABLES_IC} = CVar->get_configs('module' => 'IC'); + ($form->{CUSTOM_VARIABLES_FILTER_CODE_IC}, + $form->{CUSTOM_VARIABLES_INCLUSION_CODE_IC}) = CVar->render_search_options('variables' => $form->{CUSTOM_VARIABLES_IC}, + 'include_prefix' => 'l_', + 'include_value' => 'Y'); + + $form->{CUSTOM_VARIABLES_CT} = CVar->get_configs('module' => 'CT'); + ($form->{CUSTOM_VARIABLES_FILTER_CODE_CT}, + $form->{CUSTOM_VARIABLES_INCLUSION_CODE_CT}) = CVar->render_search_options('variables' => $form->{CUSTOM_VARIABLES_CT}, + 'include_prefix' => 'l_', + 'include_value' => 'Y'); $form->{vc_keys} = sub { "$_[0]->{name}--$_[0]->{id}" }; + $form->{employee_labels} = sub { $_[0]->{"name"} || $_[0]->{"login"} }; + $form->{salesman_labels} = $form->{employee_labels}; $form->header; print $form->parse_html_template('vk/search_invoice', { %myconfig }); @@ -86,6 +101,9 @@ sub invoice_transactions { my ($callback, $href, @columns); + # can't currently be configured from report, empty line between main sortings + my $addemptylines = '1'; + if ( $form->{customer} =~ /--/ ) { # Felddaten kommen aus Dropdownbox ($form->{customername}, $form->{customer_id}) = split(/--/, $form->{customer}); @@ -108,28 +126,46 @@ sub invoice_transactions { # decimalplaces überprüfen oder auf Default 2 setzen $form->{decimalplaces} = 2 unless $form->{decimalplaces} > 0 && $form->{decimalplaces} < 6; + my $cvar_configs_ct = CVar->get_configs('module' => 'CT'); + my $cvar_configs_ic = CVar->get_configs('module' => 'IC'); + # report_generator_set_default_sort('transdate', 1); VK->invoice_transactions(\%myconfig, \%$form); - # anhand von radio button die Sortierreihenfolge festlegen - if ($form->{sortby} eq 'artikelsort') { - $form->{'mainsort'} = 'parts_id'; - $form->{'subsort'} = 'name'; - } else { - $form->{'mainsort'} = 'name'; - $form->{'subsort'} = 'parts_id'; + + if ( $form->{mainsort} eq 'month' or $form->{subsort} eq 'month' ) { + + # Data already comes out of SELECT statement in correct month order, but + # remove whitespaces (month names are padded) and translate them as early + # as possible + + foreach (@{ $form->{AR} }) { + $_->{month} =~ s/\s//g; + $_->{month} = $locale->text($_->{month}); + }; }; $form->{title} = $locale->text('Sales Report'); @columns = - qw(description invnumber transdate customernumber partnumber transdate qty unit sellprice sellprice_total discount lastcost lastcost_total marge_total marge_percent); + qw(description invnumber transdate customernumber customername partnumber partsgroup country business transdate qty unit sellprice sellprice_total discount lastcost lastcost_total marge_total marge_percent employee salesman); + + my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs_ic }, @{ $cvar_configs_ct }; + my @searchable_custom_variables = grep { $_->{searchable} } @{ $cvar_configs_ic }, @{ $cvar_configs_ct }; + my %column_defs_cvars = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @includeable_custom_variables; + + push @columns, map { "cvar_$_->{name}" } @includeable_custom_variables; + # hidden variables für pdf/csv export übergeben # einmal mit l_ um zu bestimmen welche Spalten ausgegeben werden sollen # einmal optionen für die Überschrift (z.B. transdatefrom, partnumber, ...) - my @hidden_variables = (qw(l_headers l_subtotal l_total l_customernumber transdatefrom transdateto decimalplaces customer customername customer_id department partnumber description project_id customernumber), "$form->{db}number", map { "l_$_" } @columns); + 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), + "$form->{db}number", + map({ "cvar_$_->{name}" } @searchable_custom_variables), + map { "l_$_" } @columns + ); my @hidden_nondefault = grep({ $form->{$_} } @hidden_variables); # Variablen werden dann als Hidden Variable mitgegeben, z.B. # @@ -140,6 +176,11 @@ sub invoice_transactions { my %column_defs = ( 'description' => { 'text' => $locale->text('Description'), }, 'partnumber' => { 'text' => $locale->text('Part Number'), }, + 'partsgroup' => { 'text' => $locale->text('Group'), }, + 'country' => { 'text' => $locale->text('Country'), }, + 'business' => { 'text' => $locale->text('Customer type'), }, + 'employee' => { 'text' => $locale->text('Employee'), }, + 'salesman' => { 'text' => $locale->text('Salesperson'), }, 'invnumber' => { 'text' => $locale->text('Invoice Number'), }, 'transdate' => { 'text' => $locale->text('Invoice Date'), }, 'qty' => { 'text' => $locale->text('Quantity'), }, @@ -152,15 +193,28 @@ sub invoice_transactions { 'marge_total' => { 'text' => $locale->text('Sales margin'), }, 'marge_percent' => { 'text' => $locale->text('Sales margin %'), }, 'customernumber' => { 'text' => $locale->text('Customer Number'), }, + 'customername' => { 'text' => $locale->text('Customer Name'), }, +# add 3 more column_defs so we have a translation for top_info_text + 'customer' => { 'text' => $locale->text('Customer'), }, + 'part' => { 'text' => $locale->text('Part'), }, + 'month' => { 'text' => $locale->text('Month'), }, + %column_defs_cvars, ); + map { $column_defs{$_}->{visible} = $form->{"l_$_"} eq 'Y' } @columns; + my %column_alignment = map { $_ => 'right' } qw(lastcost sellprice sellprice_total lastcost_total unit discount marge_total marge_percent qty); - $form->{"l_type"} = "Y"; + + # so now the check-box "Description" is only used as switch for part description in invoice-mode + # always fill the column "Description" if we are in Zwischensummenmode + if (not defined $form->{"l_parts"}) { + $form->{"l_description"} = "Y"; + }; map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns; - my @options; + if ($form->{description}) { push @options, $locale->text('Description') . " : $form->{description}"; } @@ -170,6 +224,7 @@ sub invoice_transactions { if ($form->{customernumber}) { push @options, $locale->text('Customer Number') . " : $form->{customernumber}"; } +# TODO: es wird nur id übergeben if ($form->{department}) { my ($department) = split /--/, $form->{department}; push @options, $locale->text('Department') . " : $department"; @@ -183,6 +238,25 @@ sub invoice_transactions { if ($form->{partnumber}) { push @options, $locale->text('Part Number') . " : $form->{partnumber}"; } + if ($form->{partsgroup_id}) { + my $partsgroup = SL::DB::PartsGroup->new(id => $form->{partsgroup_id})->load; + push @options, $locale->text('Group') . " : $partsgroup->{partsgroup}"; + } + if ($form->{country}) { + push @options, $locale->text('Country') . " : $form->{country}"; + } + if ($form->{employee_id}) { + my $employee = SL::DB::Employee->new(id => $form->{employee_id})->load; + push @options, $locale->text('Employee') . ' : ' . $employee->name; + } + if ($form->{salesman_id}) { + my $salesman = SL::DB::Employee->new(id => $form->{salesman_id})->load; + push @options, $locale->text('Salesman') . ' : ' . $salesman->name; + } + if ($form->{business_id}) { + my $business = SL::DB::Business->new(id => $form->{business_id})->load; + push @options, $locale->text('Customer type') . ' : ' . $business->description; + } if ($form->{ordnumber}) { push @options, $locale->text('Order Number') . " : $form->{ordnumber}"; } @@ -201,7 +275,7 @@ sub invoice_transactions { my $report = SL::ReportGenerator->new(\%myconfig, $form); - $report->set_options('top_info_text' => join("\n", @options), + $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), 'output_format' => 'HTML', 'title' => $form->{title}, 'attachment_basename' => $locale->text('Sales Report') . strftime('_%Y%m%d', localtime time), @@ -216,6 +290,20 @@ sub invoice_transactions { $report->set_sort_indicator($form->{mainsort}, $form->{sortdir}); + CVar->add_custom_variables_to_report('module' => 'CT', + 'trans_id_field' => 'customerid', + 'configs' => $cvar_configs_ct, + 'column_defs' => \%column_defs, + 'data' => $form->{AR} + ); + + CVar->add_custom_variables_to_report('module' => 'IC', + 'trans_id_field' => 'parts_id', + 'configs' => $cvar_configs_ic, + 'column_defs' => \%column_defs, + 'data' => $form->{AR} + ); + # add sort and escape callback, this one we use for the add sub $form->{callback} = $href .= "&sort=$form->{mainsort}"; @@ -241,23 +329,25 @@ sub invoice_transactions { # discount was already accounted for in db sellprice $ar->{sellprice} = $ar->{sellprice} / $ar->{price_factor}; $ar->{lastcost} = $ar->{lastcost} / $ar->{price_factor}; - $ar->{sellprice_total} = $ar->{qty} * ( $ar->{fxsellprice} * ( 1 - $ar->{discount} ) ) ; + $ar->{sellprice_total} = $ar->{qty} * $ar->{sellprice}; $ar->{lastcost_total} = $ar->{qty} * $ar->{lastcost}; # marge_percent wird neu berechnet, da Wert in invoice leer ist (Bug) - $ar->{marge_percent} = $ar->{sellprice_total} ? (($ar->{sellprice_total}-$ar->{lastcost_total}) / $ar->{sellprice_total}) : 0; + $ar->{marge_percent} = $ar->{sellprice_total} ? (($ar->{sellprice_total}-$ar->{lastcost_total}) / $ar->{sellprice_total} * 100) : 0; # marge_total neu berechnen $ar->{marge_total} = $ar->{sellprice_total} ? $ar->{sellprice_total}-$ar->{lastcost_total} : 0; $ar->{discount} *= 100; # für Ausgabe formatieren, 10% stored as 0.1 in db # Anfangshauptüberschrift - if ( $form->{l_headers} eq "Y" && ( $idx == 0 or $ar->{ $form->{'mainsort'} } ne $form->{AR}->[$idx - 1]->{ $form->{'mainsort'} } )) { - my $name; + if ( $form->{l_headers_mainsort} eq "Y" && ( $idx == 0 or $ar->{ $form->{'mainsort'} } ne $form->{AR}->[$idx - 1]->{ $form->{'mainsort'} } )) { my $headerrow; - if ( $form->{mainsort} eq 'parts_id' ) { - $headerrow->{description}->{data} = "$ar->{description}"; + + # use $emptyname for mainsort header if mainsort is empty + if ( $ar->{$form->{'mainsort'}} ) { + $headerrow->{description}->{data} = $ar->{$form->{'mainsort'}}; } else { - $headerrow->{description}->{data} = "$ar->{name}"; + $headerrow->{description}->{data} = $locale->text('empty'); }; + $headerrow->{description}->{class} = "listmainsortheader"; my $headerrow_set = [ $headerrow ]; $report->add_data($headerrow_set); @@ -275,17 +365,17 @@ sub invoice_transactions { or $ar->{ $form->{'mainsort'} } ne $form->{AR}->[$idx - 1]->{ $form->{'mainsort'} } ) { my $headerrow; - my $name; - if ( $form->{subsort} eq 'parts_id' ) { - $name = 'description'; - $headerrow->{description}->{data} = "$ar->{$name}"; + + # if subsort name is defined, use that name in header, otherwise use $emptyname + if ( $ar->{$form->{'subsort'}} ) { + $headerrow->{description}->{data} = $ar->{$form->{'subsort'}}; } else { - $name = 'name'; - $headerrow->{description}->{data} = "$ar->{$name}"; + $headerrow->{description}->{data} = $locale->text('empty'); }; $headerrow->{description}->{class} = "listsubsortheader"; my $headerrow_set = [ $headerrow ]; - $report->add_data($headerrow_set) if $form->{l_headers} eq "Y"; + # special case: subsort headers only makes (aesthetical) sense if we show individual parts + $report->add_data($headerrow_set) if $form->{l_headers_subsort} eq "Y" and $form->{l_parts}; }; map { $subtotals1{$_} += $ar->{$_}; @@ -318,12 +408,12 @@ sub invoice_transactions { }; # Ertrag prozentual in den Summen: (summe VK - summe Ertrag) / summe VK - $subtotals1{marge_percent} = $subtotals1{sellprice_total} ? (($subtotals1{sellprice_total} - $subtotals1{lastcost_total}) / $subtotals1{sellprice_total}) : 0; - $subtotals2{marge_percent} = $subtotals2{sellprice_total} ? (($subtotals2{sellprice_total} - $subtotals2{lastcost_total}) / $subtotals2{sellprice_total}) : 0; + $subtotals1{marge_percent} = $subtotals1{sellprice_total} ? (($subtotals1{sellprice_total} - $subtotals1{lastcost_total}) / $subtotals1{sellprice_total}) * 100 : 0; + $subtotals2{marge_percent} = $subtotals2{sellprice_total} ? (($subtotals2{sellprice_total} - $subtotals2{lastcost_total}) / $subtotals2{sellprice_total}) *100 : 0; # Ertrag prozentual: (Summe VK betrag - Summe EK betrag) / Summe VK betrag # wird laufend bei jeder Position neu berechnet - $totals{marge_percent} = $totals{sellprice_total} ? ( ($totals{sellprice_total} - $totals{lastcost_total}) / $totals{sellprice_total} ) : 0; + $totals{marge_percent} = $totals{sellprice_total} ? ( ($totals{sellprice_total} - $totals{lastcost_total}) / $totals{sellprice_total} ) * 100 : 0; map { $ar->{$_} = $form->format_amount(\%myconfig, $ar->{$_}, 2) } qw(marge_total marge_percent); map { $ar->{$_} = $form->format_amount(\%myconfig, $ar->{$_}, $form->{"decimalplaces"} )} qw(lastcost sellprice sellprice_total lastcost_total); @@ -342,40 +432,34 @@ sub invoice_transactions { $row->{invnumber}->{link} = build_std_url("script=is.pl", 'action=edit') . "&id=" . E($ar->{id}) . "&callback=${callback}"; - my $row_set = [ $row ]; + # Einzelzeilen nur zeigen wenn l_parts gesetzt ist, nützlich, wenn man nur + # Subtotals und Totals sehen möchte + my $row_set = $form->{l_parts} ? [ $row ] : [ ]; - if (($form->{l_subtotal} eq 'Y') + # hier wird bei l_subtotal nicht differenziert zwischen mainsort und subsort + # macht man l_subtotal_mainsort aus wird l_subtotal_subsort auch nicht ausgeführt + if (($form->{l_subtotal_mainsort} eq 'Y') && (($idx == (scalar @{ $form->{AR} } - 1)) # last element always has a subtotal || ($ar->{ $form->{'subsort'} } ne $form->{AR}->[$idx + 1]->{ $form->{'subsort'} }) || ($ar->{ $form->{'mainsort'} } ne $form->{AR}->[$idx + 1]->{ $form->{'mainsort'} }) )) { # if value that is sorted by changes, print subtotal - my $name; - if ( $form->{subsort} eq 'parts_id' ) { - $name = 'description'; - } else { - $name = 'name'; - }; - if ($form->{l_subtotal} eq 'Y') { - push @{ $row_set }, create_subtotal_row_invoice(\%subtotals2, \@columns, \%column_alignment, \@subtotal_columns, 'listsubsortsubtotal', $ar->{$name}) ; - push @{ $row_set }, insert_empty_row(); + if ($form->{l_subtotal_subsort} eq 'Y') { + push @{ $row_set }, create_subtotal_row_invoice(\%subtotals2, \@columns, \%column_alignment, \@subtotal_columns, 'listsubsortsubtotal', $ar->{ $form->{'subsort'} }) ; + push @{ $row_set }, insert_empty_row() if $form->{l_parts} and $addemptylines; }; } - # if mainsort has changed, add mainsort subtotal and empty row - if (($form->{l_subtotal} eq 'Y') + # if last mainsort is reached or mainsort has changed, add mainsort subtotal and empty row + if (($form->{l_subtotal_mainsort} eq 'Y') && (($idx == (scalar @{ $form->{AR} } - 1)) # last element always has a subtotal || ($ar->{ $form->{'mainsort'} } ne $form->{AR}->[$idx + 1]->{ $form->{'mainsort'} }) )) { # if value that is sorted by changes, print subtotal - my $name; - if ( $form->{mainsort} eq 'parts_id' ) { - $name = 'description'; - } else { - $name = 'name'; - }; - if ($form->{l_subtotal} eq 'Y' ) { - push @{ $row_set }, create_subtotal_row_invoice(\%subtotals1, \@columns, \%column_alignment, \@subtotal_columns, 'listmainsortsubtotal', $ar->{$name}); - push @{ $row_set }, insert_empty_row(); + if ($form->{l_subtotal_mainsort} eq 'Y' and $form->{mainsort} ne $form->{subsort} ) { + # subtotal is overriden if mainsort and subsort are equal, don't print + # subtotal line even if it is selected + push @{ $row_set }, create_subtotal_row_invoice(\%subtotals1, \@columns, \%column_alignment, \@subtotal_columns, 'listmainsortsubtotal', $ar->{$form->{mainsort}}); + push @{ $row_set }, insert_empty_row() if $addemptylines; # insert empty row after mainsort }; } @@ -385,7 +469,7 @@ sub invoice_transactions { } if ( $form->{l_total} eq "Y" ) { $report->add_separator(); - $report->add_data(create_subtotal_row_invoice(\%totals, \@columns, \%column_alignment, \@total_columns, 'listtotal')) + $report->add_data(create_subtotal_row_invoice(\%totals, \@columns, \%column_alignment, \@total_columns, 'listtotal', 'l_total')) }; $report->generate_with_headers(); @@ -409,10 +493,19 @@ sub create_subtotal_row_invoice { my $form = $main::form; my %myconfig = %main::myconfig; + my $locale = $main::locale; my $row = { map { $_ => { 'data' => '', 'class' => $class, 'align' => $column_alignment->{$_}, } } @{ $columns } }; - $row->{description}->{data} = "Summe " . $name; + # set name as "empty" if no value is given, except if we are dealing with the + # absolute total, then just write "Total sum" + # here we assume that no name will be called 'l_total' + $name = $locale->text('empty') unless $name; + if ( $name eq 'l_total' ) { + $row->{description}->{data} = $locale->text('Total sum'); + } else { + $row->{description}->{data} = $locale->text('Total') . ' ' . $name; + }; map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, 2) } qw(marge_total marge_percent); map { $row->{$_}->{data} = $form->format_amount(\%myconfig, $totals->{$_}, 0) } qw(qty); diff --git a/locale/de/all b/locale/de/all index 9e1d62aef..6a5a58ef8 100644 --- a/locale/de/all +++ b/locale/de/all @@ -488,6 +488,7 @@ $self->{texts} = { 'Credit Tax' => 'Umsatzsteuer', 'Credit Tax Account' => 'Umsatzsteuerkonto', 'Credit note (one letter abbreviation)' => 'G', + 'Cumulated or averaged values' => 'Kumulierte oder gemittelte Werte', 'Curr' => 'Währung', 'Currencies' => 'Währungen', 'Currency' => 'Währung', @@ -511,6 +512,7 @@ $self->{texts} = { 'Customer not on file!' => 'Kunde ist nicht in der Datenbank!', 'Customer saved!' => 'Kunde gespeichert!', 'Customer type' => 'Kundentyp', + 'Customer variables' => 'Kundenvariablen', 'Customer/Vendor' => 'Kunde/Lieferant', 'Customer/Vendor (database ID)' => 'Kunde/Lieferant (Datenbank-ID)', 'Customer/Vendor Name' => 'Kunde/Lieferant', @@ -839,6 +841,8 @@ $self->{texts} = { 'Files created by kivitendo\'s "Backup Dataset" function are such files.' => 'Dateien, die von kivitendo\'s Funktion "Datenbank sichern" erstellt wurden, erfüllen diese Kriterien.', 'Filter' => 'Filter', 'Filter date by' => 'Datum filtern nach', + 'Filter for customer variables' => 'Filter für benutzerdefinierte Kundenvariablen', + 'Filter for item variables' => 'Filter für benutzerdefinierte Artikelvariablen', 'Finish' => 'Abschließen', 'Fix transaction' => 'Buchung korrigieren', 'Fix transactions' => 'Buchungen korrigieren', @@ -1012,7 +1016,10 @@ $self->{texts} = { 'It may optionally be compressed with "gzip".' => 'Sie darf optional mit "gzip" komprimiert sein.', 'It will simply set the taxkey to 0 (meaning "no taxes") which is the correct value for such inventory transactions.' => 'Es wird einfach die Steuerschlüssel auf 0 setzen, was "keine Steuer" bedeutet und für solche Warenbestandsbuchungen der richtige Wert ist.', 'Item deleted!' => 'Artikel gelöscht!', + 'Item mode' => 'Artikelmodus', 'Item not on file!' => 'Dieser Artikel ist nicht in der Datenbank!', + 'Item values' => 'Artikelwerte', + 'Item variables' => 'Artikelvariablen', 'Jahresverkehrszahlen neu' => 'Jahresverkehrszahlen neu', 'Jan' => 'Jan', 'January' => 'Januar', @@ -1154,6 +1161,7 @@ $self->{texts} = { 'Module home page' => 'Modul-Webseite', 'Module name' => 'Modulname', 'Monat' => 'Monat', + 'Month' => 'Monat', 'Monthly' => 'monatlich', 'More than one #1 found matching, please be more specific.' => 'Mehr als ein #1 wurde gefunden, bitte geben Sie den Namen genauer an.', 'More than one control file with the tag \'%s\' exist.' => 'Es gibt mehr als eine Kontrolldatei mit dem Tag \'%s\'.', @@ -1261,6 +1269,7 @@ $self->{texts} = { 'On Order' => 'Ist bestellt', 'One or more Perl modules missing' => 'Ein oder mehr Perl-Module fehlen', 'Only due follow-ups' => 'Nur fällige Wiedervorlagen', + 'Only shown in item mode' => 'werden nur im Artikelmodus angezeigt', 'Oops. No valid action found to dispatch. Please report this case to the Lx-Office team.' => 'Ups. Es wurde keine gültige Funktion zum Aufrufen gefunden. Bitte berichten Sie diesen Fall den Lx-Office-Entwicklern.', 'Open' => 'Offen', 'Open Amount' => 'Offener Betrag', @@ -1587,6 +1596,7 @@ $self->{texts} = { 'Search contacts' => 'Ansprechpartnersuche', 'Search term' => 'Suchbegriff', 'Searchable' => 'Durchsuchbar', + 'Secondary sorting' => 'Untersortierung', 'Select' => 'auswählen', 'Select a Customer' => 'Endkunde auswählen', 'Select a customer' => 'Einen Kunden auswählen', @@ -1649,12 +1659,14 @@ $self->{texts} = { 'Show details' => 'Detailsanzeige', 'Show follow ups...' => 'Zeige Wiedervorlagen...', 'Show help text' => 'Hilfetext anzeigen', + 'Show items from invoices individually' => 'Artikel aus Rechnungen anzeigen', 'Show old dunnings' => 'Alte Mahnungen anzeigen', 'Show overdue sales quotations and requests for quotations...' => 'Überfällige Angebote und Preisanfragen anzeigen...', 'Show your TODO list after loggin in' => 'Aufgabenliste nach dem Anmelden anzeigen', 'Signature' => 'Unterschrift', 'Since bin is not enforced in the parts data, please specify a bin where goods without a specified bin will be put.' => 'Da Lagerplätze kein Pflichtfeld sind, geben Sie bitte einen Lagerplatz an, in dem Waren ohne spezifizierten Lagerplatz eingelagert werden sollen.', 'Single quotes' => 'Einfache Anführungszeichen', + 'Single values in item mode, cumulated values in invoice mode' => 'Einzelwerte im Artikelmodus, kumulierte Werte im Rechnungsmodus', 'Skip' => 'Überspringen', 'Skonto' => 'Skonto', 'Skonto Terms' => 'Zahlungsziel Skonto', @@ -1962,6 +1974,7 @@ $self->{texts} = { 'Total' => 'Summe', 'Total Fees' => 'Kumulierte Gebühren', 'Total stock value' => 'Gesamter Bestandswert', + 'Total sum' => 'Gesamtsumme', 'Totals' => 'Summen', 'Trade Discount' => 'Rabatt', 'Trans Id' => 'Trans-ID', @@ -2173,6 +2186,7 @@ $self->{texts} = { 'ar_aging_list' => 'liste_offene_forderungen', 'as at' => 'zum Stand', 'assembly_list' => 'erzeugnisliste', + 'averaged values, in invoice mode only useful when filtered by a part' => 'gemittelte Werte, im Rechnungsmodus nur sinnvoll wenn nach Artikel gefiltert wird', 'back' => 'zurück', 'balance' => 'Betriebsvermögensvergleich/Bilanzierung', 'bank_collection_payment_list_#1' => 'bankeinzugszahlungsliste_#1', @@ -2216,6 +2230,7 @@ $self->{texts} = { 'eMail?' => 'eMail?', 'ea' => 'St.', 'emailed to' => 'gemailt an', + 'empty' => 'leer', 'executed' => 'ausgeführt', 'female' => 'weiblich', 'follow_up_list' => 'wiedervorlageliste', @@ -2229,6 +2244,7 @@ $self->{texts} = { 'inactive' => 'inaktiv', 'income' => 'Einnahmen-Überschuß-Rechnung', 'invoice' => 'Rechnung', + 'invoice mode or item mode' => 'Rechnungsmodus oder Artikelmodus', 'invoice_list' => 'debitorenbuchungsliste', 'kivitendo' => 'kivitendo', 'kivitendo Homepage' => 'Infos zu kivitendo', diff --git a/templates/webpages/vk/search_invoice.html b/templates/webpages/vk/search_invoice.html index 098cd40fb..77f1d38b6 100644 --- a/templates/webpages/vk/search_invoice.html +++ b/templates/webpages/vk/search_invoice.html @@ -3,24 +3,73 @@
- +
[% title %]
- +
- - +
[% 'Main sorting' | $T8 %] - - - - - + + + + + + + - - -
[% 'Configuration' | $T8 %] + + + + + + + + + + + + + + + + + + + + + + + +
[% 'Main sorting' | $T8 %] + + [% 'Heading' | $T8 %] [% 'Subtotal' | $T8 %]
[% 'Secondary sorting' | $T8 %] + + [% 'Heading' | $T8 %] [% 'Subtotal' | $T8 %]
[% 'Item mode' | $T8 %] ([%'Show items from invoices individually' | $T8 %])
+ [% 'Total sum' | $T8 %] + [% 'Decimalplaces' | $T8 %]:
+
[% 'Filter' | $T8 %] + + + + - - - - + - - + - + + + + + + + + + + + + + + + + + + + + + + + + + [% CUSTOM_VARIABLES_FILTER_CODE_CT %] + + + + [% CUSTOM_VARIABLES_FILTER_CODE_IC %]

[% 'Filter' | $T8 %]:
[% 'Customer' | $T8 %] @@ -40,11 +89,11 @@
[% 'Customer Number' | $T8 %]
[% 'Department' | $T8 %] + [%- INCLUDE 'generic/multibox.html' name = 'department', style = 'width: 250px', @@ -73,14 +122,78 @@
[% 'Part Number' | $T8 %]
[% 'Part Description' | $T8 %]
[% 'From' | $T8 %][% 'Group' | $T8 %] + [%- INCLUDE 'generic/multibox.html' + name = 'partsgroup_id', + style = 'width: 250px', + DATA = ALL_PARTSGROUPS, + id_key = 'id', + label_key = 'partsgroup', + show_empty = 1, + allow_textbox = 0, + -%] +
[% 'Country' | $T8 %]
[% 'Employee' | $T8 %] + [%- INCLUDE 'generic/multibox.html' + name = 'employee_id', + style = 'width: 250px', + DATA = ALL_EMPLOYEES, + id_key = 'id', + label_sub = 'employee_labels', + limit = vclimit, + show_empty = 1, + allow_textbox = 0, + default = ' ', + -%] +
[% 'Salesman' | $T8 %] + [%- INCLUDE 'generic/multibox.html' + name = 'salesman_id', + style = 'width: 250px', + DATA = ALL_SALESMEN, + id_key = 'id', + label_sub = 'salesman_labels', + limit = vclimit, + show_empty = 1, + allow_textbox = 0, + -%] +
[% 'Customer type' | $T8 %] + [%- INCLUDE 'generic/multibox.html' + name = 'business_id', + style = "width: 250px", + DATA = ALL_BUSINESS_TYPES, + id_key = 'id', + label_key = 'description', + limit = vclimit, + show_empty = 1, + allow_textbox = 0, + -%] +
[% 'Invoice Date' | $T8 %] [% 'From' | $T8 %] @@ -91,67 +204,83 @@

[% 'Filter for customer variables' | $T8 %]:

[% 'Filter for item variables' | $T8 %]:
+
- - + @@ -159,7 +288,6 @@ -
[% 'Include in Report' | $T8 %] - +
+ - - - - - - - - + + + + - - - - - - + + + + - - - - - - - - + + + + + + - - - - + + + + - - - - - - + + + + - - + + + + - - + + + + + [% CUSTOM_VARIABLES_INCLUSION_CODE_CT %] + + + + + + [% CUSTOM_VARIABLES_INCLUSION_CODE_IC %]
+ [% 'Cumulated or averaged values' | $T8 %] ([% 'invoice mode or item mode' | $T8 %]): +
[% 'Description' | $T8 %][% 'Part Number' | $T8 %][% 'Invnumber' | $T8 %][% 'Invdate' | $T8 %][% 'Sales price total' | $T8 %][% 'Purchase price total' | $T8 %][% 'Margetotal' | $T8 %] ([% 'Single values in item mode, cumulated values in invoice mode' | $T8 %]) +
[% 'Quantity' | $T8 %][% 'Discount' | $T8 %][% 'Unit' | $T8 %][% 'Sales price' | $T8 %][% 'Purchase price' | $T8 %][% 'Margepercent' | $T8 %]([% 'averaged values, in invoice mode only useful when filtered by a part' | $T8 %])
[% 'Sales price' | $T8 %][% 'Sales price total' | $T8 %][% 'Purchase price' | $T8 %][% 'Purchase price total' | $T8 %][% 'Quantity' | $T8 %][% 'Discount' | $T8 %]([% 'averaged values, in invoice mode only useful when filtered by a part' | $T8 %])
 
+ [% 'Item values' | $T8 %] ([% 'Only shown in item mode' | $T8 %]) +
[% 'Margetotal' | $T8 %][% 'Margepercent' | $T8 %][% 'Description' | $T8 %][% 'Part Number' | $T8 %][% 'Invnumber' | $T8 %][% 'Invdate' | $T8 %]
[% 'Subtotal' | $T8 %][% 'Total' | $T8 %][% 'Headings' | $T8 %][% 'Unit' | $T8 %][% 'Group' | $T8 %][% 'Salesperson' | $T8 %][% 'Employee' | $T8 %]
[% 'Customer Number' | $T8 %][% 'Customer Number' | $T8 %][% 'Customer Name' | $T8 %][% 'Country' | $T8 %][% 'Customer type' | $T8 %]
[% 'Decimalplaces' | $T8 %]
 
+ [% 'Customer variables' | $T8 %] ([% 'Only shown in item mode' | $T8 %]) +
 
+ [% 'Item variables' | $T8 %] ([% 'Only shown in item mode' | $T8 %]) +

-- 2.20.1