1 #=====================================================================
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
7 #=====================================================================
8 # SQL-Ledger Accounting
9 # Copyright (C) 1998-2002
11 # Author: Dieter Simader
12 # Email: dsimader@sql-ledger.org
13 # Web: http://www.sql-ledger.org
17 # This program is free software; you can redistribute it and/or modify
18 # it under the terms of the GNU General Public License as published by
19 # the Free Software Foundation; either version 2 of the License, or
20 # (at your option) any later version.
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 # GNU General Public License for more details.
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #======================================================================
31 # Inventory invoicing module
33 #======================================================================
37 use List::Util qw(max);
43 use SL::DATEV qw(:CONSTANTS);
46 use SL::GenericTranslations;
59 $main::lxdebug->enter_sub();
61 my ($self, $myconfig, $form, $locale) = @_;
63 $form->{duedate} ||= $form->{invdate};
66 my $dbh = $form->get_standard_dbh;
69 my $query = qq|SELECT date | . conv_dateq($form->{duedate}) . qq| - date | . conv_dateq($form->{invdate}) . qq| AS terms|;
70 ($form->{terms}) = selectrow_query($form, $dbh, $query);
73 $form->{TEMPLATE_ARRAYS} = {};
75 push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
77 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
80 foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
81 $price_factors{$pfac->{id}} = $pfac;
83 $pfac->{formatted_factor} = $form->format_amount($myconfig, $pfac->{factor});
86 # sort items by partsgroup
87 for my $i (1 .. $form->{rowcount}) {
89 # if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
90 # $partsgroup = $form->{"partsgroup_$i"};
92 # push @partsgroup, [$i, $partsgroup];
93 push(@project_ids, $form->{"project_id_$i"}) if ($form->{"project_id_$i"});
99 $projects = SL::DB::Manager::Project->get_all(query => [ id => \@project_ids ]);
100 %projects_by_id = map { $_->id => $_ } @$projects;
103 if ($projects_by_id{$form->{"globalproject_id"}}) {
104 $form->{globalprojectnumber} = $projects_by_id{$form->{"globalproject_id"}}->projectnumber;
105 $form->{globalprojectdescription} = $projects_by_id{$form->{"globalproject_id"}}->description;
107 for (@{ $projects_by_id{$form->{"globalproject_id"}}->cvars_by_config }) {
108 $form->{"project_cvar_" . $_->config->name} = $_->value_as_text;
118 # sort items by partsgroup
119 for $i (1 .. $form->{rowcount}) {
121 if ($form->{"partsgroup_$i"} && $form->{groupitems}) {
122 $partsgroup = $form->{"partsgroup_$i"};
124 push @partsgroup, [$i, $partsgroup];
137 my $nodiscount_subtotal = 0;
138 my $discount_subtotal = 0;
140 my $subtotal_header = 0;
143 $form->{discount} = [];
145 IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
147 my $ic_cvar_configs = CVar->get_configs(module => 'IC');
148 my $project_cvar_configs = CVar->get_configs(module => 'Projects');
151 qw(runningnumber number description longdescription qty ship unit bin
152 deliverydate_oe ordnumber_oe donumber_do transdate_oe validuntil
153 partnotes serialnumber reqdate sellprice listprice netprice
154 discount p_discount discount_sub nodiscount_sub
155 linetotal nodiscount_linetotal tax_rate projectnumber projectdescription
156 price_factor price_factor_name partsgroup weight lineweight);
158 push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
159 push @arrays, map { "project_cvar_$_->{name}" } @{ $project_cvar_configs };
161 my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber);
163 my @payment_arrays = qw(payment paymentaccount paymentdate paymentsource paymentmemo);
165 map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays, @payment_arrays);
168 foreach $item (sort { $a->[1] cmp $b->[1] } @partsgroup) {
171 if ($item->[1] ne $sameitem) {
172 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|);
173 $sameitem = $item->[1];
175 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
178 $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
180 if ($form->{"id_$i"} != 0) {
182 # add number, description and qty to $form->{number},
183 if ($form->{"subtotal_$i"} && !$subtotal_header) {
184 $subtotal_header = $i;
185 $position = int($position);
188 } elsif ($subtotal_header) {
190 $position = int($position);
191 $position = $position.".".$subposition;
193 $position = int($position);
197 my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
199 push @{ $form->{TEMPLATE_ARRAYS}->{runningnumber} }, $position;
200 push @{ $form->{TEMPLATE_ARRAYS}->{number} }, $form->{"partnumber_$i"};
201 push @{ $form->{TEMPLATE_ARRAYS}->{serialnumber} }, $form->{"serialnumber_$i"};
202 push @{ $form->{TEMPLATE_ARRAYS}->{bin} }, $form->{"bin_$i"};
203 push @{ $form->{TEMPLATE_ARRAYS}->{partnotes} }, $form->{"partnotes_$i"};
204 push @{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->{"description_$i"};
205 push @{ $form->{TEMPLATE_ARRAYS}->{longdescription} }, $form->{"longdescription_$i"};
206 push @{ $form->{TEMPLATE_ARRAYS}->{qty} }, $form->format_amount($myconfig, $form->{"qty_$i"});
207 push @{ $form->{TEMPLATE_ARRAYS}->{qty_nofmt} }, $form->{"qty_$i"};
208 push @{ $form->{TEMPLATE_ARRAYS}->{unit} }, $form->{"unit_$i"};
209 push @{ $form->{TEMPLATE_ARRAYS}->{deliverydate_oe} }, $form->{"reqdate_$i"};
210 push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} }, $form->{"sellprice_$i"};
211 push @{ $form->{TEMPLATE_ARRAYS}->{sellprice_nofmt} }, $form->parse_amount($myconfig, $form->{"sellprice_$i"});
212 push @{ $form->{TEMPLATE_ARRAYS}->{ordnumber_oe} }, $form->{"ordnumber_$i"};
213 push @{ $form->{TEMPLATE_ARRAYS}->{donumber_do} }, $form->{"donumber_$i"};
214 push @{ $form->{TEMPLATE_ARRAYS}->{transdate_oe} }, $form->{"transdate_$i"};
215 push @{ $form->{TEMPLATE_ARRAYS}->{invnumber} }, $form->{"invnumber"};
216 push @{ $form->{TEMPLATE_ARRAYS}->{invdate} }, $form->{"invdate"};
217 push @{ $form->{TEMPLATE_ARRAYS}->{price_factor} }, $price_factor->{formatted_factor};
218 push @{ $form->{TEMPLATE_ARRAYS}->{price_factor_name} }, $price_factor->{description};
219 push @{ $form->{TEMPLATE_ARRAYS}->{partsgroup} }, $form->{"partsgroup_$i"};
220 push @{ $form->{TEMPLATE_ARRAYS}->{reqdate} }, $form->{"reqdate_$i"};
221 push(@{ $form->{TEMPLATE_ARRAYS}->{listprice} }, $form->{"listprice_$i"});
223 my $sellprice = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
224 my ($dec) = ($sellprice =~ /\.(\d+)/);
225 my $decimalplaces = max 2, length($dec);
227 my $parsed_discount = $form->parse_amount($myconfig, $form->{"discount_$i"});
229 my $linetotal_exact = $form->{"qty_$i"} * $sellprice * (100 - $parsed_discount) / 100 / $price_factor->{factor};
230 my $linetotal = $form->round_amount($linetotal_exact, 2);
232 my $nodiscount_exact_linetotal = $form->{"qty_$i"} * $sellprice / $price_factor->{factor};
233 my $nodiscount_linetotal = $form->round_amount($nodiscount_exact_linetotal,2);
235 my $discount = $nodiscount_linetotal - $linetotal; # is always rounded because $nodiscount_linetotal and $linetotal are rounded
237 my $discount_round_error = $discount + ($linetotal_exact - $nodiscount_exact_linetotal); # not used
239 $form->{"netprice_$i"} = $form->round_amount($form->{"qty_$i"} ? ($linetotal / $form->{"qty_$i"}) : 0, 2);
241 push @{ $form->{TEMPLATE_ARRAYS}->{netprice} }, ($form->{"netprice_$i"} != 0) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : '';
242 push @{ $form->{TEMPLATE_ARRAYS}->{netprice_nofmt} }, ($form->{"netprice_$i"} != 0) ? $form->{"netprice_$i"} : '';
244 $linetotal = ($linetotal != 0) ? $linetotal : '';
246 push @{ $form->{TEMPLATE_ARRAYS}->{discount} }, ($discount != 0) ? $form->format_amount($myconfig, $discount * -1, 2) : '';
247 push @{ $form->{TEMPLATE_ARRAYS}->{discount_nofmt} }, ($discount != 0) ? $discount * -1 : '';
248 push @{ $form->{TEMPLATE_ARRAYS}->{p_discount} }, $form->{"discount_$i"};
250 $form->{total} += $linetotal;
251 $form->{nodiscount_total} += $nodiscount_linetotal;
252 $form->{discount_total} += $discount;
254 if ($subtotal_header) {
255 $discount_subtotal += $linetotal;
256 $nodiscount_subtotal += $nodiscount_linetotal;
259 if ($form->{"subtotal_$i"} && $subtotal_header && ($subtotal_header != $i)) {
260 push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub} }, $form->format_amount($myconfig, $discount_subtotal, 2);
261 push @{ $form->{TEMPLATE_ARRAYS}->{discount_sub_nofmt} }, $discount_subtotal;
262 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub} }, $form->format_amount($myconfig, $nodiscount_subtotal, 2);
263 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_sub_nofmt} }, $nodiscount_subtotal;
265 $discount_subtotal = 0;
266 $nodiscount_subtotal = 0;
267 $subtotal_header = 0;
270 push @{ $form->{TEMPLATE_ARRAYS}->{$_} }, "" for qw(discount_sub nodiscount_sub discount_sub_nofmt nodiscount_sub_nofmt);
273 if (!$form->{"discount_$i"}) {
274 $nodiscount += $linetotal;
277 push @{ $form->{TEMPLATE_ARRAYS}->{linetotal} }, $form->format_amount($myconfig, $linetotal, 2);
278 push @{ $form->{TEMPLATE_ARRAYS}->{linetotal_nofmt} }, $linetotal_exact;
279 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal} }, $form->format_amount($myconfig, $nodiscount_linetotal, 2);
280 push @{ $form->{TEMPLATE_ARRAYS}->{nodiscount_linetotal_nofmt} }, $nodiscount_linetotal;
282 my $project = $projects_by_id{$form->{"project_id_$i"}} || SL::DB::Project->new;
284 push @{ $form->{TEMPLATE_ARRAYS}->{projectnumber} }, $project->projectnumber;
285 push @{ $form->{TEMPLATE_ARRAYS}->{projectdescription} }, $project->description;
287 my $lineweight = $form->{"qty_$i"} * $form->{"weight_$i"};
288 $totalweight += $lineweight;
289 push @{ $form->{TEMPLATE_ARRAYS}->{weight} }, $form->format_amount($myconfig, $form->{"weight_$i"}, 3);
290 push @{ $form->{TEMPLATE_ARRAYS}->{weight_nofmt} }, $form->{"weight_$i"};
291 push @{ $form->{TEMPLATE_ARRAYS}->{lineweight} }, $form->format_amount($myconfig, $lineweight, 3);
292 push @{ $form->{TEMPLATE_ARRAYS}->{lineweight_nofmt} }, $lineweight;
294 @taxaccounts = split(/ /, $form->{"taxaccounts_$i"});
298 map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
300 if ($form->{taxincluded}) {
303 $taxamount = $linetotal * $taxrate / (1 + $taxrate);
304 $taxbase = $linetotal - $taxamount;
306 $taxamount = $linetotal * $taxrate;
307 $taxbase = $linetotal;
310 if ($form->round_amount($taxrate, 7) == 0) {
311 if ($form->{taxincluded}) {
312 foreach my $accno (@taxaccounts) {
313 $taxamount = $form->round_amount($linetotal * $form->{"${accno}_rate"} / (1 + abs($form->{"${accno}_rate"})), 2);
315 $taxaccounts{$accno} += $taxamount;
316 $taxdiff += $taxamount;
318 $taxbase{$accno} += $taxbase;
320 $taxaccounts{ $taxaccounts[0] } += $taxdiff;
322 foreach my $accno (@taxaccounts) {
323 $taxaccounts{$accno} += $linetotal * $form->{"${accno}_rate"};
324 $taxbase{$accno} += $taxbase;
328 foreach my $accno (@taxaccounts) {
329 $taxaccounts{$accno} += $taxamount * $form->{"${accno}_rate"} / $taxrate;
330 $taxbase{$accno} += $taxbase;
333 my $tax_rate = $taxrate * 100;
334 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_rate} }, qq|$tax_rate|);
335 if ($form->{"assembly_$i"}) {
338 # get parts and push them onto the stack
340 if ($form->{groupitems}) {
342 qq|ORDER BY pg.partsgroup, a.oid|;
344 $sortorder = qq|ORDER BY a.oid|;
348 qq|SELECT p.partnumber, p.description, p.unit, a.qty, pg.partsgroup
350 JOIN parts p ON (a.parts_id = p.id)
351 LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
352 WHERE (a.bom = '1') AND (a.id = ?) $sortorder|;
353 $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{"id_$i"}));
355 while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
356 if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
357 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
358 $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
359 push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
362 map { $form->{"a_$_"} = $ref->{$_} } qw(partnumber description);
364 push(@{ $form->{TEMPLATE_ARRAYS}->{description} },
365 $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}
367 . qq| -- $form->{"a_partnumber"}, $form->{"a_description"}|);
368 map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
374 push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} },
375 CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_)
376 for @{ $ic_cvar_configs };
378 push @{ $form->{TEMPLATE_ARRAYS}->{"project_cvar_" . $_->config->name} }, $_->value_as_text for @{ $project->cvars_by_config };
382 $form->{totalweight} = $form->format_amount($myconfig, $totalweight, 3);
383 $form->{totalweight_nofmt} = $totalweight;
384 my $defaults = AM->get_defaults();
385 $form->{weightunit} = $defaults->{weightunit};
387 foreach my $item (sort keys %taxaccounts) {
388 $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
390 push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase} }, $form->format_amount($myconfig, $taxbase{$item}, 2));
391 push(@{ $form->{TEMPLATE_ARRAYS}->{taxbase_nofmt} }, $taxbase{$item});
392 push(@{ $form->{TEMPLATE_ARRAYS}->{tax} }, $form->format_amount($myconfig, $taxamount, 2));
393 push(@{ $form->{TEMPLATE_ARRAYS}->{tax_nofmt} }, $taxamount );
394 push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} }, $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
395 push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate_nofmt} }, $form->{"${item}_rate"} * 100);
396 push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} }, $form->{"${item}_taxnumber"});
398 my $tax_obj = SL::DB::Manager::Tax->find_by(taxnumber => $form->{"${item}_taxnumber"});
399 my $description = $tax_obj->translated_attribute('taxdescription', $form->{language_id}, 0) if $tax_obj;
400 push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $description . q{ } . 100 * $form->{"${item}_rate"} . q{%});
403 for my $i (1 .. $form->{paidaccounts}) {
404 if ($form->{"paid_$i"}) {
405 my ($accno, $description) = split(/--/, $form->{"AR_paid_$i"});
407 push(@{ $form->{TEMPLATE_ARRAYS}->{payment} }, $form->{"paid_$i"});
408 push(@{ $form->{TEMPLATE_ARRAYS}->{paymentaccount} }, $description);
409 push(@{ $form->{TEMPLATE_ARRAYS}->{paymentdate} }, $form->{"datepaid_$i"});
410 push(@{ $form->{TEMPLATE_ARRAYS}->{paymentsource} }, $form->{"source_$i"});
411 push(@{ $form->{TEMPLATE_ARRAYS}->{paymentmemo} }, $form->{"memo_$i"});
413 $form->{paid} += $form->parse_amount($myconfig, $form->{"paid_$i"});
416 if($form->{taxincluded}) {
417 $form->{subtotal} = $form->format_amount($myconfig, $form->{total} - $tax, 2);
418 $form->{subtotal_nofmt} = $form->{total} - $tax;
421 $form->{subtotal} = $form->format_amount($myconfig, $form->{total}, 2);
422 $form->{subtotal_nofmt} = $form->{total};
425 $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
426 $form->{discount_total} = $form->format_amount($myconfig, $form->{discount_total}, 2);
427 $form->{nodiscount} = $form->format_amount($myconfig, $nodiscount, 2);
428 $form->{yesdiscount} = $form->format_amount($myconfig, $form->{nodiscount_total} - $nodiscount, 2);
430 $form->{invtotal} = ($form->{taxincluded}) ? $form->{total} : $form->{total} + $tax;
431 $form->{total} = $form->format_amount($myconfig, $form->{invtotal} - $form->{paid}, 2);
433 $form->{invtotal} = $form->format_amount($myconfig, $form->{invtotal}, 2);
434 $form->{paid} = $form->format_amount($myconfig, $form->{paid}, 2);
436 $form->set_payment_options($myconfig, $form->{invdate});
438 $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
439 $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
441 $form->{username} = $myconfig->{name};
443 $main::lxdebug->leave_sub();
446 sub project_description {
447 $main::lxdebug->enter_sub();
449 my ($self, $dbh, $id) = @_;
450 my $form = \%main::form;
452 my $query = qq|SELECT description FROM project WHERE id = ?|;
453 my ($description) = selectrow_query($form, $dbh, $query, conv_i($id));
455 $main::lxdebug->leave_sub();
460 sub customer_details {
461 $main::lxdebug->enter_sub();
463 my ($self, $myconfig, $form, @wanted_vars) = @_;
465 # connect to database
466 my $dbh = $form->get_standard_dbh;
468 my $language_id = $form->{language_id};
470 # get contact id, set it if nessessary
473 my @values = (conv_i($form->{customer_id}));
476 if ($form->{cp_id}) {
477 $where = qq| AND (cp.cp_id = ?) |;
478 push(@values, conv_i($form->{cp_id}));
481 # get rest for the customer
483 qq|SELECT ct.*, cp.*, ct.notes as customernotes,
484 ct.phone AS customerphone, ct.fax AS customerfax, ct.email AS customeremail,
487 LEFT JOIN contacts cp on ct.id = cp.cp_cv_id
488 LEFT JOIN currencies cu ON (ct.currency_id = cu.id)
489 WHERE (ct.id = ?) $where
492 my $ref = selectfirst_hashref_query($form, $dbh, $query, @values);
494 # remove id and taxincluded before copy back
495 delete @$ref{qw(id taxincluded)};
497 @wanted_vars = grep({ $_ } @wanted_vars);
498 if (scalar(@wanted_vars) > 0) {
500 map({ $h_wanted_vars{$_} = 1; } @wanted_vars);
501 map({ delete($ref->{$_}) unless ($h_wanted_vars{$_}); } keys(%{$ref}));
504 map { $form->{$_} = $ref->{$_} } keys %$ref;
506 if ($form->{delivery_customer_id}) {
508 qq|SELECT *, notes as customernotes
512 $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{delivery_customer_id}));
514 map { $form->{"dc_$_"} = $ref->{$_} } keys %$ref;
517 if ($form->{delivery_vendor_id}) {
519 qq|SELECT *, notes as customernotes
523 $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{delivery_vendor_id}));
525 map { $form->{"dv_$_"} = $ref->{$_} } keys %$ref;
528 my $custom_variables = CVar->get_custom_variables('dbh' => $dbh,
530 'trans_id' => $form->{customer_id});
531 map { $form->{"vc_cvar_$_->{name}"} = $_->{value} } @{ $custom_variables };
533 $form->{cp_greeting} = GenericTranslations->get('dbh' => $dbh,
534 'translation_type' => 'greetings::' . ($form->{cp_gender} eq 'f' ? 'female' : 'male'),
535 'language_id' => $language_id,
536 'allow_fallback' => 1);
539 $main::lxdebug->leave_sub();
543 $main::lxdebug->enter_sub();
545 my ($self, $myconfig, $form, $provided_dbh, $payments_only) = @_;
547 # connect to database, turn off autocommit
548 my $dbh = $provided_dbh ? $provided_dbh : $form->get_standard_dbh;
550 my ($query, $sth, $null, $project_id, @values);
551 my $exchangerate = 0;
553 my $ic_cvar_configs = CVar->get_configs(module => 'IC',
556 if (!$form->{employee_id}) {
557 $form->get_employee($dbh);
560 $form->{defaultcurrency} = $form->get_default_currency($myconfig);
561 my $defaultcurrency = $form->{defaultcurrency};
563 # Seit neuestem wird die department_id schon Ă¼bergeben UND $form->department nicht mehr
564 # korrekt zusammengebaut. Sehr wahrscheinlich beim Umstieg auf T8 kaputt gegangen
565 # Ich lass den Code von 2005 erstmal noch stehen ;-) jb 03-2011
566 if (!$form->{department_id}){
567 ($null, $form->{department_id}) = split(/--/, $form->{department});
570 my $all_units = AM->retrieve_units($myconfig, $form);
572 if (!$payments_only) {
574 &reverse_invoice($dbh, $form);
577 my $trans_number = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{invnumber}, save => 1);
578 $form->{invnumber} = $trans_number->create_unique unless $trans_number->is_unique;
580 $query = qq|SELECT nextval('glid')|;
581 ($form->{"id"}) = selectrow_query($form, $dbh, $query);
583 $query = qq|INSERT INTO ar (id, invnumber, currency_id) VALUES (?, ?, (SELECT id FROM currencies WHERE name=?))|;
584 do_query($form, $dbh, $query, $form->{"id"}, $form->{"id"}, $form->{currency});
586 if (!$form->{invnumber}) {
587 my $trans_number = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{invnumber}, id => $form->{id});
588 $form->{invnumber} = $trans_number->create_unique;
593 my ($netamount, $invoicediff) = (0, 0);
594 my ($amount, $linetotal, $lastincomeaccno);
596 if ($form->{currency} eq $defaultcurrency) {
597 $form->{exchangerate} = 1;
599 $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{invdate}, 'buy');
602 $form->{exchangerate} =
605 : $form->parse_amount($myconfig, $form->{exchangerate});
607 $form->{expense_inventory} = "";
611 $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
612 my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
615 $form->{amount} = {};
616 $form->{amount_cogs} = {};
618 foreach my $i (1 .. $form->{rowcount}) {
619 if ($form->{type} eq "credit_note") {
620 $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"}) * -1;
621 $form->{shipped} = 1;
623 $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
628 $form->{"marge_percent_$i"} = $form->parse_amount($myconfig, $form->{"marge_percent_$i"}) * 1;
629 $form->{"marge_absolut_$i"} = $form->parse_amount($myconfig, $form->{"marge_absolut_$i"}) * 1;
630 $form->{"lastcost_$i"} = $form->parse_amount($myconfig, $form->{"lastcost_$i"}) * 1;
632 if ($form->{storno}) {
633 $form->{"qty_$i"} *= -1;
636 if ($form->{"id_$i"}) {
639 if (defined($baseunits{$form->{"id_$i"}})) {
640 $item_unit = $baseunits{$form->{"id_$i"}};
643 $query = qq|SELECT unit FROM parts WHERE id = ?|;
644 ($item_unit) = selectrow_query($form, $dbh, $query, conv_i($form->{"id_$i"}));
645 $baseunits{$form->{"id_$i"}} = $item_unit;
648 if (defined($all_units->{$item_unit}->{factor})
649 && ($all_units->{$item_unit}->{factor} ne '')
650 && ($all_units->{$item_unit}->{factor} != 0)) {
651 $basefactor = $all_units->{$form->{"unit_$i"}}->{factor} / $all_units->{$item_unit}->{factor};
655 $baseqty = $form->{"qty_$i"} * $basefactor;
657 my ($allocated, $taxrate) = (0, 0);
661 map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
663 # keep entered selling price
665 $form->parse_amount($myconfig, $form->{"sellprice_$i"});
667 my ($dec) = ($fxsellprice =~ /\.(\d+)/);
669 my $decimalplaces = ($dec > 2) ? $dec : 2;
671 # undo discount formatting
672 $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
675 $form->{"sellprice_$i"} = $fxsellprice * (1 - $form->{"discount_$i"});
677 # round linetotal to 2 decimal places
678 $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
679 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2);
681 if ($form->{taxincluded}) {
682 $taxamount = $linetotal * ($taxrate / (1 + $taxrate));
683 $form->{"sellprice_$i"} =
684 $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
686 $taxamount = $linetotal * $taxrate;
689 $netamount += $linetotal;
691 if ($taxamount != 0) {
693 $form->{amount}{ $form->{id} }{$_} +=
694 $taxamount * $form->{"${_}_rate"} / $taxrate
695 } split(/ /, $form->{"taxaccounts_$i"});
698 # add amount to income, $form->{amount}{trans_id}{accno}
699 $amount = $form->{"sellprice_$i"} * $form->{"qty_$i"} * $form->{exchangerate} / $price_factor;
701 $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"} / $price_factor, 2) * $form->{exchangerate};
702 $linetotal = $form->round_amount($linetotal, 2);
704 # this is the difference from the inventory
705 $invoicediff += ($amount - $linetotal);
707 $form->{amount}{ $form->{id} }{ $form->{"income_accno_$i"} } +=
710 $lastincomeaccno = $form->{"income_accno_$i"};
712 # adjust and round sellprice
713 $form->{"sellprice_$i"} =
714 $form->round_amount($form->{"sellprice_$i"} * $form->{exchangerate},
717 next if $payments_only;
719 if ($form->{"inventory_accno_$i"} || $form->{"assembly_$i"}) {
721 if ($form->{"assembly_$i"}) {
722 # record assembly item as allocated
723 &process_assembly($dbh, $myconfig, $form, $form->{"id_$i"}, $baseqty);
726 $allocated = &cogs($dbh, $myconfig, $form, $form->{"id_$i"}, $baseqty, $basefactor, $i);
730 # Get pricegroup_id and save it. Unfortunately the interface
731 # also uses ID "0" for signalling that none is selected, but "0"
732 # must not be stored in the database. Therefore we cannot simply
734 ($null, my $pricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
736 $pricegroup_id = undef if !$pricegroup_id;
738 my ($invoice_id) = selectfirst_array_query($form, $dbh, qq|SELECT nextval('invoiceid')|);
740 # save detail record in invoice table
742 qq|INSERT INTO invoice (id, trans_id, parts_id, description, longdescription, qty,
743 sellprice, fxsellprice, discount, allocated, assemblyitem,
744 unit, deliverydate, project_id, serialnumber, pricegroup_id,
745 ordnumber, donumber, transdate, cusordnumber, base_qty, subtotal,
746 marge_percent, marge_total, lastcost,
747 price_factor_id, price_factor, marge_price_factor)
748 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
749 (SELECT factor FROM price_factors WHERE id = ?), ?)|;
751 @values = ($invoice_id, conv_i($form->{id}), conv_i($form->{"id_$i"}),
752 $form->{"description_$i"}, $form->{"longdescription_$i"}, $form->{"qty_$i"},
753 $form->{"sellprice_$i"}, $fxsellprice,
754 $form->{"discount_$i"}, $allocated, 'f',
755 $form->{"unit_$i"}, conv_date($form->{"reqdate_$i"}), conv_i($form->{"project_id_$i"}),
756 $form->{"serialnumber_$i"}, $pricegroup_id,
757 $form->{"ordnumber_$i"}, $form->{"donumber_$i"}, conv_date($form->{"transdate_$i"}),
758 $form->{"cusordnumber_$i"}, $baseqty, $form->{"subtotal_$i"} ? 't' : 'f',
759 $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
760 $form->{"lastcost_$i"},
761 conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
762 conv_i($form->{"marge_price_factor_$i"}));
763 do_query($form, $dbh, $query, @values);
765 CVar->save_custom_variables(module => 'IC',
766 sub_module => 'invoice',
767 trans_id => $invoice_id,
768 configs => $ic_cvar_configs,
770 name_prefix => 'ic_',
771 name_postfix => "_$i",
776 # total payments, don't move we need it here
777 for my $i (1 .. $form->{paidaccounts}) {
778 if ($form->{type} eq "credit_note") {
779 $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"}) * -1;
781 $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
783 $form->{paid} += $form->{"paid_$i"};
784 $form->{datepaid} = $form->{"datepaid_$i"} if ($form->{"datepaid_$i"});
787 my ($tax, $diff) = (0, 0);
789 $netamount = $form->round_amount($netamount, 2);
791 # figure out rounding errors for total amount vs netamount + taxes
792 if ($form->{taxincluded}) {
794 $amount = $form->round_amount($netamount * $form->{exchangerate}, 2);
795 $diff += $amount - $netamount * $form->{exchangerate};
796 $netamount = $amount;
798 foreach my $item (split(/ /, $form->{taxaccounts})) {
799 $amount = $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate};
800 $form->{amount}{ $form->{id} }{$item} = $form->round_amount($amount, 2);
801 $tax += $form->{amount}{ $form->{id} }{$item};
802 $netamount -= $form->{amount}{ $form->{id} }{$item};
805 $invoicediff += $diff;
806 ######## this only applies to tax included
807 if ($lastincomeaccno) {
808 $form->{amount}{ $form->{id} }{$lastincomeaccno} += $invoicediff;
812 $amount = $form->round_amount($netamount * $form->{exchangerate}, 2);
813 $diff = $amount - $netamount * $form->{exchangerate};
814 $netamount = $amount;
815 foreach my $item (split(/ /, $form->{taxaccounts})) {
816 $form->{amount}{ $form->{id} }{$item} =
817 $form->round_amount($form->{amount}{ $form->{id} }{$item}, 2);
820 $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate},
823 $amount - $form->{amount}{ $form->{id} }{$item} *
824 $form->{exchangerate};
825 $form->{amount}{ $form->{id} }{$item} = $form->round_amount($amount, 2);
826 $tax += $form->{amount}{ $form->{id} }{$item};
830 $form->{amount}{ $form->{id} }{ $form->{AR} } = $netamount + $tax;
832 $form->round_amount($form->{paid} * $form->{exchangerate} + $diff, 2);
835 $form->{amount}{ $form->{id} }{ $form->{AR} } *= -1;
837 # update exchangerate
838 if (($form->{currency} ne $defaultcurrency) && !$exchangerate) {
839 $form->update_exchangerate($dbh, $form->{currency}, $form->{invdate},
840 $form->{exchangerate}, 0);
843 $project_id = conv_i($form->{"globalproject_id"});
844 # entsprechend auch beim Bestimmen des SteuerschlĂ¼ssels in Taxkey.pm berĂ¼cksichtigen
845 my $taxdate = $form->{deliverydate} ? $form->{deliverydate} : $form->{invdate};
847 foreach my $trans_id (keys %{ $form->{amount_cogs} }) {
848 foreach my $accno (keys %{ $form->{amount_cogs}{$trans_id} }) {
849 next unless ($form->{expense_inventory} =~ /\Q$accno\E/);
851 $form->{amount_cogs}{$trans_id}{$accno} = $form->round_amount($form->{amount_cogs}{$trans_id}{$accno}, 2);
853 if (!$payments_only && ($form->{amount_cogs}{$trans_id}{$accno} != 0)) {
855 qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
856 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, (SELECT id FROM tax WHERE taxkey=0), 0, ?, (SELECT link FROM chart WHERE accno = ?))|;
857 @values = (conv_i($trans_id), $accno, $form->{amount_cogs}{$trans_id}{$accno}, conv_date($form->{invdate}), conv_i($project_id), $accno);
858 do_query($form, $dbh, $query, @values);
859 $form->{amount_cogs}{$trans_id}{$accno} = 0;
863 foreach my $accno (keys %{ $form->{amount_cogs}{$trans_id} }) {
864 $form->{amount_cogs}{$trans_id}{$accno} = $form->round_amount($form->{amount_cogs}{$trans_id}{$accno}, 2);
866 if (!$payments_only && ($form->{amount_cogs}{$trans_id}{$accno} != 0)) {
868 qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
869 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, (SELECT id FROM tax WHERE taxkey=0), 0, ?, (SELECT link FROM chart WHERE accno = ?))|;
870 @values = (conv_i($trans_id), $accno, $form->{amount_cogs}{$trans_id}{$accno}, conv_date($form->{invdate}), conv_i($project_id), $accno);
871 do_query($form, $dbh, $query, @values);
876 foreach my $trans_id (keys %{ $form->{amount} }) {
877 foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
878 next unless ($form->{expense_inventory} =~ /\Q$accno\E/);
880 $form->{amount}{$trans_id}{$accno} = $form->round_amount($form->{amount}{$trans_id}{$accno}, 2);
882 if (!$payments_only && ($form->{amount}{$trans_id}{$accno} != 0)) {
884 qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
885 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
888 WHERE chart_id= (SELECT id
892 ORDER BY startdate DESC LIMIT 1),
895 WHERE chart_id= (SELECT id
899 ORDER BY startdate DESC LIMIT 1),
901 (SELECT link FROM chart WHERE accno = ?))|;
902 @values = (conv_i($trans_id), $accno, $form->{amount}{$trans_id}{$accno}, conv_date($form->{invdate}), $accno, conv_date($taxdate), $accno, conv_date($taxdate), conv_i($project_id), $accno);
903 do_query($form, $dbh, $query, @values);
904 $form->{amount}{$trans_id}{$accno} = 0;
908 foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
909 $form->{amount}{$trans_id}{$accno} = $form->round_amount($form->{amount}{$trans_id}{$accno}, 2);
911 if (!$payments_only && ($form->{amount}{$trans_id}{$accno} != 0)) {
913 qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
914 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
917 WHERE chart_id= (SELECT id
921 ORDER BY startdate DESC LIMIT 1),
924 WHERE chart_id= (SELECT id
928 ORDER BY startdate DESC LIMIT 1),
930 (SELECT link FROM chart WHERE accno = ?))|;
931 @values = (conv_i($trans_id), $accno, $form->{amount}{$trans_id}{$accno}, conv_date($form->{invdate}), $accno, conv_date($taxdate), $accno, conv_date($taxdate), conv_i($project_id), $accno);
932 do_query($form, $dbh, $query, @values);
937 # deduct payment differences from diff
938 for my $i (1 .. $form->{paidaccounts}) {
939 if ($form->{"paid_$i"} != 0) {
941 $form->round_amount($form->{"paid_$i"} * $form->{exchangerate}, 2);
942 $diff -= $amount - $form->{"paid_$i"} * $form->{exchangerate};
946 # record payments and offsetting AR
947 if (!$form->{storno}) {
948 for my $i (1 .. $form->{paidaccounts}) {
950 if ($form->{"acc_trans_id_$i"}
952 && (SL::DB::Default->get->payments_changeable == 0)) {
956 next if ($form->{"paid_$i"} == 0);
958 my ($accno) = split(/--/, $form->{"AR_paid_$i"});
959 $form->{"datepaid_$i"} = $form->{invdate}
960 unless ($form->{"datepaid_$i"});
961 $form->{datepaid} = $form->{"datepaid_$i"};
965 if ($form->{currency} eq $defaultcurrency) {
966 $form->{"exchangerate_$i"} = 1;
968 $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{"datepaid_$i"}, 'buy');
969 $form->{"exchangerate_$i"} = $exchangerate || $form->parse_amount($myconfig, $form->{"exchangerate_$i"});
973 $amount = $form->round_amount($form->{"paid_$i"} * $form->{exchangerate} + $diff, 2);
975 if ($form->{amount}{ $form->{id} }{ $form->{AR} } != 0) {
977 qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link)
978 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
981 WHERE chart_id= (SELECT id
985 ORDER BY startdate DESC LIMIT 1),
988 WHERE chart_id= (SELECT id
992 ORDER BY startdate DESC LIMIT 1),
994 (SELECT link FROM chart WHERE accno = ?))|;
995 @values = (conv_i($form->{"id"}), $form->{AR}, $amount, $form->{"datepaid_$i"}, $form->{AR}, conv_date($taxdate), $form->{AR}, conv_date($taxdate), $project_id, $form->{AR});
996 do_query($form, $dbh, $query, @values);
1000 $form->{"paid_$i"} *= -1;
1001 my $gldate = (conv_date($form->{"gldate_$i"}))? conv_date($form->{"gldate_$i"}) : conv_date($form->current_date($myconfig));
1004 qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, gldate, source, memo, tax_id, taxkey, project_id, chart_link)
1005 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, ?, ?, ?,
1008 WHERE chart_id= (SELECT id
1012 ORDER BY startdate DESC LIMIT 1),
1015 WHERE chart_id= (SELECT id
1019 ORDER BY startdate DESC LIMIT 1),
1021 (SELECT link FROM chart WHERE accno = ?))|;
1022 @values = (conv_i($form->{"id"}), $accno, $form->{"paid_$i"}, $form->{"datepaid_$i"},
1023 $gldate, $form->{"source_$i"}, $form->{"memo_$i"}, $accno, conv_date($taxdate), $accno, conv_date($taxdate), $project_id, $accno);
1024 do_query($form, $dbh, $query, @values);
1026 # exchangerate difference
1027 $form->{fx}{$accno}{ $form->{"datepaid_$i"} } +=
1028 $form->{"paid_$i"} * ($form->{"exchangerate_$i"} - 1) + $diff;
1032 $form->{"paid_$i"} * $form->{exchangerate} - $form->{"paid_$i"} *
1033 $form->{"exchangerate_$i"};
1035 $form->{fx}{ $form->{fxgain_accno} }{ $form->{"datepaid_$i"} } += $amount;
1037 $form->{fx}{ $form->{fxloss_accno} }{ $form->{"datepaid_$i"} } += $amount;
1042 # update exchange rate
1043 if (($form->{currency} ne $defaultcurrency) && !$exchangerate) {
1044 $form->update_exchangerate($dbh, $form->{currency},
1045 $form->{"datepaid_$i"},
1046 $form->{"exchangerate_$i"}, 0);
1050 } else { # if (!$form->{storno})
1051 $form->{marge_total} *= -1;
1054 IO->set_datepaid(table => 'ar', id => $form->{id}, dbh => $dbh);
1056 # record exchange rate differences and gains/losses
1057 foreach my $accno (keys %{ $form->{fx} }) {
1058 foreach my $transdate (keys %{ $form->{fx}{$accno} }) {
1059 $form->{fx}{$accno}{$transdate} = $form->round_amount($form->{fx}{$accno}{$transdate}, 2);
1060 if ( $form->{fx}{$accno}{$transdate} != 0 ) {
1063 qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, cleared, fx_transaction, tax_id, taxkey, project_id, chart_link)
1064 VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, '0', '1',
1067 WHERE chart_id= (SELECT id
1071 ORDER BY startdate DESC LIMIT 1),
1074 WHERE chart_id= (SELECT id
1078 ORDER BY startdate DESC LIMIT 1),
1080 (SELECT link FROM chart WHERE accno = ?))|;
1081 @values = (conv_i($form->{"id"}), $accno, $form->{fx}{$accno}{$transdate}, conv_date($transdate), $accno, conv_date($taxdate), $accno, conv_date($taxdate), conv_i($project_id), $accno);
1082 do_query($form, $dbh, $query, @values);
1087 if ($payments_only) {
1088 $query = qq|UPDATE ar SET paid = ? WHERE id = ?|;
1089 do_query($form, $dbh, $query, $form->{paid}, conv_i($form->{id}));
1091 $dbh->commit if !$provided_dbh;
1093 $main::lxdebug->leave_sub();
1097 $amount = $netamount + $tax;
1100 #erweiterung fuer lieferscheinnummer (donumber) 12.02.09 jb
1102 $query = qq|UPDATE ar set
1103 invnumber = ?, ordnumber = ?, quonumber = ?, cusordnumber = ?,
1104 transdate = ?, orddate = ?, quodate = ?, customer_id = ?,
1105 amount = ?, netamount = ?, paid = ?,
1106 duedate = ?, deliverydate = ?, invoice = ?, shippingpoint = ?,
1107 shipvia = ?, terms = ?, notes = ?, intnotes = ?,
1108 currency_id = (SELECT id FROM currencies WHERE name = ?),
1109 department_id = ?, payment_id = ?, taxincluded = ?,
1110 type = ?, language_id = ?, taxzone_id = ?, shipto_id = ?,
1111 employee_id = ?, salesman_id = ?, storno_id = ?, storno = ?,
1112 cp_id = ?, marge_total = ?, marge_percent = ?,
1113 globalproject_id = ?, delivery_customer_id = ?,
1114 transaction_description = ?, delivery_vendor_id = ?,
1115 donumber = ?, invnumber_for_credit_note = ?, direct_debit = ?,
1116 delivery_term_id = ?
1118 @values = ( $form->{"invnumber"}, $form->{"ordnumber"}, $form->{"quonumber"}, $form->{"cusordnumber"},
1119 conv_date($form->{"invdate"}), conv_date($form->{"orddate"}), conv_date($form->{"quodate"}), conv_i($form->{"customer_id"}),
1120 $amount, $netamount, $form->{"paid"},
1121 conv_date($form->{"duedate"}), conv_date($form->{"deliverydate"}), '1', $form->{"shippingpoint"},
1122 $form->{"shipvia"}, conv_i($form->{"terms"}), $form->{"notes"}, $form->{"intnotes"},
1123 $form->{"currency"}, conv_i($form->{"department_id"}), conv_i($form->{"payment_id"}), $form->{"taxincluded"} ? 't' : 'f',
1124 $form->{"type"}, conv_i($form->{"language_id"}), conv_i($form->{"taxzone_id"}), conv_i($form->{"shipto_id"}),
1125 conv_i($form->{"employee_id"}), conv_i($form->{"salesman_id"}), conv_i($form->{storno_id}), $form->{"storno"} ? 't' : 'f',
1126 conv_i($form->{"cp_id"}), 1 * $form->{marge_total} , 1 * $form->{marge_percent},
1127 conv_i($form->{"globalproject_id"}), conv_i($form->{"delivery_customer_id"}),
1128 $form->{transaction_description}, conv_i($form->{"delivery_vendor_id"}),
1129 $form->{"donumber"}, $form->{"invnumber_for_credit_note"}, $form->{direct_debit} ? 't' : 'f',
1130 conv_i($form->{delivery_term_id}),
1131 conv_i($form->{"id"}));
1132 do_query($form, $dbh, $query, @values);
1135 if ($form->{storno}) {
1138 paid = paid + amount,
1140 intnotes = ? || intnotes
1142 do_query($form, $dbh, $query, "Rechnung storniert am $form->{invdate} ", conv_i($form->{"storno_id"}));
1143 do_query($form, $dbh, qq|UPDATE ar SET paid = amount WHERE id = ?|, conv_i($form->{"id"}));
1147 $form->{name} = $form->{customer};
1148 $form->{name} =~ s/--\Q$form->{customer_id}\E//;
1150 if (!$form->{shipto_id}) {
1151 $form->add_shipto($dbh, $form->{id}, "AR");
1154 # save printed, emailed and queued
1155 $form->save_status($dbh);
1157 Common::webdav_folder($form);
1159 # Link this record to the records it was created from.
1160 RecordLinks->create_links('dbh' => $dbh,
1162 'from_table' => 'oe',
1163 'from_ids' => $form->{convert_from_oe_ids},
1165 'to_id' => $form->{id},
1167 delete $form->{convert_from_oe_ids};
1169 my @convert_from_do_ids = map { $_ * 1 } grep { $_ } split m/\s+/, $form->{convert_from_do_ids};
1171 if (scalar @convert_from_do_ids) {
1172 DO->close_orders('dbh' => $dbh,
1173 'ids' => \@convert_from_do_ids);
1175 RecordLinks->create_links('dbh' => $dbh,
1177 'from_table' => 'delivery_orders',
1178 'from_ids' => \@convert_from_do_ids,
1180 'to_id' => $form->{id},
1183 delete $form->{convert_from_do_ids};
1185 ARAP->close_orders_if_billed('dbh' => $dbh,
1186 'arap_id' => $form->{id},
1189 # safety check datev export
1190 if ($::instance_conf->get_datev_check_on_sales_invoice) {
1191 my $transdate = $::form->{invdate} ? DateTime->from_lxoffice($::form->{invdate}) : undef;
1192 $transdate ||= DateTime->today;
1194 my $datev = SL::DATEV->new(
1195 exporttype => DATEV_ET_BUCHUNGEN,
1196 format => DATEV_FORMAT_KNE,
1200 trans_id => $form->{id},
1205 if ($datev->errors) {
1207 die join "\n", $::locale->text('DATEV check returned errors:'), $datev->errors;
1212 $dbh->commit if !$provided_dbh;
1214 $main::lxdebug->leave_sub();
1219 sub _delete_payments {
1220 $main::lxdebug->enter_sub();
1222 my ($self, $form, $dbh) = @_;
1224 my @delete_acc_trans_ids;
1226 # Delete old payment entries from acc_trans.
1228 qq|SELECT acc_trans_id
1230 WHERE (trans_id = ?) AND fx_transaction
1234 SELECT at.acc_trans_id
1236 LEFT JOIN chart c ON (at.chart_id = c.id)
1237 WHERE (trans_id = ?) AND (c.link LIKE '%AR_paid%')|;
1238 push @delete_acc_trans_ids, selectall_array_query($form, $dbh, $query, conv_i($form->{id}), conv_i($form->{id}));
1241 qq|SELECT at.acc_trans_id
1243 LEFT JOIN chart c ON (at.chart_id = c.id)
1244 WHERE (trans_id = ?)
1245 AND ((c.link = 'AR') OR (c.link LIKE '%:AR') OR (c.link LIKE 'AR:%'))
1246 ORDER BY at.acc_trans_id
1248 push @delete_acc_trans_ids, selectall_array_query($form, $dbh, $query, conv_i($form->{id}));
1250 if (@delete_acc_trans_ids) {
1251 $query = qq|DELETE FROM acc_trans WHERE acc_trans_id IN (| . join(", ", @delete_acc_trans_ids) . qq|)|;
1252 do_query($form, $dbh, $query);
1255 $main::lxdebug->leave_sub();
1259 $main::lxdebug->enter_sub();
1261 my ($self, $myconfig, $form, $locale) = @_;
1263 # connect to database, turn off autocommit
1264 my $dbh = $form->get_standard_dbh;
1266 my (%payments, $old_form, $row, $item, $query, %keep_vars);
1268 $old_form = save_form();
1270 # Delete all entries in acc_trans from prior payments.
1271 if (SL::DB::Default->get->payments_changeable != 0) {
1272 $self->_delete_payments($form, $dbh);
1275 # Save the new payments the user made before cleaning up $form.
1276 map { $payments{$_} = $form->{$_} } grep m/^datepaid_\d+$|^gldate_\d+$|^acc_trans_id_\d+$|^memo_\d+$|^source_\d+$|^exchangerate_\d+$|^paid_\d+$|^AR_paid_\d+$|^paidaccounts$/, keys %{ $form };
1278 # Clean up $form so that old content won't tamper the results.
1279 %keep_vars = map { $_, 1 } qw(login password id);
1280 map { delete $form->{$_} unless $keep_vars{$_} } keys %{ $form };
1282 # Retrieve the invoice from the database.
1283 $self->retrieve_invoice($myconfig, $form);
1285 # Set up the content of $form in the way that IS::post_invoice() expects.
1286 $form->{exchangerate} = $form->format_amount($myconfig, $form->{exchangerate});
1288 for $row (1 .. scalar @{ $form->{invoice_details} }) {
1289 $item = $form->{invoice_details}->[$row - 1];
1291 map { $item->{$_} = $form->format_amount($myconfig, $item->{$_}) } qw(qty sellprice discount);
1293 map { $form->{"${_}_${row}"} = $item->{$_} } keys %{ $item };
1296 $form->{rowcount} = scalar @{ $form->{invoice_details} };
1298 delete @{$form}{qw(invoice_details paidaccounts storno paid)};
1300 # Restore the payment options from the user input.
1301 map { $form->{$_} = $payments{$_} } keys %payments;
1303 # Get the AR accno (which is normally done by Form::create_links()).
1307 LEFT JOIN chart c ON (at.chart_id = c.id)
1308 WHERE (trans_id = ?)
1309 AND ((c.link = 'AR') OR (c.link LIKE '%:AR') OR (c.link LIKE 'AR:%'))
1310 ORDER BY at.acc_trans_id
1313 ($form->{AR}) = selectfirst_array_query($form, $dbh, $query, conv_i($form->{id}));
1315 # Post the new payments.
1316 $self->post_invoice($myconfig, $form, $dbh, 1);
1318 restore_form($old_form);
1320 my $rc = $dbh->commit();
1322 $main::lxdebug->leave_sub();
1327 sub process_assembly {
1328 $main::lxdebug->enter_sub();
1330 my ($dbh, $myconfig, $form, $id, $totalqty) = @_;
1333 qq|SELECT a.parts_id, a.qty, p.assembly, p.partnumber, p.description, p.unit,
1334 p.inventory_accno_id, p.income_accno_id, p.expense_accno_id
1336 JOIN parts p ON (a.parts_id = p.id)
1338 my $sth = prepare_execute_query($form, $dbh, $query, conv_i($id));
1340 while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
1344 $ref->{inventory_accno_id} *= 1;
1345 $ref->{expense_accno_id} *= 1;
1347 # multiply by number of assemblies
1348 $ref->{qty} *= $totalqty;
1350 if ($ref->{assembly}) {
1351 &process_assembly($dbh, $myconfig, $form, $ref->{parts_id}, $ref->{qty});
1354 if ($ref->{inventory_accno_id}) {
1355 $allocated = &cogs($dbh, $myconfig, $form, $ref->{parts_id}, $ref->{qty});
1359 # save detail record for individual assembly item in invoice table
1361 qq|INSERT INTO invoice (trans_id, description, parts_id, qty, sellprice, fxsellprice, allocated, assemblyitem, unit)
1362 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)|;
1363 my @values = (conv_i($form->{id}), $ref->{description}, conv_i($ref->{parts_id}), $ref->{qty}, 0, 0, $allocated, 't', $ref->{unit});
1364 do_query($form, $dbh, $query, @values);
1370 $main::lxdebug->leave_sub();
1374 $main::lxdebug->enter_sub();
1376 # adjust allocated in table invoice according to FIFO princicple
1377 # for a certain part with part_id $id
1379 my ($dbh, $myconfig, $form, $id, $totalqty, $basefactor, $row) = @_;
1383 $form->{taxzone_id} *=1;
1384 my $transdate = $form->{invdate} ? $dbh->quote($form->{invdate}) : "current_date";
1385 my $taxzone_id = $form->{"taxzone_id"} * 1;
1387 qq|SELECT i.id, i.trans_id, i.base_qty, i.allocated, i.sellprice, i.price_factor,
1388 c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from AS inventory_valid,
1389 c2.accno AS income_accno, c2.new_chart_id AS income_new_chart, date($transdate) - c2.valid_from AS income_valid,
1390 c3.accno AS expense_accno, c3.new_chart_id AS expense_new_chart, date($transdate) - c3.valid_from AS expense_valid
1391 FROM invoice i, parts p
1392 LEFT JOIN chart c1 ON ((SELECT inventory_accno_id FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c1.id)
1393 LEFT JOIN chart c2 ON ((SELECT income_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c2.id)
1394 LEFT JOIN chart c3 ON ((select expense_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c3.id)
1395 WHERE (i.parts_id = p.id)
1396 AND (i.parts_id = ?)
1397 AND ((i.base_qty + i.allocated) < 0)
1399 my $sth = prepare_execute_query($form, $dbh, $query, conv_i($id));
1404 # all invoice entries of an example part:
1406 # id | trans_id | base_qty | allocated | sellprice | inventory_accno | income_accno | expense_accno
1407 # ---+----------+----------+-----------+-----------+-----------------+--------------+---------------
1408 # 4 | 4 | -5 | 5 | 20.00000 | 1140 | 4400 | 5400 bought 5 for 20
1409 # 5 | 5 | 4 | -4 | 50.00000 | 1140 | 4400 | 5400 sold 4 for 50
1410 # 6 | 6 | 1 | -1 | 50.00000 | 1140 | 4400 | 5400 sold 1 for 50
1411 # 7 | 7 | -5 | 1 | 20.00000 | 1140 | 4400 | 5400 bought 5 for 20
1412 # 8 | 8 | 1 | -1 | 50.00000 | 1140 | 4400 | 5400 sold 1 for 50
1414 # AND ((i.base_qty + i.allocated) < 0) filters out all but line with id=7, elsewhere i.base_qty + i.allocated has already reached 0
1415 # and all parts have been allocated
1417 # so transaction 8 only sees transaction 7 with unallocated parts and adjusts allocated for that transaction, before allocated was 0
1418 # 7 | 7 | -5 | 1 | 20.00000 | 1140 | 4400 | 5400 bought 5 for 20
1420 # in this example there are still 4 unsold articles
1423 # search all invoice entries for the part in question, adjusting "allocated"
1424 # until the total number of sold parts has been reached
1426 # ORDER BY trans_id ensures FIFO
1429 while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
1430 if (($qty = (($ref->{base_qty} * -1) - $ref->{allocated})) > $totalqty) {
1434 # update allocated in invoice
1435 $form->update_balance($dbh, "invoice", "allocated", qq|id = $ref->{id}|, $qty);
1437 # total expenses and inventory
1438 # sellprice is the cost of the item
1439 my $linetotal = $form->round_amount(($ref->{sellprice} * $qty) / ( ($ref->{price_factor} || 1) * ( $basefactor || 1 )), 2);
1441 if ( $::instance_conf->get_inventory_system eq 'perpetual' ) {
1442 # Bestandsmethode: when selling parts, deduct their purchase value from the inventory account
1443 $ref->{expense_accno} = ($form->{"expense_accno_$row"}) ? $form->{"expense_accno_$row"} : $ref->{expense_accno};
1445 $form->{amount_cogs}{ $form->{id} }{ $ref->{expense_accno} } += -$linetotal;
1446 $form->{expense_inventory} .= " " . $ref->{expense_accno};
1447 $ref->{inventory_accno} = ($form->{"inventory_accno_$row"}) ? $form->{"inventory_accno_$row"} : $ref->{inventory_accno};
1449 $form->{amount_cogs}{ $form->{id} }{ $ref->{inventory_accno} } -= -$linetotal;
1450 $form->{expense_inventory} .= " " . $ref->{inventory_accno};
1456 last if (($totalqty -= $qty) <= 0);
1461 $main::lxdebug->leave_sub();
1466 sub reverse_invoice {
1467 $main::lxdebug->enter_sub();
1469 my ($dbh, $form) = @_;
1471 # reverse inventory items
1473 qq|SELECT i.id, i.parts_id, i.qty, i.assemblyitem, p.assembly, p.inventory_accno_id
1475 JOIN parts p ON (i.parts_id = p.id)
1476 WHERE i.trans_id = ?|;
1477 my $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{"id"}));
1479 while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
1481 if ($ref->{inventory_accno_id}) {
1482 # de-allocated purchases
1484 qq|SELECT i.id, i.trans_id, i.allocated
1486 WHERE (i.parts_id = ?) AND (i.allocated > 0)
1487 ORDER BY i.trans_id DESC|;
1488 my $sth2 = prepare_execute_query($form, $dbh, $query, conv_i($ref->{"parts_id"}));
1490 while (my $inhref = $sth2->fetchrow_hashref('NAME_lc')) {
1491 my $qty = $ref->{qty};
1492 if (($ref->{qty} - $inhref->{allocated}) > 0) {
1493 $qty = $inhref->{allocated};
1497 $form->update_balance($dbh, "invoice", "allocated", qq|id = $inhref->{id}|, $qty * -1);
1499 last if (($ref->{qty} -= $qty) <= 0);
1508 my @values = (conv_i($form->{id}));
1509 do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE trans_id = ?|, @values);
1510 do_query($form, $dbh, qq|DELETE FROM invoice WHERE trans_id = ?|, @values);
1511 do_query($form, $dbh, qq|DELETE FROM shipto WHERE (trans_id = ?) AND (module = 'AR')|, @values);
1513 $main::lxdebug->leave_sub();
1516 sub delete_invoice {
1517 $main::lxdebug->enter_sub();
1519 my ($self, $myconfig, $form) = @_;
1521 # connect to database
1522 my $dbh = $form->get_standard_dbh;
1524 &reverse_invoice($dbh, $form);
1526 my @values = (conv_i($form->{id}));
1528 # Falls wir ein Storno haben, mĂ¼ssen zwei Felder in der stornierten Rechnung wieder
1529 # zurĂ¼ckgesetzt werden. Vgl:
1530 # id | storno | storno_id | paid | amount
1531 #----+--------+-----------+---------+-----------
1532 # 18 | f | | 0.00000 | 119.00000
1534 # 18 | t | | 119.00000 | 119.00000
1536 if($form->{storno}){
1537 # storno_id auslesen und korrigieren
1538 my ($invoice_id) = selectfirst_array_query($form, $dbh, qq|SELECT storno_id FROM ar WHERE id = ?|,@values);
1539 do_query($form, $dbh, qq|UPDATE ar SET storno = 'f', paid = 0 WHERE id = ?|, $invoice_id);
1542 # delete spool files
1543 my @spoolfiles = selectall_array_query($form, $dbh, qq|SELECT spoolfile FROM status WHERE trans_id = ?|, @values);
1546 qq|DELETE FROM status WHERE trans_id = ?|,
1547 qq|DELETE FROM periodic_invoices WHERE ar_id = ?|,
1548 qq|DELETE FROM ar WHERE id = ?|,
1551 map { do_query($form, $dbh, $_, @values) } @queries;
1553 my $rc = $dbh->commit;
1556 my $spool = $::lx_office_conf{paths}->{spool};
1557 map { unlink "$spool/$_" if -f "$spool/$_"; } @spoolfiles;
1560 $main::lxdebug->leave_sub();
1565 sub retrieve_invoice {
1566 $main::lxdebug->enter_sub();
1568 my ($self, $myconfig, $form) = @_;
1570 # connect to database
1571 my $dbh = $form->get_standard_dbh;
1573 my ($sth, $ref, $query);
1575 my $query_transdate = !$form->{id} ? ", current_date AS invdate" : '';
1579 (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
1580 (SELECT c.accno FROM chart c WHERE d.income_accno_id = c.id) AS income_accno,
1581 (SELECT c.accno FROM chart c WHERE d.expense_accno_id = c.id) AS expense_accno,
1582 (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
1583 (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id) AS fxloss_accno
1587 $ref = selectfirst_hashref_query($form, $dbh, $query);
1588 map { $form->{$_} = $ref->{$_} } keys %{ $ref };
1591 my $id = conv_i($form->{id});
1594 #erweiterung um das entsprechende feld lieferscheinnummer (a.donumber) in der html-maske anzuzeigen 12.02.2009 jb
1598 a.invnumber, a.ordnumber, a.quonumber, a.cusordnumber,
1599 a.orddate, a.quodate, a.globalproject_id,
1600 a.transdate AS invdate, a.deliverydate, a.paid, a.storno, a.gldate,
1601 a.shippingpoint, a.shipvia, a.terms, a.notes, a.intnotes, a.taxzone_id,
1602 a.duedate, a.taxincluded, (SELECT cu.name FROM currencies cu WHERE cu.id=a.currency_id) AS currency, a.shipto_id, a.cp_id,
1603 a.employee_id, a.salesman_id, a.payment_id,
1604 a.language_id, a.delivery_customer_id, a.delivery_vendor_id, a.type,
1605 a.transaction_description, a.donumber, a.invnumber_for_credit_note,
1606 a.marge_total, a.marge_percent, a.direct_debit, a.delivery_term_id,
1609 LEFT JOIN employee e ON (e.id = a.employee_id)
1611 $ref = selectfirst_hashref_query($form, $dbh, $query, $id);
1612 map { $form->{$_} = $ref->{$_} } keys %{ $ref };
1614 $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{invdate}, "buy");
1617 $query = qq|SELECT * FROM shipto WHERE (trans_id = ?) AND (module = 'AR')|;
1618 $ref = selectfirst_hashref_query($form, $dbh, $query, $id);
1620 map { $form->{$_} = $ref->{$_} } keys %{ $ref };
1622 foreach my $vc (qw(customer vendor)) {
1623 next if !$form->{"delivery_${vc}_id"};
1624 ($form->{"delivery_${vc}_string"}) = selectrow_query($form, $dbh, qq|SELECT name FROM customer WHERE id = ?|, $id);
1627 # get printed, emailed
1628 $query = qq|SELECT printed, emailed, spoolfile, formname FROM status WHERE trans_id = ?|;
1629 $sth = prepare_execute_query($form, $dbh, $query, $id);
1631 while ($ref = $sth->fetchrow_hashref('NAME_lc')) {
1632 $form->{printed} .= "$ref->{formname} " if $ref->{printed};
1633 $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
1634 $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
1637 map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
1639 my $transdate = $form->{deliverydate} ? $dbh->quote($form->{deliverydate})
1640 : $form->{invdate} ? $dbh->quote($form->{invdate})
1644 my $taxzone_id = $form->{taxzone_id} *= 1;
1645 $taxzone_id = 0 if (0 > $taxzone_id) || (3 < $taxzone_id);
1647 # retrieve individual items
1650 c1.accno AS inventory_accno, c1.new_chart_id AS inventory_new_chart, date($transdate) - c1.valid_from AS inventory_valid,
1651 c2.accno AS income_accno, c2.new_chart_id AS income_new_chart, date($transdate) - c2.valid_from as income_valid,
1652 c3.accno AS expense_accno, c3.new_chart_id AS expense_new_chart, date($transdate) - c3.valid_from AS expense_valid,
1655 i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.discount, i.parts_id AS id, i.unit, i.deliverydate AS reqdate,
1656 i.project_id, i.serialnumber, i.id AS invoice_pos, i.pricegroup_id, i.ordnumber, i.donumber, i.transdate, i.cusordnumber, i.subtotal, i.lastcost,
1657 i.price_factor_id, i.price_factor, i.marge_price_factor,
1658 p.partnumber, p.assembly, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, p.formel, p.listprice,
1659 pr.projectnumber, pg.partsgroup, prg.pricegroup
1662 LEFT JOIN parts p ON (i.parts_id = p.id)
1663 LEFT JOIN project pr ON (i.project_id = pr.id)
1664 LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1665 LEFT JOIN pricegroup prg ON (i.pricegroup_id = prg.id)
1667 LEFT JOIN chart c1 ON ((SELECT inventory_accno_id FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c1.id)
1668 LEFT JOIN chart c2 ON ((SELECT income_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c2.id)
1669 LEFT JOIN chart c3 ON ((SELECT expense_accno_id_${taxzone_id} FROM buchungsgruppen WHERE id = p.buchungsgruppen_id) = c3.id)
1671 WHERE (i.trans_id = ?) AND NOT (i.assemblyitem = '1') ORDER BY i.id|;
1673 $sth = prepare_execute_query($form, $dbh, $query, $id);
1675 while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
1676 # Retrieve custom variables.
1677 my $cvars = CVar->get_custom_variables(dbh => $dbh,
1679 sub_module => 'invoice',
1680 trans_id => $ref->{invoice_id},
1682 map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
1683 delete $ref->{invoice_id};
1685 map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid)) if !$ref->{"part_inventory_accno_id"};
1686 delete($ref->{"part_inventory_accno_id"});
1688 foreach my $type (qw(inventory income expense)) {
1689 while ($ref->{"${type}_new_chart"} && ($ref->{"${type}_valid"} >=0)) {
1690 my $query = qq|SELECT accno, new_chart_id, date($transdate) - valid_from FROM chart WHERE id = ?|;
1691 @$ref{ map $type.$_, qw(_accno _new_chart _valid) } = selectrow_query($form, $dbh, $query, $ref->{"${type}_new_chart"});
1695 # get tax rates and description
1696 my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
1698 qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber FROM tax t
1699 LEFT JOIN chart c ON (c.id = t.chart_id)
1701 (SELECT tk.tax_id FROM taxkeys tk
1702 WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?)
1703 AND startdate <= date($transdate)
1704 ORDER BY startdate DESC LIMIT 1)
1706 my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
1707 $ref->{taxaccounts} = "";
1709 while (my $ptr = $stw->fetchrow_hashref('NAME_lc')) {
1711 if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
1715 $ref->{taxaccounts} .= "$ptr->{accno} ";
1717 if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
1718 $form->{"$ptr->{accno}_rate"} = $ptr->{rate};
1719 $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
1720 $form->{"$ptr->{accno}_taxnumber"} = $ptr->{taxnumber};
1721 $form->{taxaccounts} .= "$ptr->{accno} ";
1726 $ref->{qty} *= -1 if $form->{type} eq "credit_note";
1728 chop $ref->{taxaccounts};
1729 push @{ $form->{invoice_details} }, $ref;
1734 Common::webdav_folder($form);
1737 my $rc = $dbh->commit;
1739 $main::lxdebug->leave_sub();
1745 $main::lxdebug->enter_sub();
1747 my ($self, $myconfig, $form) = @_;
1749 # connect to database
1750 my $dbh = $form->get_standard_dbh;
1752 my $dateformat = $myconfig->{dateformat};
1753 $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
1755 my (@values, $duedate, $ref, $query);
1757 if ($form->{invdate}) {
1758 $duedate = "to_date(?, '$dateformat')";
1759 push @values, $form->{invdate};
1761 $duedate = "current_date";
1764 my $cid = conv_i($form->{customer_id});
1767 if ($form->{payment_id}) {
1768 $payment_id = "(pt.id = ?) OR";
1769 push @values, conv_i($form->{payment_id});
1775 c.id AS customer_id, c.name AS customer, c.discount as customer_discount, c.creditlimit, c.terms,
1776 c.email, c.cc, c.bcc, c.language_id, c.payment_id, c.delivery_term_id,
1777 c.street, c.zipcode, c.city, c.country,
1778 c.notes AS intnotes, c.klass as customer_klass, c.taxzone_id, c.salesman_id, cu.name AS curr,
1779 c.taxincluded_checked, c.direct_debit,
1780 $duedate + COALESCE(pt.terms_netto, 0) AS duedate,
1781 b.discount AS tradediscount, b.description AS business
1783 LEFT JOIN business b ON (b.id = c.business_id)
1784 LEFT JOIN payment_terms pt ON ($payment_id (c.payment_id = pt.id))
1785 LEFT JOIN currencies cu ON (c.currency_id=cu.id)
1788 $ref = selectfirst_hashref_query($form, $dbh, $query, @values);
1790 delete $ref->{salesman_id} if !$ref->{salesman_id};
1792 map { $form->{$_} = $ref->{$_} } keys %$ref;
1794 # use customer currency
1795 $form->{currency} = $form->{curr};
1798 qq|SELECT sum(amount - paid) AS dunning_amount
1800 WHERE (paid < amount)
1801 AND (customer_id = ?)
1802 AND (dunning_config_id IS NOT NULL)|;
1803 $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1804 map { $form->{$_} = $ref->{$_} } keys %$ref;
1807 qq|SELECT dnn.dunning_description AS max_dunning_level
1808 FROM dunning_config dnn
1809 WHERE id IN (SELECT dunning_config_id
1811 WHERE (paid < amount) AND (customer_id = ?) AND (dunning_config_id IS NOT NULL))
1812 ORDER BY dunning_level DESC LIMIT 1|;
1813 $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1814 map { $form->{$_} = $ref->{$_} } keys %$ref;
1816 $form->{creditremaining} = $form->{creditlimit};
1817 $query = qq|SELECT SUM(amount - paid) FROM ar WHERE customer_id = ?|;
1818 my ($value) = selectrow_query($form, $dbh, $query, $cid);
1819 $form->{creditremaining} -= $value;
1823 (SELECT e.buy FROM exchangerate e
1824 WHERE e.currency_id = o.currency_id
1825 AND e.transdate = o.transdate)
1827 WHERE o.customer_id = ?
1828 AND o.quotation = '0'
1829 AND o.closed = '0'|;
1830 my $sth = prepare_execute_query($form, $dbh, $query, $cid);
1832 while (my ($amount, $exch) = $sth->fetchrow_array) {
1833 $exch = 1 unless $exch;
1834 $form->{creditremaining} -= $amount * $exch;
1838 # get shipto if we did not converted an order or invoice
1839 if (!$form->{shipto}) {
1840 map { delete $form->{$_} }
1841 qw(shiptoname shiptodepartment_1 shiptodepartment_2
1842 shiptostreet shiptozipcode shiptocity shiptocountry
1843 shiptocontact shiptophone shiptofax shiptoemail);
1845 $query = qq|SELECT * FROM shipto WHERE trans_id = ? AND module = 'CT'|;
1846 $ref = selectfirst_hashref_query($form, $dbh, $query, $cid);
1848 map { $form->{$_} = $ref->{$_} } keys %$ref;
1851 # setup last accounts used for this customer
1852 if (!$form->{id} && $form->{type} !~ /_(order|quotation)/) {
1854 qq|SELECT c.id, c.accno, c.description, c.link, c.category
1856 JOIN acc_trans ac ON (ac.chart_id = c.id)
1857 JOIN ar a ON (a.id = ac.trans_id)
1858 WHERE a.customer_id = ?
1859 AND NOT (c.link LIKE '%_tax%' OR c.link LIKE '%_paid%')
1860 AND a.id IN (SELECT max(a2.id) FROM ar a2 WHERE a2.customer_id = ?)|;
1861 $sth = prepare_execute_query($form, $dbh, $query, $cid, $cid);
1864 while ($ref = $sth->fetchrow_hashref('NAME_lc')) {
1865 if ($ref->{category} eq 'I') {
1867 $form->{"AR_amount_$i"} = "$ref->{accno}--$ref->{description}";
1869 if ($form->{initial_transdate}) {
1871 qq|SELECT tk.tax_id, t.rate
1873 LEFT JOIN tax t ON tk.tax_id = t.id
1874 WHERE (tk.chart_id = ?) AND (startdate <= date(?))
1875 ORDER BY tk.startdate DESC
1877 my ($tax_id, $rate) =
1878 selectrow_query($form, $dbh, $tax_query, $ref->{id},
1879 $form->{initial_transdate});
1880 $form->{"taxchart_$i"} = "${tax_id}--${rate}";
1883 if ($ref->{category} eq 'A') {
1884 $form->{ARselected} = $form->{AR_1} = $ref->{accno};
1888 $form->{rowcount} = $i if ($i && !$form->{type});
1891 $main::lxdebug->leave_sub();
1895 $main::lxdebug->enter_sub();
1897 my ($self, $myconfig, $form) = @_;
1899 # connect to database
1900 my $dbh = $form->get_standard_dbh;
1902 my $i = $form->{rowcount};
1904 my $where = qq|NOT p.obsolete = '1'|;
1907 foreach my $column (qw(p.partnumber p.description pgpartsgroup )) {
1908 my ($table, $field) = split m/\./, $column;
1909 next if !$form->{"${field}_${i}"};
1910 $where .= qq| AND lower(${column}) ILIKE ?|;
1911 push @values, '%' . $form->{"${field}_${i}"} . '%';
1914 #Es soll auch nach EAN gesucht werden, ohne Einschränkung durch Beschreibung
1915 if ($form->{"partnumber_$i"} && !$form->{"description_$i"}) {
1916 $where .= qq| OR (NOT p.obsolete = '1' AND p.ean = ? )|;
1917 push @values, $form->{"partnumber_$i"};
1920 # Search for part ID overrides all other criteria.
1921 if ($form->{"id_${i}"}) {
1922 $where = qq|p.id = ?|;
1923 @values = ($form->{"id_${i}"});
1926 if ($form->{"description_$i"}) {
1927 $where .= qq| ORDER BY p.description|;
1929 $where .= qq| ORDER BY p.partnumber|;
1933 if ($form->{type} eq "invoice") {
1935 $form->{deliverydate} ? $dbh->quote($form->{deliverydate}) :
1936 $form->{invdate} ? $dbh->quote($form->{invdate}) :
1940 $form->{transdate} ? $dbh->quote($form->{transdate}) :
1944 my $taxzone_id = $form->{taxzone_id} * 1;
1945 $taxzone_id = 0 if (0 > $taxzone_id) || (3 < $taxzone_id);
1949 p.id, p.partnumber, p.description, p.sellprice,
1950 p.listprice, p.inventory_accno_id, p.lastcost,
1952 c1.accno AS inventory_accno,
1953 c1.new_chart_id AS inventory_new_chart,
1954 date($transdate) - c1.valid_from AS inventory_valid,
1956 c2.accno AS income_accno,
1957 c2.new_chart_id AS income_new_chart,
1958 date($transdate) - c2.valid_from AS income_valid,
1960 c3.accno AS expense_accno,
1961 c3.new_chart_id AS expense_new_chart,
1962 date($transdate) - c3.valid_from AS expense_valid,
1964 p.unit, p.assembly, p.onhand,
1965 p.notes AS partnotes, p.notes AS longdescription,
1966 p.not_discountable, p.formel, p.payment_id AS part_payment_id,
1967 p.price_factor_id, p.weight,
1969 pfac.factor AS price_factor,
1974 LEFT JOIN chart c1 ON
1975 ((SELECT inventory_accno_id
1976 FROM buchungsgruppen
1977 WHERE id = p.buchungsgruppen_id) = c1.id)
1978 LEFT JOIN chart c2 ON
1979 ((SELECT income_accno_id_${taxzone_id}
1980 FROM buchungsgruppen
1981 WHERE id = p.buchungsgruppen_id) = c2.id)
1982 LEFT JOIN chart c3 ON
1983 ((SELECT expense_accno_id_${taxzone_id}
1984 FROM buchungsgruppen
1985 WHERE id = p.buchungsgruppen_id) = c3.id)
1986 LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
1987 LEFT JOIN price_factors pfac ON (pfac.id = p.price_factor_id)
1989 my $sth = prepare_execute_query($form, $dbh, $query, @values);
1991 my @translation_queries = ( [ qq|SELECT tr.translation, tr.longdescription
1993 WHERE tr.language_id = ? AND tr.parts_id = ?| ],
1994 [ qq|SELECT tr.translation, tr.longdescription
1996 WHERE tr.language_id IN
1999 WHERE article_code = (SELECT article_code FROM language WHERE id = ?))
2002 map { push @{ $_ }, prepare_query($form, $dbh, $_->[0]) } @translation_queries;
2004 while (my $ref = $sth->fetchrow_hashref('NAME_lc')) {
2006 # In der Buchungsgruppe ist immer ein Bestandskonto verknuepft, auch wenn
2007 # es sich um eine Dienstleistung handelt. Bei Dienstleistungen muss das
2008 # Buchungskonto also aus dem Ergebnis rausgenommen werden.
2009 if (!$ref->{inventory_accno_id}) {
2010 map({ delete($ref->{"inventory_${_}"}); } qw(accno new_chart valid));
2012 delete($ref->{inventory_accno_id});
2014 foreach my $type (qw(inventory income expense)) {
2015 while ($ref->{"${type}_new_chart"} && ($ref->{"${type}_valid"} >=0)) {
2017 qq|SELECT accno, new_chart_id, date($transdate) - valid_from
2020 ($ref->{"${type}_accno"},
2021 $ref->{"${type}_new_chart"},
2022 $ref->{"${type}_valid"})
2023 = selectrow_query($form, $dbh, $query, $ref->{"${type}_new_chart"});
2027 if ($form->{payment_id} eq "") {
2028 $form->{payment_id} = $form->{part_payment_id};
2031 # get tax rates and description
2032 my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
2034 qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber
2036 LEFT JOIN chart c ON (c.id = t.chart_id)
2040 WHERE tk.chart_id = (SELECT id from chart WHERE accno = ?)
2042 ORDER BY startdate DESC
2045 @values = ($accno_id, $transdate eq "current_date" ? "now" : $transdate);
2046 my $stw = $dbh->prepare($query);
2047 $stw->execute(@values) || $form->dberror($query);
2049 $ref->{taxaccounts} = "";
2051 while (my $ptr = $stw->fetchrow_hashref('NAME_lc')) {
2053 if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
2057 $ref->{taxaccounts} .= "$ptr->{accno} ";
2059 if (!($form->{taxaccounts} =~ /\Q$ptr->{accno}\E/)) {
2060 $form->{"$ptr->{accno}_rate"} = $ptr->{rate};
2061 $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
2062 $form->{"$ptr->{accno}_taxnumber"} = $ptr->{taxnumber};
2063 $form->{taxaccounts} .= "$ptr->{accno} ";
2069 chop $ref->{taxaccounts};
2071 if ($form->{language_id}) {
2072 for my $spec (@translation_queries) {
2073 do_statement($form, $spec->[1], $spec->[0], conv_i($form->{language_id}), conv_i($ref->{id}));
2074 my ($translation, $longdescription) = $spec->[1]->fetchrow_array;
2075 next unless $translation;
2076 $ref->{description} = $translation;
2077 $ref->{longdescription} = $longdescription;
2082 $ref->{onhand} *= 1;
2084 push @{ $form->{item_list} }, $ref;
2087 $_->[1]->finish for @translation_queries;
2089 foreach my $item (@{ $form->{item_list} }) {
2090 my $custom_variables = CVar->get_custom_variables(module => 'IC',
2091 trans_id => $item->{id},
2095 map { $item->{"ic_cvar_" . $_->{name} } = $_->{value} } @{ $custom_variables };
2098 $main::lxdebug->leave_sub();
2101 ##########################
2102 # get pricegroups from database
2103 # build up selected pricegroup
2104 # if an exchange rate - change price
2107 sub get_pricegroups_for_parts {
2109 $main::lxdebug->enter_sub();
2111 my ($self, $myconfig, $form) = @_;
2113 my $dbh = $form->get_standard_dbh;
2115 $form->{"PRICES"} = {};
2119 my $all_units = AM->retrieve_units($myconfig, $form);
2120 while (($form->{"id_$i"}) or ($form->{"new_id_$i"})) {
2121 $form->{"PRICES"}{$i} = [];
2123 $id = $form->{"id_$i"};
2125 if (!($form->{"id_$i"}) and $form->{"new_id_$i"}) {
2126 $id = $form->{"new_id_$i"};
2129 my ($price, $selectedpricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
2131 my $pricegroup_old = $form->{"pricegroup_old_$i"};
2133 # sellprice has format 13,0000 or 0,00000, can't check for 0 numerically
2134 my $sellprice = $form->{"sellprice_$i"};
2135 my $pricegroup_id = $form->{"pricegroup_id_$i"};
2136 $form->{"new_pricegroup_$i"} = $selectedpricegroup_id;
2137 $form->{"old_pricegroup_$i"} = $pricegroup_old;
2139 my $price_new = $form->{"price_new_$i"};
2140 my $price_old = $form->{"price_old_$i"};
2142 if (!$form->{"unit_old_$i"}) {
2143 # Neue Ware aus der Datenbank. In diesem Fall ist unit_$i die
2144 # Einheit, wie sie in den Stammdaten hinterlegt wurde.
2145 # Es sollte also angenommen werden, dass diese ausgewaehlt war.
2146 $form->{"unit_old_$i"} = $form->{"unit_$i"};
2149 # Die zuletzt ausgewaehlte mit der aktuell ausgewaehlten Einheit
2150 # vergleichen und bei Unterschied den Preis entsprechend umrechnen.
2151 $form->{"selected_unit_$i"} = $form->{"unit_$i"} unless ($form->{"selected_unit_$i"});
2153 if (!$all_units->{$form->{"selected_unit_$i"}} ||
2154 ($all_units->{$form->{"selected_unit_$i"}}->{"base_unit"} ne
2155 $all_units->{$form->{"unit_old_$i"}}->{"base_unit"})) {
2156 # Die ausgewaehlte Einheit ist fuer diesen Artikel nicht gueltig
2157 # (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber
2158 # um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen.
2159 $form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"};
2164 if ($form->{"unit_old_$i"} ne $form->{"selected_unit_$i"}) {
2165 if (defined($all_units->{$form->{"unit_old_$i"}}->{"factor"}) &&
2166 $all_units->{$form->{"unit_old_$i"}}->{"factor"}) {
2167 $basefactor = $all_units->{$form->{"selected_unit_$i"}}->{"factor"} /
2168 $all_units->{$form->{"unit_old_$i"}}->{"factor"};
2172 if (!$form->{"basefactor_$i"}) {
2173 $form->{"basefactor_$i"} = 1;
2179 sellprice AS default_sellprice,
2182 'selected' AS selected
2188 parts.sellprice AS default_sellprice,
2189 pricegroup.pricegroup,
2193 LEFT JOIN parts ON parts.id = parts_id
2194 LEFT JOIN pricegroup ON pricegroup.id = pricegroup_id
2196 ORDER BY pricegroup|;
2197 my @values = (conv_i($id), conv_i($id));
2198 my $pkq = prepare_execute_query($form, $dbh, $query, @values);
2200 while (my $pkr = $pkq->fetchrow_hashref('NAME_lc')) {
2202 $pkr->{selected} = '';
2204 # if there is an exchange rate change price
2205 if (($form->{exchangerate} * 1) != 0) {
2206 $pkr->{price} /= $form->{exchangerate};
2209 $pkr->{price} *= $form->{"basefactor_$i"};
2210 $pkr->{price} *= $basefactor;
2211 $pkr->{price_ufmt} = $pkr->{price};
2212 $pkr->{price} = $form->format_amount($myconfig, $pkr->{price}, 5);
2214 if (!defined $selectedpricegroup_id) {
2215 # new entries in article list, either old invoice was loaded (edit) or a new article was added
2216 # Case A: open old invoice, no pricegroup selected
2217 # Case B: add new article to invoice, no pricegroup selected
2219 # to distinguish case A and B the variable pricegroup_id_$i is used
2220 # for new articles this variable isn't defined, for loaded articles it is
2221 # sellprice can't be used, as it already has 0,00 set
2223 if ($pkr->{pricegroup_id} eq $form->{"pricegroup_id_$i"} and defined $form->{"pricegroup_id_$i"}) {
2225 $pkr->{selected} = ' selected';
2226 } elsif ($pkr->{pricegroup_id} eq $form->{customer_klass}
2227 and not defined $form->{"pricegroup_id_$i"}
2228 and $pkr->{price_ufmt} != 0 # only use customer pricegroup price if it has a value, else use default_sellprice
2229 # for the case where pricegroup prices haven't been set
2231 # Case B: use default pricegroup of customer
2233 $pkr->{selected} = ' selected'; # unless $form->{selected};
2234 # no customer pricesgroup set
2235 if ($pkr->{price_ufmt} == $pkr->{default_sellprice}) {
2237 $pkr->{price} = $form->{"sellprice_$i"};
2241 # this sub should not set anything and only return. --sschoeling, 20090506
2242 # is this correct? put in again... -- grichardson 20110119
2243 $form->{"sellprice_$i"} = $pkr->{price};
2246 } elsif ($pkr->{price_ufmt} == $pkr->{default_sellprice} and $pkr->{default_sellprice} != 0) {
2247 $pkr->{price} = $form->{"sellprice_$i"};
2248 $pkr->{selected} = ' selected';
2252 # existing article: pricegroup or price changed
2253 if ($selectedpricegroup_id or $selectedpricegroup_id == 0) {
2254 if ($selectedpricegroup_id ne $pricegroup_old) {
2255 # pricegroup has changed
2256 if ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
2257 $pkr->{selected} = ' selected';
2259 } elsif ( ($form->parse_amount($myconfig, $price_new)
2260 != $form->parse_amount($myconfig, $form->{"sellprice_$i"}))
2261 and ($price_new ne 0) and defined $price_new) {
2262 # sellprice has changed
2263 # when loading existing invoices $price_new is NULL
2264 if ($pkr->{pricegroup_id} == 0) {
2265 $pkr->{price} = $form->{"sellprice_$i"};
2266 $pkr->{selected} = ' selected';
2268 } elsif ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
2269 # neither sellprice nor pricegroup changed
2270 $pkr->{selected} = ' selected';
2271 if ( ($pkr->{pricegroup_id} == 0) and ($pkr->{price} == $form->{"sellprice_$i"})) {
2272 # $pkr->{price} = $form->{"sellprice_$i"};
2274 $pkr->{price} = $form->{"sellprice_$i"};
2278 push @{ $form->{PRICES}{$i} }, $pkr;
2281 $form->{"basefactor_$i"} *= $basefactor;
2288 $main::lxdebug->leave_sub();
2292 $main::lxdebug->enter_sub();
2294 my ($self, $myconfig, $form, $table) = @_;
2296 $main::lxdebug->leave_sub() and return 0 unless ($form->{id});
2298 # make sure there's no funny stuff in $table
2299 # ToDO: die when this happens and throw an error
2300 $main::lxdebug->leave_sub() and return 0 if ($table =~ /\W/);
2302 my $dbh = $form->get_standard_dbh;
2304 my $query = qq|SELECT storno FROM $table WHERE storno_id = ?|;
2305 my ($result) = selectrow_query($form, $dbh, $query, $form->{id});
2307 $main::lxdebug->leave_sub();
2313 $main::lxdebug->enter_sub();
2315 my ($self, $myconfig, $form, $table, $id) = @_;
2317 $main::lxdebug->leave_sub() and return 0 unless ($id);
2319 # make sure there's no funny stuff in $table
2320 # ToDO: die when this happens and throw an error
2321 $main::lxdebug->leave_sub() and return 0 if ($table =~ /\W/);
2323 my $dbh = $form->get_standard_dbh;
2325 my $query = qq|SELECT storno FROM $table WHERE id = ?|;
2326 my ($result) = selectrow_query($form, $dbh, $query, $id);
2328 $main::lxdebug->leave_sub();
2333 sub get_standard_accno_current_assets {
2334 $main::lxdebug->enter_sub();
2336 my ($self, $myconfig, $form) = @_;
2338 my $dbh = $form->get_standard_dbh;
2340 my $query = qq| SELECT accno FROM chart WHERE id = (SELECT ar_paid_accno_id FROM defaults)|;
2341 my ($result) = selectrow_query($form, $dbh, $query);
2343 $main::lxdebug->leave_sub();