X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=SL%2FIS.pm;h=43303e894b2c0ceb21099622a9c23aa4335dbb03;hb=b8125c17c8add08c9b84fb054f797547a853dd3a;hp=28323326f56ceb8027df6623cb4284a5e61ec9f9;hpb=fc44ab06e84d2546c7eb9c7c5f0586db17f01242;p=kivitendo-erp.git diff --git a/SL/IS.pm b/SL/IS.pm index 28323326f..43303e894 100644 --- a/SL/IS.pm +++ b/SL/IS.pm @@ -44,11 +44,15 @@ use SL::DATEV qw(:CONSTANTS); use SL::DBUtils; use SL::DO; use SL::GenericTranslations; +use SL::HTML::Restrict; use SL::MoreCommon; use SL::IC; use SL::IO; use SL::TransNumber; use SL::DB::Default; +use SL::DB::Tax; +use SL::DB::TaxZone; +use SL::TransNumber; use Data::Dumper; use strict; @@ -67,7 +71,7 @@ sub invoice_details { my $query = qq|SELECT date | . conv_dateq($form->{duedate}) . qq| - date | . conv_dateq($form->{invdate}) . qq| AS terms|; ($form->{terms}) = selectrow_query($form, $dbh, $query); - my (@project_ids, %projectnumbers, %projectdescriptions); + my (@project_ids); $form->{TEMPLATE_ARRAYS} = {}; push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"}); @@ -91,31 +95,27 @@ sub invoice_details { push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"}); } + my $projects = []; + my %projects_by_id; if (@project_ids) { - $query = "SELECT id, projectnumber, description FROM project WHERE id IN (" . - join(", ", map({ "?" } @project_ids)) . ")"; - $sth = $dbh->prepare($query); - $sth->execute(@project_ids) || - $form->dberror($query . " (" . join(", ", @project_ids) . ")"); - while (my $ref = $sth->fetchrow_hashref()) { - $projectnumbers{$ref->{id}} = $ref->{projectnumber}; - $projectdescriptions{$ref->{id}} = $ref->{description}; - } - $sth->finish(); + $projects = SL::DB::Manager::Project->get_all(query => [ id => \@project_ids ]); + %projects_by_id = map { $_->id => $_ } @$projects; } - $form->{"globalprojectnumber"} = - $projectnumbers{$form->{"globalproject_id"}}; - $form->{"globalprojectdescription"} = - $projectdescriptions{$form->{"globalproject_id"}}; + if ($projects_by_id{$form->{"globalproject_id"}}) { + $form->{globalprojectnumber} = $projects_by_id{$form->{"globalproject_id"}}->projectnumber; + $form->{globalprojectdescription} = $projects_by_id{$form->{"globalproject_id"}}->description; + + for (@{ $projects_by_id{$form->{"globalproject_id"}}->cvars_by_config }) { + $form->{"project_cvar_" . $_->config->name} = $_->value_as_text; + } + } my $tax = 0; my $item; my $i; my @partsgroup = (); my $partsgroup; - my %oid = ('Pg' => 'oid', - 'Oracle' => 'rowid'); # sort items by partsgroup for $i (1 .. $form->{rowcount}) { @@ -144,19 +144,21 @@ sub invoice_details { $form->{discount} = []; - IC->prepare_parts_for_printing(); + IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form); my $ic_cvar_configs = CVar->get_configs(module => 'IC'); + my $project_cvar_configs = CVar->get_configs(module => 'Projects'); my @arrays = qw(runningnumber number description longdescription qty ship unit bin - deliverydate_oe ordnumber_oe transdate_oe validuntil + deliverydate_oe ordnumber_oe donumber_do transdate_oe validuntil partnotes serialnumber reqdate sellprice listprice netprice discount p_discount discount_sub nodiscount_sub linetotal nodiscount_linetotal tax_rate projectnumber projectdescription - price_factor price_factor_name partsgroup); + price_factor price_factor_name partsgroup weight lineweight); push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs }; + push @arrays, map { "project_cvar_$_->{name}" } @{ $project_cvar_configs }; my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber); @@ -164,6 +166,7 @@ sub invoice_details { map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays, @payment_arrays); + my $totalweight = 0; foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) { $i = $item->[0]; @@ -209,6 +212,7 @@ sub invoice_details { push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} }, $form->{"sellprice_$i"}; push @{ $form->{TEMPLATE_ARRAYS}->{sellprice_nofmt} }, $form->parse_amount($myconfig, $form->{"sellprice_$i"}); push @{ $form->{TEMPLATE_ARRAYS}->{ordnumber_oe} }, $form->{"ordnumber_$i"}; + push @{ $form->{TEMPLATE_ARRAYS}->{donumber_do} }, $form->{"donumber_$i"}; push @{ $form->{TEMPLATE_ARRAYS}->{transdate_oe} }, $form->{"transdate_$i"}; push @{ $form->{TEMPLATE_ARRAYS}->{invnumber} }, $form->{"invnumber"}; push @{ $form->{TEMPLATE_ARRAYS}->{invdate} }, $form->{"invdate"}; @@ -234,7 +238,7 @@ sub invoice_details { my $discount_round_error = $discount + ($linetotal_exact - $nodiscount_exact_linetotal); # not used - $form->{"netprice_$i"} = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, 2); + $form->{"netprice_$i"} = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, $decimalplaces); push @{ $form->{TEMPLATE_ARRAYS}->{netprice} }, ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : ''; push @{ $form->{TEMPLATE_ARRAYS}->{netprice_nofmt} }, ($form->{"netprice_$i"} != 0) ? $form->{"netprice_$i"} : ''; @@ -277,8 +281,17 @@ sub invoice_details { push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal} }, $form->format_amount($myconfig, $nodiscount_linetotal, 2); push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal_nofmt} }, $nodiscount_linetotal; - push(@{ $form->{TEMPLATE_ARRAYS}->{projectnumber} }, $projectnumbers{$form->{"project_id_$i"}}); - push(@{ $form->{TEMPLATE_ARRAYS}->{projectdescription} }, $projectdescriptions{$form->{"project_id_$i"}}); + my $project = $projects_by_id{$form->{"project_id_$i"}} || SL::DB::Project->new; + + push @{ $form->{TEMPLATE_ARRAYS}->{projectnumber} }, $project->projectnumber; + push @{ $form->{TEMPLATE_ARRAYS}->{projectdescription} }, $project->description; + + my $lineweight = $form->{"qty_$i"} * $form->{"weight_$i"}; + $totalweight += $lineweight; + push @{ $form->{TEMPLATE_ARRAYS}->{weight} }, $form->format_amount($myconfig, $form->{"weight_$i"}, 3); + push @{ $form->{TEMPLATE_ARRAYS}->{weight_nofmt} }, $form->{"weight_$i"}; + push @{ $form->{TEMPLATE_ARRAYS}->{lineweight} }, $form->format_amount($myconfig, $lineweight, 3); + push @{ $form->{TEMPLATE_ARRAYS}->{lineweight_nofmt} }, $lineweight; @taxaccounts = split(/ /, $form->{"taxaccounts_$i"}); $taxrate = 0; @@ -328,9 +341,9 @@ sub invoice_details { my $sortorder = ""; if ($form->{groupitems}) { $sortorder = - qq|ORDER BY pg.partsgroup, a.$oid{$myconfig->{dbdriver}}|; + qq|ORDER BY pg.partsgroup, a.oid|; } else { - $sortorder = qq|ORDER BY a.$oid{$myconfig->{dbdriver}}|; + $sortorder = qq|ORDER BY a.oid|; } $query = @@ -360,12 +373,25 @@ sub invoice_details { $sth->finish; } + CVar->get_non_editable_ic_cvars(form => $form, + dbh => $dbh, + row => $i, + sub_module => 'invoice', + may_converted_from => ['delivery_order_items', 'orderitems', 'invoice']); + push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} }, CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_) for @{ $ic_cvar_configs }; + + push @{ $form->{TEMPLATE_ARRAYS}->{"project_cvar_" . $_->config->name} }, $_->value_as_text for @{ $project->cvars_by_config }; } } + $form->{totalweight} = $form->format_amount($myconfig, $totalweight, 3); + $form->{totalweight_nofmt} = $totalweight; + my $defaults = AM->get_defaults(); + $form->{weightunit} = $defaults->{weightunit}; + foreach my $item (sort keys %taxaccounts) { $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2); @@ -375,8 +401,11 @@ sub invoice_details { push(@{ $form->{TEMPLATE_ARRAYS}->{tax_nofmt} }, $taxamount ); push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} }, $form->format_amount($myconfig, $form->{"${item}_rate"} * 100)); push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate_nofmt} }, $form->{"${item}_rate"} * 100); - push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $form->{"${item}_description"} . q{ } . 100 * $form->{"${item}_rate"} . q{%}); push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} }, $form->{"${item}_taxnumber"}); + + my $tax_obj = SL::DB::Manager::Tax->find_by(taxnumber => $form->{"${item}_taxnumber"}); + my $description = $tax_obj ? $tax_obj->translated_attribute('taxdescription', $form->{language_id}, 0) : ''; + push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $description . q{ } . 100 * $form->{"${item}_rate"} . q{%}); } for my $i (1 .. $form->{paidaccounts}) { @@ -414,6 +443,9 @@ sub invoice_details { $form->set_payment_options($myconfig, $form->{invdate}); + $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef); + $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id}; + $form->{username} = $myconfig->{name}; $main::lxdebug->leave_sub(); @@ -458,14 +490,30 @@ sub customer_details { my $query = qq|SELECT ct.*, cp.*, ct.notes as customernotes, ct.phone AS customerphone, ct.fax AS customerfax, ct.email AS customeremail, - ct.curr AS currency + cu.name AS currency FROM customer ct LEFT JOIN contacts cp on ct.id = cp.cp_cv_id + LEFT JOIN currencies cu ON (ct.currency_id = cu.id) WHERE (ct.id = ?) $where ORDER BY cp.cp_id LIMIT 1|; my $ref = selectfirst_hashref_query($form, $dbh, $query, @values); - + # we have no values, probably a invalid contact person. hotfix and first idea for issue #10 + if (!$ref) { + my $customer = SL::DB::Manager::Customer->find_by(id => $::form->{customer_id}); + if ($customer) { + $ref->{name} = $customer->name; + $ref->{street} = $customer->street; + $ref->{zipcode} = $customer->zipcode; + $ref->{country} = $customer->country; + } + my $contact = SL::DB::Manager::Contact->find_by(cp_id => $::form->{cp_id}); + if ($contact) { + $ref->{cp_name} = $contact->cp_name; + $ref->{cp_givenname} = $contact->cp_givenname; + $ref->{cp_gender} = $contact->cp_gender; + } + } # remove id and taxincluded before copy back delete @$ref{qw(id taxincluded)}; @@ -478,9 +526,6 @@ sub customer_details { map { $form->{$_} = $ref->{$_} } keys %$ref; - # remove any trailing whitespace - $form->{currency} =~ s/\s*$// if ($form->{currency}); - if ($form->{delivery_customer_id}) { $query = qq|SELECT *, notes as customernotes @@ -524,6 +569,7 @@ sub post_invoice { # connect to database, turn off autocommit my $dbh = $provided_dbh ? $provided_dbh : $form->get_standard_dbh; + my $restricter = SL::HTML::Restrict->create; my ($query, $sth, $null, $project_id, @values); my $exchangerate = 0; @@ -536,12 +582,7 @@ sub post_invoice { } $form->{defaultcurrency} = $form->get_default_currency($myconfig); - # Seit neuestem wird die department_id schon übergeben UND $form->department nicht mehr - # korrekt zusammengebaut. Sehr wahrscheinlich beim Umstieg auf T8 kaputt gegangen - # Ich lass den Code von 2005 erstmal noch stehen ;-) jb 03-2011 - if (!$form->{department_id}){ - ($null, $form->{department_id}) = split(/--/, $form->{department}); - } + my $defaultcurrency = $form->{defaultcurrency}; my $all_units = AM->retrieve_units($myconfig, $form); @@ -556,13 +597,12 @@ sub post_invoice { $query = qq|SELECT nextval('glid')|; ($form->{"id"}) = selectrow_query($form, $dbh, $query); - $query = qq|INSERT INTO ar (id, invnumber) VALUES (?, ?)|; - do_query($form, $dbh, $query, $form->{"id"}, $form->{"id"}); + $query = qq|INSERT INTO ar (id, invnumber, currency_id, taxzone_id) VALUES (?, ?, (SELECT id FROM currencies WHERE name=?), ?)|; + do_query($form, $dbh, $query, $form->{"id"}, $form->{"id"}, $form->{currency}, $form->{taxzone_id}); if (!$form->{invnumber}) { - $form->{invnumber} = - $form->update_defaults($myconfig, $form->{type} eq "credit_note" ? - "cnnumber" : "invnumber", $dbh); + my $trans_number = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{invnumber}, id => $form->{id}); + $form->{invnumber} = $trans_number->create_unique; } } } @@ -570,9 +610,6 @@ sub post_invoice { my ($netamount, $invoicediff) = (0, 0); my ($amount, $linetotal, $lastincomeaccno); - my ($currencies) = selectfirst_array_query($form, $dbh, qq|SELECT curr FROM defaults|); - my $defaultcurrency = (split m/:/, $currencies)[0]; - if ($form->{currency} eq $defaultcurrency) { $form->{exchangerate} = 1; } else { @@ -595,6 +632,8 @@ sub post_invoice { $form->{amount} = {}; $form->{amount_cogs} = {}; + my @processed_invoice_ids; + foreach my $i (1 .. $form->{rowcount}) { if ($form->{type} eq "credit_note") { $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"}) * -1; @@ -615,6 +654,7 @@ sub post_invoice { if ($form->{"id_$i"}) { my $item_unit; + my $position = $i; if (defined($baseunits{$form->{"id_$i"}})) { $item_unit = $baseunits{$form->{"id_$i"}}; @@ -700,7 +740,7 @@ sub post_invoice { if ($form->{"assembly_$i"}) { # record assembly item as allocated - &process_assembly($dbh, $myconfig, $form, $form->{"id_$i"}, $baseqty); + &process_assembly($dbh, $myconfig, $form, $position, $form->{"id_$i"}, $baseqty); } else { $allocated = &cogs($dbh, $myconfig, $form, $form->{"id_$i"}, $baseqty, $basefactor, $i); @@ -715,42 +755,74 @@ sub post_invoice { $pricegroup_id *= 1; $pricegroup_id = undef if !$pricegroup_id; - my ($invoice_id) = selectfirst_array_query($form, $dbh, qq|SELECT nextval('invoiceid')|); + CVar->get_non_editable_ic_cvars(form => $form, + dbh => $dbh, + row => $i, + sub_module => 'invoice', + may_converted_from => ['delivery_order_items', 'orderitems', 'invoice']); + + if (!$form->{"invoice_id_$i"}) { + # there is no persistent id, therefore create one with all necessary constraints + my $q_invoice_id = qq|SELECT nextval('invoiceid')|; + my $h_invoice_id = prepare_query($form, $dbh, $q_invoice_id); + do_statement($form, $h_invoice_id, $q_invoice_id); + $form->{"invoice_id_$i"} = $h_invoice_id->fetchrow_array(); + my $q_create_invoice_id = qq|INSERT INTO invoice (id, trans_id, position, parts_id) values (?, ?, ?, ?)|; + do_query($form, $dbh, $q_create_invoice_id, conv_i($form->{"invoice_id_$i"}), + conv_i($form->{id}), conv_i($position), conv_i($form->{"id_$i"})); + $h_invoice_id->finish(); + } # save detail record in invoice table - $query = - qq|INSERT INTO invoice (id, trans_id, parts_id, description, longdescription, qty, - sellprice, fxsellprice, discount, allocated, assemblyitem, - unit, deliverydate, project_id, serialnumber, pricegroup_id, - ordnumber, transdate, cusordnumber, base_qty, subtotal, - marge_percent, marge_total, lastcost, - price_factor_id, price_factor, marge_price_factor) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - (SELECT factor FROM price_factors WHERE id = ?), ?)|; - - @values = ($invoice_id, conv_i($form->{id}), conv_i($form->{"id_$i"}), - $form->{"description_$i"}, $form->{"longdescription_$i"}, $form->{"qty_$i"}, + $query = <{id}), conv_i($position), conv_i($form->{"id_$i"}), + $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}), $form->{"qty_$i"}, $form->{"sellprice_$i"}, $fxsellprice, $form->{"discount_$i"}, $allocated, 'f', $form->{"unit_$i"}, conv_date($form->{"reqdate_$i"}), conv_i($form->{"project_id_$i"}), $form->{"serialnumber_$i"}, $pricegroup_id, - $form->{"ordnumber_$i"}, conv_date($form->{"transdate_$i"}), + $form->{"ordnumber_$i"}, $form->{"donumber_$i"}, conv_date($form->{"transdate_$i"}), $form->{"cusordnumber_$i"}, $baseqty, $form->{"subtotal_$i"} ? 't' : 'f', $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"}, $form->{"lastcost_$i"}, + $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"}, conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}), - conv_i($form->{"marge_price_factor_$i"})); + conv_i($form->{"marge_price_factor_$i"}), + conv_i($form->{"invoice_id_$i"})); do_query($form, $dbh, $query, @values); + push @processed_invoice_ids, $form->{"invoice_id_$i"}; CVar->save_custom_variables(module => 'IC', sub_module => 'invoice', - trans_id => $invoice_id, + trans_id => $form->{"invoice_id_$i"}, configs => $ic_cvar_configs, variables => $form, name_prefix => 'ic_', name_postfix => "_$i", dbh => $dbh); } + # link previous items with invoice items + foreach (qw(delivery_order_items orderitems invoice)) { + if (!$form->{useasnew} && $form->{"converted_from_${_}_id_$i"}) { + RecordLinks->create_links('dbh' => $dbh, + 'mode' => 'ids', + 'from_table' => $_, + 'from_ids' => $form->{"converted_from_${_}_id_$i"}, + 'to_table' => 'invoice', + 'to_id' => $form->{"invoice_id_$i"}, + ); + } + delete $form->{"converted_from_${_}_id_$i"}; + } } # total payments, don't move we need it here @@ -1085,13 +1157,15 @@ sub post_invoice { amount = ?, netamount = ?, paid = ?, duedate = ?, deliverydate = ?, invoice = ?, shippingpoint = ?, shipvia = ?, terms = ?, notes = ?, intnotes = ?, - curr = ?, department_id = ?, payment_id = ?, taxincluded = ?, + currency_id = (SELECT id FROM currencies WHERE name = ?), + department_id = ?, payment_id = ?, taxincluded = ?, type = ?, language_id = ?, taxzone_id = ?, shipto_id = ?, employee_id = ?, salesman_id = ?, storno_id = ?, storno = ?, cp_id = ?, marge_total = ?, marge_percent = ?, globalproject_id = ?, delivery_customer_id = ?, transaction_description = ?, delivery_vendor_id = ?, - donumber = ?, invnumber_for_credit_note = ?, direct_debit = ? + donumber = ?, invnumber_for_credit_note = ?, direct_debit = ?, + delivery_term_id = ? WHERE id = ?|; @values = ( $form->{"invnumber"}, $form->{"ordnumber"}, $form->{"quonumber"}, $form->{"cusordnumber"}, conv_date($form->{"invdate"}), conv_date($form->{"orddate"}), conv_date($form->{"quodate"}), conv_i($form->{"customer_id"}), @@ -1105,6 +1179,7 @@ sub post_invoice { conv_i($form->{"globalproject_id"}), conv_i($form->{"delivery_customer_id"}), $form->{transaction_description}, conv_i($form->{"delivery_vendor_id"}), $form->{"donumber"}, $form->{"invnumber_for_credit_note"}, $form->{direct_debit} ? 't' : 'f', + conv_i($form->{delivery_term_id}), conv_i($form->{"id"})); do_query($form, $dbh, $query, @values); @@ -1120,10 +1195,10 @@ sub post_invoice { do_query($form, $dbh, qq|UPDATE ar SET paid = amount WHERE id = ?|, conv_i($form->{"id"})); } - # add shipto $form->{name} = $form->{customer}; $form->{name} =~ s/--\Q$form->{customer_id}\E//; + # add shipto if (!$form->{shipto_id}) { $form->add_shipto($dbh, $form->{id}, "AR"); } @@ -1133,15 +1208,28 @@ sub post_invoice { Common::webdav_folder($form); - # Link this record to the records it was created from. - RecordLinks->create_links('dbh' => $dbh, - 'mode' => 'ids', - 'from_table' => 'oe', - 'from_ids' => $form->{convert_from_oe_ids}, - 'to_table' => 'ar', - 'to_id' => $form->{id}, + if ($form->{convert_from_ar_ids}) { + RecordLinks->create_links('dbh' => $dbh, + 'mode' => 'ids', + 'from_table' => 'ar', + 'from_ids' => $form->{convert_from_ar_ids}, + 'to_table' => 'ar', + 'to_id' => $form->{id}, ); - delete $form->{convert_from_oe_ids}; + delete $form->{convert_from_ar_ids}; + } + + # Link this record to the records it was created from. + if ($form->{convert_from_oe_ids}) { + RecordLinks->create_links('dbh' => $dbh, + 'mode' => 'ids', + 'from_table' => 'oe', + 'from_ids' => $form->{convert_from_oe_ids}, + 'to_table' => 'ar', + 'to_id' => $form->{id}, + ); + delete $form->{convert_from_oe_ids}; + } my @convert_from_do_ids = map { $_ * 1 } grep { $_ } split m/\s+/, $form->{convert_from_do_ids}; @@ -1163,6 +1251,16 @@ sub post_invoice { 'arap_id' => $form->{id}, 'table' => 'ar',); + # search for orphaned invoice items + $query = sprintf 'SELECT id FROM invoice WHERE trans_id = ? AND NOT id IN (%s)', join ', ', ("?") x scalar @processed_invoice_ids; + @values = (conv_i($form->{id}), map { conv_i($_) } @processed_invoice_ids); + my @orphaned_ids = map { $_->{id} } selectall_hashref_query($form, $dbh, $query, @values); + if (scalar @orphaned_ids) { + # clean up invoice items + $query = sprintf 'DELETE FROM invoice WHERE id IN (%s)', join ', ', ("?") x scalar @orphaned_ids; + do_query($form, $dbh, $query, @orphaned_ids); + } + # safety check datev export if ($::instance_conf->get_datev_check_on_sales_invoice) { my $transdate = $::form->{invdate} ? DateTime->from_lxoffice($::form->{invdate}) : undef; @@ -1174,6 +1272,7 @@ sub post_invoice { dbh => $dbh, from => $transdate, to => $transdate, + trans_id => $form->{id}, ); $datev->export; @@ -1238,7 +1337,6 @@ sub post_payment { # connect to database, turn off autocommit my $dbh = $form->get_standard_dbh; - $dbh->begin_work; my (%payments, $old_form, $row, $item, $query, %keep_vars); @@ -1304,7 +1402,7 @@ sub post_payment { sub process_assembly { $main::lxdebug->enter_sub(); - my ($dbh, $myconfig, $form, $id, $totalqty) = @_; + my ($dbh, $myconfig, $form, $position, $id, $totalqty) = @_; my $query = qq|SELECT a.parts_id, a.qty, p.assembly, p.partnumber, p.description, p.unit, @@ -1325,7 +1423,7 @@ sub process_assembly { $ref->{qty} *= $totalqty; if ($ref->{assembly}) { - &process_assembly($dbh, $myconfig, $form, $ref->{parts_id}, $ref->{qty}); + &process_assembly($dbh, $myconfig, $form, $position, $ref->{parts_id}, $ref->{qty}); next; } else { if ($ref->{inventory_accno_id}) { @@ -1335,9 +1433,10 @@ sub process_assembly { # save detail record for individual assembly item in invoice table $query = - qq|INSERT INTO invoice (trans_id, description, parts_id, qty, sellprice, fxsellprice, allocated, assemblyitem, unit) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)|; - my @values = (conv_i($form->{id}), $ref->{description}, conv_i($ref->{parts_id}), $ref->{qty}, 0, 0, $allocated, 't', $ref->{unit}); + qq|INSERT INTO invoice (trans_id, position, description, parts_id, qty, sellprice, fxsellprice, allocated, assemblyitem, unit) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)|; + my @values = (conv_i($form->{id}), conv_i($position), $ref->{description}, + conv_i($ref->{parts_id}), $ref->{qty}, 0, 0, $allocated, 't', $ref->{unit}); do_query($form, $dbh, $query, @values); } @@ -1367,8 +1466,8 @@ sub cogs { c3.accno AS expense_accno, c3.new_chart_id AS expense_new_chart, date($transdate) - c3.valid_from AS expense_valid FROM invoice i, parts p LEFT JOIN chart c1 ON ((SELECT inventory_accno_id FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c1.id) - LEFT JOIN chart c2 ON ((SELECT income_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c2.id) - LEFT JOIN chart c3 ON ((select expense_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c3.id) + LEFT JOIN chart c2 ON ((SELECT tc.income_accno_id FROM taxzone_charts tc WHERE tc.taxzone_id = '$taxzone_id' and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c2.id) + LEFT JOIN chart c3 ON ((SELECT tc.expense_accno_id FROM taxzone_charts tc WHERE tc.taxzone_id = '$taxzone_id' and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c3.id) WHERE (i.parts_id = p.id) AND (i.parts_id = ?) AND ((i.base_qty + i.allocated) < 0) @@ -1484,7 +1583,6 @@ sub reverse_invoice { # delete acc_trans my @values = (conv_i($form->{id})); do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE trans_id = ?|, @values); - do_query($form, $dbh, qq|DELETE FROM invoice WHERE trans_id = ?|, @values); do_query($form, $dbh, qq|DELETE FROM shipto WHERE (trans_id = ?) AND (module = 'AR')|, @values); $main::lxdebug->leave_sub(); @@ -1497,7 +1595,6 @@ sub delete_invoice { # connect to database my $dbh = $form->get_standard_dbh; - $dbh->begin_work; &reverse_invoice($dbh, $form); @@ -1523,6 +1620,7 @@ sub delete_invoice { my @queries = ( qq|DELETE FROM status WHERE trans_id = ?|, qq|DELETE FROM periodic_invoices WHERE ar_id = ?|, + qq|DELETE FROM invoice WHERE trans_id = ?|, qq|DELETE FROM ar WHERE id = ?|, ); @@ -1558,8 +1656,7 @@ sub retrieve_invoice { (SELECT c.accno FROM chart c WHERE d.income_accno_id = c.id) AS income_accno, (SELECT c.accno FROM chart c WHERE d.expense_accno_id = c.id) AS expense_accno, (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno, - (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno, - d.curr AS currencies + (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno ${query_transdate} FROM defaults d|; @@ -1578,11 +1675,11 @@ sub retrieve_invoice { a.orddate, a.quodate, a.globalproject_id, a.transdate AS invdate, a.deliverydate, a.paid, a.storno, a.gldate, a.shippingpoint, a.shipvia, a.terms, a.notes, a.intnotes, a.taxzone_id, - a.duedate, a.taxincluded, a.curr AS currency, a.shipto_id, a.cp_id, + a.duedate, a.taxincluded, (SELECT cu.name FROM currencies cu WHERE cu.id=a.currency_id) AS currency, a.shipto_id, a.cp_id, a.employee_id, a.salesman_id, a.payment_id, a.language_id, a.delivery_customer_id, a.delivery_vendor_id, a.type, a.transaction_description, a.donumber, a.invnumber_for_credit_note, - a.marge_total, a.marge_percent, a.direct_debit, + a.marge_total, a.marge_percent, a.direct_debit, a.delivery_term_id, e.name AS employee FROM ar a LEFT JOIN employee e ON (e.id = a.employee_id) @@ -1590,17 +1687,8 @@ sub retrieve_invoice { $ref = selectfirst_hashref_query($form, $dbh, $query, $id); map { $form->{$_} = $ref->{$_} } keys %{ $ref }; - # remove any trailing whitespace - $form->{currency} =~ s/\s*$//; - $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{invdate}, "buy"); - # get shipto - $query = qq|SELECT * FROM shipto WHERE (trans_id = ?) AND (module = 'AR')|; - $ref = selectfirst_hashref_query($form, $dbh, $query, $id); - delete $ref->{id}; - map { $form->{$_} = $ref->{$_} } keys %{ $ref }; - foreach my $vc (qw(customer vendor)) { next if !$form->{"delivery_${vc}_id"}; ($form->{"delivery_${vc}_string"}) = selectrow_query($form, $dbh, qq|SELECT name FROM customer WHERE id = ?|, $id); @@ -1624,7 +1712,7 @@ sub retrieve_invoice { my $taxzone_id = $form->{taxzone_id} *= 1; - $taxzone_id = 0 if (0 > $taxzone_id) || (3 < $taxzone_id); + $taxzone_id = SL::DB::Manager::TaxZone->get_default->id unless SL::DB::Manager::TaxZone->find_by(id => $taxzone_id); # retrieve individual items $query = @@ -1635,9 +1723,9 @@ sub retrieve_invoice { i.id AS invoice_id, i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.discount, i.parts_id AS id, i.unit, i.deliverydate AS reqdate, - i.project_id, i.serialnumber, i.id AS invoice_pos, i.pricegroup_id, i.ordnumber, i.transdate, i.cusordnumber, i.subtotal, i.lastcost, - i.price_factor_id, i.price_factor, i.marge_price_factor, - p.partnumber, p.assembly, p.bin, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, p.formel, p.listprice, + i.project_id, i.serialnumber, i.pricegroup_id, i.ordnumber, i.donumber, i.transdate, i.cusordnumber, i.subtotal, i.lastcost, + i.price_factor_id, i.price_factor, i.marge_price_factor, i.active_price_source, i.active_discount_source, + p.partnumber, p.assembly, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, p.formel, p.listprice, pr.projectnumber, pg.partsgroup, prg.pricegroup FROM invoice i @@ -1647,10 +1735,10 @@ sub retrieve_invoice { LEFT JOIN pricegroup prg ON (i.pricegroup_id = prg.id) LEFT JOIN chart c1 ON ((SELECT inventory_accno_id FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c1.id) - LEFT JOIN chart c2 ON ((SELECT income_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c2.id) - LEFT JOIN chart c3 ON ((SELECT expense_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c3.id) + LEFT JOIN chart c2 ON ((SELECT tc.income_accno_id FROM taxzone_charts tc WHERE tc.taxzone_id = '$taxzone_id' and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c2.id) + LEFT JOIN chart c3 ON ((SELECT tc.expense_accno_id FROM taxzone_charts tc WHERE tc.taxzone_id = '$taxzone_id' and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c3.id) - WHERE (i.trans_id = ?) AND NOT (i.assemblyitem = '1') ORDER BY i.id|; + WHERE (i.trans_id = ?) AND NOT (i.assemblyitem = '1') ORDER BY i.position|; $sth = prepare_execute_query($form, $dbh, $query, $id); @@ -1662,7 +1750,6 @@ sub retrieve_invoice { trans_id => $ref->{invoice_id}, ); map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars }; - delete $ref->{invoice_id}; map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid)) if !$ref->{"part_inventory_accno_id"}; delete($ref->{"part_inventory_accno_id"}); @@ -1755,15 +1842,16 @@ sub get_customer { $query = qq|SELECT c.id AS customer_id, c.name AS customer, c.discount as customer_discount, c.creditlimit, c.terms, - c.email, c.cc, c.bcc, c.language_id, c.payment_id, + c.email, c.cc, c.bcc, c.language_id, c.payment_id, c.delivery_term_id, c.street, c.zipcode, c.city, c.country, - c.notes AS intnotes, c.klass as customer_klass, c.taxzone_id, c.salesman_id, c.curr, + c.notes AS intnotes, c.klass as customer_klass, c.taxzone_id, c.salesman_id, cu.name AS curr, c.taxincluded_checked, c.direct_debit, $duedate + COALESCE(pt.terms_netto, 0) AS duedate, b.discount AS tradediscount, b.description AS business FROM customer c LEFT JOIN business b ON (b.id = c.business_id) LEFT JOIN payment_terms pt ON ($payment_id (c.payment_id = pt.id)) + LEFT JOIN currencies cu ON (c.currency_id=cu.id) WHERE c.id = ?|; push @values, $cid; $ref = selectfirst_hashref_query($form, $dbh, $query, @values); @@ -1772,11 +1860,8 @@ sub get_customer { map { $form->{$_} = $ref->{$_} } keys %$ref; - # remove any trailing whitespace - $form->{curr} =~ s/\s*$//; - - # use customer currency if not empty - $form->{currency} = $form->{curr} if $form->{curr}; + # use customer currency + $form->{currency} = $form->{curr}; $query = qq|SELECT sum(amount - paid) AS dunning_amount @@ -1805,7 +1890,7 @@ sub get_customer { $query = qq|SELECT o.amount, (SELECT e.buy FROM exchangerate e - WHERE e.curr = o.curr + WHERE e.currency_id = o.currency_id AND e.transdate = o.transdate) FROM oe o WHERE o.customer_id = ? @@ -1819,19 +1904,6 @@ sub get_customer { } $sth->finish; - # get shipto if we did not converted an order or invoice - if (!$form->{shipto}) { - map { delete $form->{$_} } - qw(shiptoname shiptodepartment_1 shiptodepartment_2 - shiptostreet shiptozipcode shiptocity shiptocountry - shiptocontact shiptophone shiptofax shiptoemail); - - $query = qq|SELECT * FROM shipto WHERE trans_id = ? AND module = 'CT'|; - $ref = selectfirst_hashref_query($form, $dbh, $query, $cid); - delete $ref->{id}; - map { $form->{$_} = $ref->{$_} } keys %$ref; - } - # setup last accounts used for this customer if (!$form->{id} && $form->{type} !~ /_(order|quotation)/) { $query = @@ -1895,10 +1967,23 @@ sub retrieve_item { push @values, '%' . $form->{"${field}_${i}"} . '%'; } - #Es soll auch nach EAN gesucht werden, ohne Einschränkung durch Beschreibung + my (%mm_by_id); if ($form->{"partnumber_$i"} && !$form->{"description_$i"}) { $where .= qq| OR (NOT p.obsolete = '1' AND p.ean = ? )|; push @values, $form->{"partnumber_$i"}; + + # also search hits in makemodels, but only cache the results by id and merge later + my $mm_query = qq| + SELECT parts_id, model FROM makemodel LEFT JOIN parts ON parts.id = parts_id WHERE NOT parts.obsolete AND model ILIKE ?; + |; + my $mm_results = selectall_hashref_query($::form, $dbh, $mm_query, '%' . $form->{"partnumber_$i"} . '%'); + my @mm_ids = map { $_->{parts_id} } @$mm_results; + push @{$mm_by_id{ $_->{parts_id} } ||= []}, $_ for @$mm_results; + + if (@mm_ids) { + $where .= qq| OR p.id IN (| . join(',', ('?') x @mm_ids) . qq|)|; + push @values, @mm_ids; + } } # Search for part ID overrides all other criteria. @@ -1932,6 +2017,7 @@ sub retrieve_item { qq|SELECT p.id, p.partnumber, p.description, p.sellprice, p.listprice, p.inventory_accno_id, p.lastcost, + p.ean, c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, @@ -1945,10 +2031,10 @@ sub retrieve_item { c3.new_chart_id AS expense_new_chart, date($transdate) - c3.valid_from AS expense_valid, - p.unit, p.assembly, p.bin, p.onhand, + p.unit, p.assembly, p.onhand, p.notes AS partnotes, p.notes AS longdescription, p.not_discountable, p.formel, p.payment_id AS part_payment_id, - p.price_factor_id, + p.price_factor_id, p.weight, pfac.factor AS price_factor, @@ -1960,13 +2046,13 @@ sub retrieve_item { FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c1.id) LEFT JOIN chart c2 ON - ((SELECT income_accno_id_${taxzone_id} - FROM buchungsgruppen - WHERE id = p.buchungsgruppen_id) = c2.id) + ((SELECT tc.income_accno_id + FROM taxzone_charts tc + WHERE tc.buchungsgruppen_id = p.buchungsgruppen_id and tc.taxzone_id = ${taxzone_id}) = c2.id) LEFT JOIN chart c3 ON - ((SELECT expense_accno_id_${taxzone_id} - FROM buchungsgruppen - WHERE id = p.buchungsgruppen_id) = c3.id) + ((SELECT tc.expense_accno_id + FROM taxzone_charts tc + WHERE tc.buchungsgruppen_id = p.buchungsgruppen_id and tc.taxzone_id = ${taxzone_id}) = c3.id) LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id) LEFT JOIN price_factors pfac ON (pfac.id = p.price_factor_id) WHERE $where|; @@ -1987,6 +2073,15 @@ sub retrieve_item { while (my $ref = $sth->fetchrow_hashref('NAME_lc')) { + if ($mm_by_id{$ref->{id}}) { + $ref->{makemodels} = $mm_by_id{$ref->{id}}; + push @{ $ref->{matches} ||= [] }, $::locale->text('Model') . ': ' . join ', ', map { $_->{model} } @{ $mm_by_id{$ref->{id}} }; + } + + if ($ref->{ean} eq $::form->{"partnumber_$i"}) { + push @{ $ref->{matches} ||= [] }, $::locale->text('EAN') . ': ' . $ref->{ean}; + } + # In der Buchungsgruppe ist immer ein Bestandskonto verknuepft, auch wenn # es sich um eine Dienstleistung handelt. Bei Dienstleistungen muss das # Buchungskonto also aus dem Ergebnis rausgenommen werden. @@ -2082,196 +2177,6 @@ sub retrieve_item { $main::lxdebug->leave_sub(); } -########################## -# get pricegroups from database -# build up selected pricegroup -# if an exchange rate - change price -# for each part -# -sub get_pricegroups_for_parts { - - $main::lxdebug->enter_sub(); - - my ($self, $myconfig, $form) = @_; - - my $dbh = $form->get_standard_dbh; - - $form->{"PRICES"} = {}; - - my $i = 1; - my $id = 0; - my $all_units = AM->retrieve_units($myconfig, $form); - while (($form->{"id_$i"}) or ($form->{"new_id_$i"})) { - $form->{"PRICES"}{$i} = []; - - $id = $form->{"id_$i"}; - - if (!($form->{"id_$i"}) and $form->{"new_id_$i"}) { - $id = $form->{"new_id_$i"}; - } - - my ($price, $selectedpricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"}); - - my $pricegroup_old = $form->{"pricegroup_old_$i"}; - - # sellprice has format 13,0000 or 0,00000, can't check for 0 numerically - my $sellprice = $form->{"sellprice_$i"}; - my $pricegroup_id = $form->{"pricegroup_id_$i"}; - $form->{"new_pricegroup_$i"} = $selectedpricegroup_id; - $form->{"old_pricegroup_$i"} = $pricegroup_old; - - my $price_new = $form->{"price_new_$i"}; - my $price_old = $form->{"price_old_$i"}; - - if (!$form->{"unit_old_$i"}) { - # Neue Ware aus der Datenbank. In diesem Fall ist unit_$i die - # Einheit, wie sie in den Stammdaten hinterlegt wurde. - # Es sollte also angenommen werden, dass diese ausgewaehlt war. - $form->{"unit_old_$i"} = $form->{"unit_$i"}; - } - - # Die zuletzt ausgewaehlte mit der aktuell ausgewaehlten Einheit - # vergleichen und bei Unterschied den Preis entsprechend umrechnen. - $form->{"selected_unit_$i"} = $form->{"unit_$i"} unless ($form->{"selected_unit_$i"}); - - if (!$all_units->{$form->{"selected_unit_$i"}} || - ($all_units->{$form->{"selected_unit_$i"}}->{"base_unit"} ne - $all_units->{$form->{"unit_old_$i"}}->{"base_unit"})) { - # Die ausgewaehlte Einheit ist fuer diesen Artikel nicht gueltig - # (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber - # um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen. - $form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"}; - } - - my $basefactor = 1; - - if ($form->{"unit_old_$i"} ne $form->{"selected_unit_$i"}) { - if (defined($all_units->{$form->{"unit_old_$i"}}->{"factor"}) && - $all_units->{$form->{"unit_old_$i"}}->{"factor"}) { - $basefactor = $all_units->{$form->{"selected_unit_$i"}}->{"factor"} / - $all_units->{$form->{"unit_old_$i"}}->{"factor"}; - } - } - - if (!$form->{"basefactor_$i"}) { - $form->{"basefactor_$i"} = 1; - } - - my $query = - qq|SELECT - 0 as pricegroup_id, - sellprice AS default_sellprice, - '' AS pricegroup, - sellprice AS price, - 'selected' AS selected - FROM parts - WHERE id = ? - UNION ALL - SELECT - pricegroup_id, - parts.sellprice AS default_sellprice, - pricegroup.pricegroup, - price, - '' AS selected - FROM prices - LEFT JOIN parts ON parts.id = parts_id - LEFT JOIN pricegroup ON pricegroup.id = pricegroup_id - WHERE parts_id = ? - ORDER BY pricegroup|; - my @values = (conv_i($id), conv_i($id)); - my $pkq = prepare_execute_query($form, $dbh, $query, @values); - - while (my $pkr = $pkq->fetchrow_hashref('NAME_lc')) { - $pkr->{id} = $id; - $pkr->{selected} = ''; - - # if there is an exchange rate change price - if (($form->{exchangerate} * 1) != 0) { - $pkr->{price} /= $form->{exchangerate}; - } - - $pkr->{price} *= $form->{"basefactor_$i"}; - $pkr->{price} *= $basefactor; - $pkr->{price_ufmt} = $pkr->{price}; - $pkr->{price} = $form->format_amount($myconfig, $pkr->{price}, 5); - - if (!defined $selectedpricegroup_id) { - # new entries in article list, either old invoice was loaded (edit) or a new article was added - # Case A: open old invoice, no pricegroup selected - # Case B: add new article to invoice, no pricegroup selected - - # to distinguish case A and B the variable pricegroup_id_$i is used - # for new articles this variable isn't defined, for loaded articles it is - # sellprice can't be used, as it already has 0,00 set - - if ($pkr->{pricegroup_id} eq $form->{"pricegroup_id_$i"} and defined $form->{"pricegroup_id_$i"}) { - # Case A - $pkr->{selected} = ' selected'; - } elsif ($pkr->{pricegroup_id} eq $form->{customer_klass} - and not defined $form->{"pricegroup_id_$i"} - and $pkr->{price_ufmt} != 0 # only use customer pricegroup price if it has a value, else use default_sellprice - # for the case where pricegroup prices haven't been set - ) { - # Case B: use default pricegroup of customer - - $pkr->{selected} = ' selected'; # unless $form->{selected}; - # no customer pricesgroup set - if ($pkr->{price_ufmt} == $pkr->{default_sellprice}) { - - $pkr->{price} = $form->{"sellprice_$i"}; - - } else { - -# this sub should not set anything and only return. --sschoeling, 20090506 -# is this correct? put in again... -- grichardson 20110119 - $form->{"sellprice_$i"} = $pkr->{price}; - } - - } elsif ($pkr->{price_ufmt} == $pkr->{default_sellprice} and $pkr->{default_sellprice} != 0) { - $pkr->{price} = $form->{"sellprice_$i"}; - $pkr->{selected} = ' selected'; - } - } - - # existing article: pricegroup or price changed - if ($selectedpricegroup_id or $selectedpricegroup_id == 0) { - if ($selectedpricegroup_id ne $pricegroup_old) { - # pricegroup has changed - if ($pkr->{pricegroup_id} eq $selectedpricegroup_id) { - $pkr->{selected} = ' selected'; - } - } elsif ( ($form->parse_amount($myconfig, $price_new) - != $form->parse_amount($myconfig, $form->{"sellprice_$i"})) - and ($price_new ne 0) and defined $price_new) { - # sellprice has changed - # when loading existing invoices $price_new is NULL - if ($pkr->{pricegroup_id} == 0) { - $pkr->{price} = $form->{"sellprice_$i"}; - $pkr->{selected} = ' selected'; - } - } elsif ($pkr->{pricegroup_id} eq $selectedpricegroup_id) { - # neither sellprice nor pricegroup changed - $pkr->{selected} = ' selected'; - if ( ($pkr->{pricegroup_id} == 0) and ($pkr->{price} == $form->{"sellprice_$i"})) { - # $pkr->{price} = $form->{"sellprice_$i"}; - } else { - $pkr->{price} = $form->{"sellprice_$i"}; - } - } - } - push @{ $form->{PRICES}{$i} }, $pkr; - - } - $form->{"basefactor_$i"} *= $basefactor; - - $i++; - - $pkq->finish; - } - - $main::lxdebug->leave_sub(); -} - sub has_storno { $main::lxdebug->enter_sub();