1 #=====================================================================
 
   4 # Based on SQL-Ledger Version 2.1.9
 
   5 # Web http://www.lx-office.org
 
   7 #=====================================================================
 
   8 # SQL-Ledger Accounting
 
  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., 51 Franklin Street, Fifth Floor, Boston,
 
  30 #======================================================================
 
  32 # General ledger backend code
 
  35 #   DS. 2000-07-04  Created
 
  36 #   DS. 2001-06-12  Changed relations from accno to chart_id
 
  38 #======================================================================
 
  42 use List::Util qw(first);
 
  45 use SL::DATEV qw(:CONSTANTS);
 
  49 use SL::Util qw(trim);
 
  54 sub delete_transaction {
 
  55   my ($self, $myconfig, $form) = @_;
 
  56   $main::lxdebug->enter_sub();
 
  58   SL::DB->client->with_transaction(sub {
 
  59     do_query($form, SL::DB->client->dbh, qq|DELETE FROM gl WHERE id = ?|, conv_i($form->{id}));
 
  61   }) or do { die SL::DB->client->error };
 
  63   $main::lxdebug->leave_sub();
 
  66 sub post_transaction {
 
  67   my ($self, $myconfig, $form) = @_;
 
  68   $main::lxdebug->enter_sub();
 
  70   my $rc = SL::DB->client->with_transaction(\&_post_transaction, $self, $myconfig, $form);
 
  72   $::lxdebug->leave_sub;
 
  76 sub _post_transaction {
 
  77   my ($self, $myconfig, $form) = @_;
 
  78   $main::lxdebug->enter_sub();
 
  80   my ($debit, $credit) = (0, 0);
 
  85   my $dbh = SL::DB->client->dbh;
 
  87   # post the transaction
 
  88   # make up a unique handle and store in reference field
 
  89   # then retrieve the record based on the unique handle to get the id
 
  90   # replace the reference field with the actual variable
 
  91   # add records to acc_trans
 
  93   # if there is a $form->{id} replace the old transaction
 
  94   # delete all acc_trans entries and add the new ones
 
  96   if (!$form->{taxincluded}) {
 
  97     $form->{taxincluded} = 0;
 
 100   my ($query, $sth, @values, $taxkey, $rate, $posted);
 
 104     # delete individual transactions
 
 105     $query = qq|DELETE FROM acc_trans WHERE trans_id = ?|;
 
 106     @values = (conv_i($form->{id}));
 
 107     do_query($form, $dbh, $query, @values);
 
 110     $query = qq|SELECT nextval('glid')|;
 
 111     ($form->{id}) = selectrow_query($form, $dbh, $query);
 
 114       qq|INSERT INTO gl (id, employee_id) | .
 
 115       qq|VALUES (?, (SELECT id FROM employee WHERE login = ?))|;
 
 116     @values = ($form->{id}, $::myconfig{login});
 
 117     do_query($form, $dbh, $query, @values);
 
 120   $form->{ob_transaction} *= 1;
 
 121   $form->{cb_transaction} *= 1;
 
 125          reference = ?, description = ?, notes = ?,
 
 126          transdate = ?, deliverydate = ?, tax_point = ?, department_id = ?, taxincluded = ?,
 
 127          storno = ?, storno_id = ?, ob_transaction = ?, cb_transaction = ?
 
 130   @values = ($form->{reference}, $form->{description}, $form->{notes},
 
 131              conv_date($form->{transdate}), conv_date($form->{deliverydate}), conv_date($form->{tax_point}), conv_i($form->{department_id}), $form->{taxincluded} ? 't' : 'f',
 
 132              $form->{storno} ? 't' : 'f', conv_i($form->{storno_id}), $form->{ob_transaction} ? 't' : 'f', $form->{cb_transaction} ? 't' : 'f',
 
 133              conv_i($form->{id}));
 
 134   do_query($form, $dbh, $query, @values);
 
 136   # insert acc_trans transactions
 
 137   for $i (1 .. $form->{rowcount}) {
 
 138     ($form->{"tax_id_$i"}) = split(/--/, $form->{"taxchart_$i"});
 
 139     if ($form->{"tax_id_$i"} ne "") {
 
 140       $query = qq|SELECT taxkey, rate FROM tax WHERE id = ?|;
 
 141       ($taxkey, $rate) = selectrow_query($form, $dbh, $query, conv_i($form->{"tax_id_$i"}));
 
 145     my $debit  = $form->{"debit_$i"};
 
 146     my $credit = $form->{"credit_$i"};
 
 147     my $tax    = $form->{"tax_$i"};
 
 154       $amount = $debit * -1;
 
 159     $project_id = conv_i($form->{"project_id_$i"});
 
 161     # if there is an amount, add the record
 
 164         qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
 
 165                                   source, memo, project_id, taxkey, ob_transaction, cb_transaction, tax_id, chart_link)
 
 166            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT link FROM chart WHERE id = ?))|;
 
 167       @values = (conv_i($form->{id}), $form->{"accno_id_$i"}, $amount, conv_date($form->{transdate}),
 
 168                  $form->{"source_$i"}, $form->{"memo_$i"}, $project_id, $taxkey, $form->{ob_transaction} ? 't' : 'f', $form->{cb_transaction} ? 't' : 'f', conv_i($form->{"tax_id_$i"}), $form->{"accno_id_$i"});
 
 169       do_query($form, $dbh, $query, @values);
 
 175         qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
 
 176                                   source, memo, project_id, taxkey, tax_id, chart_link)
 
 177            VALUES (?, (SELECT chart_id FROM tax WHERE id = ?),
 
 178                    ?, ?, ?, ?, ?, ?, ?, (SELECT link
 
 180                                          WHERE id = (SELECT chart_id
 
 183       @values = (conv_i($form->{id}), conv_i($form->{"tax_id_$i"}),
 
 184                  $tax, conv_date($form->{transdate}), $form->{"source_$i"},
 
 185                  $form->{"memo_$i"}, $project_id, $taxkey, conv_i($form->{"tax_id_$i"}), conv_i($form->{"tax_id_$i"}));
 
 186       do_query($form, $dbh, $query, @values);
 
 190   if ($form->{storno} && $form->{storno_id}) {
 
 191     do_query($form, $dbh, qq|UPDATE gl SET storno = 't' WHERE id = ?|, conv_i($form->{storno_id}));
 
 194   if ($form->{draft_id}) {
 
 195     SL::DB::Manager::Draft->delete_all(where => [ id => delete($form->{draft_id}) ]);
 
 198   # safety check datev export
 
 199   if ($::instance_conf->get_datev_check_on_gl_transaction) {
 
 201     # create datev object
 
 202     my $datev = SL::DATEV->new(
 
 204       trans_id   => $form->{id},
 
 207     $datev->generate_datev_data;
 
 209     if ($datev->errors) {
 
 210       die join "\n", $::locale->text('DATEV check returned errors:'), $datev->errors;
 
 217 sub all_transactions {
 
 218   my ($self, $myconfig, $form) = @_;
 
 219   $main::lxdebug->enter_sub();
 
 221   my $dbh = SL::DB->client->dbh;
 
 222   my ($query, $sth, $source, $null, $space);
 
 224   my ($glwhere, $arwhere, $apwhere) = ("1 = 1", "1 = 1", "1 = 1");
 
 225   my (@glvalues, @arvalues, @apvalues);
 
 227   if ($form->{reference}) {
 
 228     $glwhere .= qq| AND g.reference ILIKE ?|;
 
 229     $arwhere .= qq| AND a.invnumber ILIKE ?|;
 
 230     $apwhere .= qq| AND a.invnumber ILIKE ?|;
 
 231     push(@glvalues, like($form->{reference}));
 
 232     push(@arvalues, like($form->{reference}));
 
 233     push(@apvalues, like($form->{reference}));
 
 236   if ($form->{department_id}) {
 
 237     $glwhere .= qq| AND g.department_id = ?|;
 
 238     $arwhere .= qq| AND a.department_id = ?|;
 
 239     $apwhere .= qq| AND a.department_id = ?|;
 
 240     push(@glvalues, $form->{department_id});
 
 241     push(@arvalues, $form->{department_id});
 
 242     push(@apvalues, $form->{department_id});
 
 245   if ($form->{source}) {
 
 246     $glwhere .= " AND ac.trans_id IN (SELECT trans_id from acc_trans WHERE source ILIKE ?)";
 
 247     $arwhere .= " AND ac.trans_id IN (SELECT trans_id from acc_trans WHERE source ILIKE ?)";
 
 248     $apwhere .= " AND ac.trans_id IN (SELECT trans_id from acc_trans WHERE source ILIKE ?)";
 
 249     push(@glvalues, like($form->{source}));
 
 250     push(@arvalues, like($form->{source}));
 
 251     push(@apvalues, like($form->{source}));
 
 254   # default Datumseinschränkung falls nicht oder falsch übergeben (sollte nie passieren)
 
 255   $form->{datesort} = 'transdate' unless $form->{datesort} =~ /^(transdate|gldate)$/;
 
 257   if (trim($form->{datefrom})) {
 
 258     $glwhere .= " AND ac.$form->{datesort} >= ?";
 
 259     $arwhere .= " AND ac.$form->{datesort} >= ?";
 
 260     $apwhere .= " AND ac.$form->{datesort} >= ?";
 
 261     push(@glvalues, trim($form->{datefrom}));
 
 262     push(@arvalues, trim($form->{datefrom}));
 
 263     push(@apvalues, trim($form->{datefrom}));
 
 266   if (trim($form->{dateto})) {
 
 267     $glwhere .= " AND ac.$form->{datesort} <= ?";
 
 268     $arwhere .= " AND ac.$form->{datesort} <= ?";
 
 269     $apwhere .= " AND ac.$form->{datesort} <= ?";
 
 270     push(@glvalues, trim($form->{dateto}));
 
 271     push(@arvalues, trim($form->{dateto}));
 
 272     push(@apvalues, trim($form->{dateto}));
 
 275   if (trim($form->{description})) {
 
 276     $glwhere .= " AND g.description ILIKE ?";
 
 277     $arwhere .= " AND ct.name ILIKE ?";
 
 278     $apwhere .= " AND ct.name ILIKE ?";
 
 279     push(@glvalues, like($form->{description}));
 
 280     push(@arvalues, like($form->{description}));
 
 281     push(@apvalues, like($form->{description}));
 
 284   if ($form->{employee_id}) {
 
 285     $glwhere .= " AND g.employee_id = ? ";
 
 286     $arwhere .= " AND a.employee_id = ? ";
 
 287     $apwhere .= " AND a.employee_id = ? ";
 
 288     push(@glvalues, conv_i($form->{employee_id}));
 
 289     push(@arvalues, conv_i($form->{employee_id}));
 
 290     push(@apvalues, conv_i($form->{employee_id}));
 
 293   if (trim($form->{notes})) {
 
 294     $glwhere .= " AND g.notes ILIKE ?";
 
 295     $arwhere .= " AND a.notes ILIKE ?";
 
 296     $apwhere .= " AND a.notes ILIKE ?";
 
 297     push(@glvalues, like($form->{notes}));
 
 298     push(@arvalues, like($form->{notes}));
 
 299     push(@apvalues, like($form->{notes}));
 
 302   if ($form->{accno}) {
 
 303     $glwhere .= " AND c.accno = '$form->{accno}'";
 
 304     $arwhere .= " AND c.accno = '$form->{accno}'";
 
 305     $apwhere .= " AND c.accno = '$form->{accno}'";
 
 308   if ($form->{category} ne 'X') {
 
 309     $glwhere .= qq| AND g.id in (SELECT trans_id FROM acc_trans ac2 WHERE ac2.chart_id IN (SELECT id FROM chart c2 WHERE c2.category = ?))|;
 
 310     $arwhere .= qq| AND a.id in (SELECT trans_id FROM acc_trans ac2 WHERE ac2.chart_id IN (SELECT id FROM chart c2 WHERE c2.category = ?))|;
 
 311     $apwhere .= qq| AND a.id in (SELECT trans_id FROM acc_trans ac2 WHERE ac2.chart_id IN (SELECT id FROM chart c2 WHERE c2.category = ?))|;
 
 312     push(@glvalues, $form->{category});
 
 313     push(@arvalues, $form->{category});
 
 314     push(@apvalues, $form->{category});
 
 317   if ($form->{project_id}) {
 
 318     $glwhere .= qq| AND g.id IN (SELECT DISTINCT trans_id FROM acc_trans WHERE project_id = ?)|;
 
 320       qq| AND ((a.globalproject_id = ?) OR
 
 321                (a.id IN (SELECT DISTINCT trans_id FROM acc_trans WHERE project_id = ?)))|;
 
 323       qq| AND ((a.globalproject_id = ?) OR
 
 324                (a.id IN (SELECT DISTINCT trans_id FROM acc_trans WHERE project_id = ?)))|;
 
 325     my $project_id = conv_i($form->{project_id});
 
 326     push(@glvalues, $project_id);
 
 327     push(@arvalues, $project_id, $project_id);
 
 328     push(@apvalues, $project_id, $project_id);
 
 331   my ($project_columns, $project_join);
 
 332   if ($form->{"l_projectnumbers"}) {
 
 333     $project_columns = qq|, ac.project_id, pr.projectnumber|;
 
 334     $project_join = qq|LEFT JOIN project pr ON (ac.project_id = pr.id)|;
 
 337   if ($form->{accno}) {
 
 338     # get category for account
 
 339     $query = qq|SELECT category FROM chart WHERE accno = ?|;
 
 340     ($form->{ml}) = selectrow_query($form, $dbh, $query, $form->{accno});
 
 342     if ($form->{datefrom}) {
 
 344         qq|SELECT SUM(ac.amount)
 
 346            LEFT JOIN chart c ON (ac.chart_id = c.id)
 
 347            WHERE (c.accno = ?) AND (ac.$form->{datesort} < ?)|;
 
 348       ($form->{balance}) = selectrow_query($form, $dbh, $query, $form->{accno}, conv_date($form->{datefrom}));
 
 354     'transdate'    => [ qw(transdate id)         ],
 
 355     'gldate'       => [ qw(gldate id)         ],
 
 356     'reference'    => [ qw(lower_reference id)   ],
 
 357     'description'  => [ qw(lower_description id) ],
 
 358     'accno'        => [ qw(accno transdate id)   ],
 
 360   my %lowered_columns =  (
 
 361     'reference'       => { 'gl' => 'g.reference',   'arap' => 'a.invnumber', },
 
 362     'source'          => { 'gl' => 'ac.source',     'arap' => 'ac.source',   },
 
 363     'description'     => { 'gl' => 'g.description', 'arap' => 'ct.name',     },
 
 366   # sortdir = sort direction (ascending or descending)
 
 367   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
 
 368   my $sortkey   = $sort_columns{$form->{sort}} ? $form->{sort} : $form->{datesort};  # default used to be transdate
 
 369   my $sortorder = join ', ', map { "$_ $sortdir" } @{ $sort_columns{$sortkey} };
 
 371   my %columns_for_sorting = ( 'gl' => '', 'arap' => '', );
 
 372   foreach my $spec (@{ $sort_columns{$sortkey} }) {
 
 373     next if ($spec !~ m/^lower_(.*)$/);
 
 376     map { $columns_for_sorting{$_} .= sprintf(', lower(%s) AS lower_%s', $lowered_columns{$column}->{$_}, $column) } qw(gl arap);
 
 381         ac.acc_trans_id, g.id, 'gl' AS type, FALSE AS invoice, g.reference, ac.taxkey, c.link,
 
 382         g.description, ac.transdate, ac.gldate, ac.source, ac.trans_id,
 
 383         ac.amount, c.accno, g.notes, t.chart_id,
 
 384         CASE WHEN (COALESCE(e.name, '') = '') THEN e.login ELSE e.name END AS employee
 
 386         $columns_for_sorting{gl}
 
 388       LEFT JOIN employee e ON (g.employee_id = e.id),
 
 389       acc_trans ac $project_join, chart c
 
 390       LEFT JOIN tax t ON (t.chart_id = c.id)
 
 392         AND (ac.chart_id = c.id)
 
 393         AND (g.id = ac.trans_id)
 
 397       SELECT ac.acc_trans_id, a.id, 'ar' AS type, a.invoice, a.invnumber, ac.taxkey, c.link,
 
 398         ct.name, ac.transdate, ac.gldate, ac.source, ac.trans_id,
 
 399         ac.amount, c.accno, a.notes, t.chart_id,
 
 400         CASE WHEN (COALESCE(e.name, '') = '') THEN e.login ELSE e.name END AS employee
 
 402         $columns_for_sorting{arap}
 
 404       LEFT JOIN employee e ON (a.employee_id = e.id),
 
 405       acc_trans ac $project_join, customer ct, chart c
 
 406       LEFT JOIN tax t ON (t.chart_id=c.id)
 
 408         AND (ac.chart_id = c.id)
 
 409         AND (a.customer_id = ct.id)
 
 410         AND (a.id = ac.trans_id)
 
 414       SELECT ac.acc_trans_id, a.id, 'ap' AS type, a.invoice, a.invnumber, ac.taxkey, c.link,
 
 415         ct.name, ac.transdate, ac.gldate, ac.source, ac.trans_id,
 
 416         ac.amount, c.accno, a.notes, t.chart_id,
 
 417         CASE WHEN (COALESCE(e.name, '') = '') THEN e.login ELSE e.name END AS employee
 
 419         $columns_for_sorting{arap}
 
 421       LEFT JOIN employee e ON (a.employee_id = e.id),
 
 422       acc_trans ac $project_join, vendor ct, chart c
 
 423       LEFT JOIN tax t ON (t.chart_id=c.id)
 
 425         AND (ac.chart_id = c.id)
 
 426         AND (a.vendor_id = ct.id)
 
 427         AND (a.id = ac.trans_id)
 
 429       ORDER BY $sortorder, acc_trans_id $sortdir|;
 
 430 #      ORDER BY gldate DESC, id DESC, acc_trans_id DESC
 
 432   my @values = (@glvalues, @arvalues, @apvalues);
 
 434   # Show all $query in Debuglevel LXDebug::QUERY
 
 435   my $callingdetails = (caller (0))[3];
 
 436   dump_query(LXDebug->QUERY(), "$callingdetails", $query, @values);
 
 438   $sth = prepare_execute_query($form, $dbh, $query, @values);
 
 443   my ($i, $j, $k, $l, $ref, $ref2);
 
 446   while (my $ref0 = $sth->fetchrow_hashref("NAME_lc")) {
 
 448     $trans_id = $ref0->{id};
 
 450     my $source = $ref0->{source};
 
 451     undef($ref0->{source});
 
 453     if ($trans_id != $trans_id2) { # first line of a booking
 
 456         push(@{ $form->{GL} }, $ref);
 
 461       $trans_id2 = $ref->{id};
 
 464       if ($ref->{type} eq "gl") {
 
 465         $ref->{module} = "gl";
 
 469       if ($ref->{type} eq "ap") {
 
 470         if ($ref->{invoice}) {
 
 471           $ref->{module} = "ir";
 
 473           $ref->{module} = "ap";
 
 478       if ($ref->{type} eq "ar") {
 
 479         if ($ref->{invoice}) {
 
 480           $ref->{module} = "is";
 
 482           $ref->{module} = "ar";
 
 486       $ref->{"projectnumbers"} = {};
 
 487       $ref->{"projectnumbers"}->{$ref->{"projectnumber"}} = 1 if ($ref->{"projectnumber"});
 
 489       $balance = $ref->{amount};
 
 491       # Linenumbers of General Ledger
 
 492       $k       = 0; # Debit      # AP      # Soll
 
 493       $l       = 0; # Credit     # AR      # Haben
 
 494       $i       = 0; # Debit Tax  # AP_tax  # VSt
 
 495       $j       = 0; # Credit Tax # AR_tax  # USt
 
 497       if ($ref->{chart_id} > 0) { # all tax accounts first line, no line increasing
 
 498         if ($ref->{amount} < 0) {
 
 499           if ($ref->{link} =~ /AR_tax/) {
 
 500             $ref->{credit_tax}{$j}       = $ref->{amount};
 
 501             $ref->{credit_tax_accno}{$j} = $ref->{accno};
 
 503           if ($ref->{link} =~ /AP_tax/) {
 
 504             $ref->{debit_tax}{$i}       = $ref->{amount} * -1;
 
 505             $ref->{debit_tax_accno}{$i} = $ref->{accno};
 
 508           if ($ref->{link} =~ /AR_tax/) {
 
 509             $ref->{credit_tax}{$j}       = $ref->{amount};
 
 510             $ref->{credit_tax_accno}{$j} = $ref->{accno};
 
 512           if ($ref->{link} =~ /AP_tax/) {
 
 513             $ref->{debit_tax}{$i}       = $ref->{amount} * -1;
 
 514             $ref->{debit_tax_accno}{$i} = $ref->{accno};
 
 517       } else { #all other accounts first line
 
 519         if ($ref->{amount} < 0) {
 
 520           $ref->{debit}{$k}        = $ref->{amount} * -1;
 
 521           $ref->{debit_accno}{$k}  = $ref->{accno};
 
 522           $ref->{debit_taxkey}{$k} = $ref->{taxkey};
 
 523           $ref->{ac_transdate}{$k} = $ref->{transdate};
 
 524           $ref->{source}{$k}       = $source;
 
 526           $ref->{credit}{$l}        = $ref->{amount} * 1;
 
 527           $ref->{credit_accno}{$l}  = $ref->{accno};
 
 528           $ref->{credit_taxkey}{$l} = $ref->{taxkey};
 
 529           $ref->{ac_transdate}{$l}  = $ref->{transdate};
 
 530           $ref->{source}{$l}        = $source;
 
 534     } else { # following lines of a booking, line increasing
 
 537 #      $trans_old = $trans_id2;   # doesn't seem to be used anymore
 
 538       $trans_id2 = $ref2->{id};
 
 541         (int($balance * 100000) + int(100000 * $ref2->{amount})) / 100000;
 
 543       $ref->{"projectnumbers"}->{$ref2->{"projectnumber"}} = 1 if ($ref2->{"projectnumber"});
 
 545       if ($ref2->{chart_id} > 0) { # all tax accounts, following lines
 
 546         if ($ref2->{amount} < 0) {
 
 547           if ($ref2->{link} =~ /AR_tax/) {
 
 548             if ($ref->{credit_tax_accno}{$j} ne "") {
 
 551             $ref->{credit_tax}{$j}       = $ref2->{amount};
 
 552             $ref->{credit_tax_accno}{$j} = $ref2->{accno};
 
 554           if ($ref2->{link} =~ /AP_tax/) {
 
 555             if ($ref->{debit_tax_accno}{$i} ne "") {
 
 558             $ref->{debit_tax}{$i}       = $ref2->{amount} * -1;
 
 559             $ref->{debit_tax_accno}{$i} = $ref2->{accno};
 
 562           if ($ref2->{link} =~ /AR_tax/) {
 
 563             if ($ref->{credit_tax_accno}{$j} ne "") {
 
 566             $ref->{credit_tax}{$j}       = $ref2->{amount};
 
 567             $ref->{credit_tax_accno}{$j} = $ref2->{accno};
 
 569           if ($ref2->{link} =~ /AP_tax/) {
 
 570             if ($ref->{debit_tax_accno}{$i} ne "") {
 
 573             $ref->{debit_tax}{$i}       = $ref2->{amount} * -1;
 
 574             $ref->{debit_tax_accno}{$i} = $ref2->{accno};
 
 577       } else { # all other accounts, following lines
 
 578         if ($ref2->{amount} < 0) {
 
 579           if ($ref->{debit_accno}{$k} ne "") {
 
 582           if ($ref->{source}{$k} ne "") {
 
 587           $ref->{debit}{$k}        = $ref2->{amount} * - 1;
 
 588           $ref->{debit_accno}{$k}  = $ref2->{accno};
 
 589           $ref->{debit_taxkey}{$k} = $ref2->{taxkey};
 
 590           $ref->{ac_transdate}{$k} = $ref2->{transdate};
 
 591           $ref->{source}{$k}       = $source . $space . $ref->{source}{$k};
 
 593           if ($ref->{credit_accno}{$l} ne "") {
 
 596           if ($ref->{source}{$l} ne "") {
 
 601           $ref->{credit}{$l}        = $ref2->{amount};
 
 602           $ref->{credit_accno}{$l}  = $ref2->{accno};
 
 603           $ref->{credit_taxkey}{$l} = $ref2->{taxkey};
 
 604           $ref->{ac_transdate}{$l}  = $ref2->{transdate};
 
 605           $ref->{source}{$l}        = $ref->{source}{$l} . $space . $source;
 
 611   push @{ $form->{GL} }, $ref;
 
 614   if ($form->{accno}) {
 
 615     $query = qq|SELECT c.description FROM chart c WHERE c.accno = ?|;
 
 616     ($form->{account_description}) = selectrow_query($form, $dbh, $query, $form->{accno});
 
 619   $main::lxdebug->leave_sub();
 
 623   my ($self, $myconfig, $form) = @_;
 
 624   $main::lxdebug->enter_sub();
 
 626   my ($query, $sth, $ref, @values);
 
 628   my $dbh = SL::DB->client->dbh;
 
 630   $query = qq|SELECT closedto, revtrans FROM defaults|;
 
 631   ($form->{closedto}, $form->{revtrans}) = selectrow_query($form, $dbh, $query);
 
 633   $query = qq|SELECT id, gldate
 
 635               WHERE id = (SELECT max(id) FROM gl)|;
 
 636   ($form->{previous_id}, $form->{previous_gldate}) = selectrow_query($form, $dbh, $query);
 
 640       qq|SELECT g.reference, g.description, g.notes, g.transdate, g.deliverydate, g.tax_point,
 
 641            g.storno, g.storno_id,
 
 642            g.department_id, d.description AS department,
 
 643            e.name AS employee, g.taxincluded, g.gldate,
 
 644          g.ob_transaction, g.cb_transaction
 
 646          LEFT JOIN department d ON (d.id = g.department_id)
 
 647          LEFT JOIN employee e ON (e.id = g.employee_id)
 
 649     $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{id}));
 
 650     map { $form->{$_} = $ref->{$_} } keys %$ref;
 
 652     # retrieve individual rows
 
 654       qq|SELECT c.accno, t.taxkey AS accnotaxkey, a.amount, a.memo, a.source,
 
 655            a.transdate, a.cleared, a.project_id, p.projectnumber,
 
 656            a.taxkey, t.rate AS taxrate, t.id, a.chart_id,
 
 658             FROM chart c1, tax t1
 
 659             WHERE (t1.id = t.id) AND (c1.id = t.chart_id)) AS taxaccno,
 
 662             WHERE (tk.chart_id = a.chart_id) AND (tk.startdate <= a.transdate)
 
 663             ORDER BY tk.startdate desc LIMIT 1) AS tax_id
 
 665          JOIN chart c ON (c.id = a.chart_id)
 
 666          LEFT JOIN project p ON (p.id = a.project_id)
 
 667          LEFT JOIN tax t ON (t.id = a.tax_id)
 
 668          WHERE (a.trans_id = ?)
 
 669            AND (a.fx_transaction = '0')
 
 670          ORDER BY a.acc_trans_id, a.transdate|;
 
 671     $form->{GL} = selectall_hashref_query($form, $dbh, $query, conv_i($form->{id}));
 
 678             WHERE id = (SELECT MAX(id) FROM gl)
 
 681     ($form->{transdate}) = selectrow_query($form, $dbh, $query);
 
 684   # get tax description
 
 685   $query = qq|SELECT * FROM tax ORDER BY taxkey|;
 
 686   $form->{TAX} = selectall_hashref_query($form, $dbh, $query);
 
 688   # get chart of accounts
 
 690     qq|SELECT c.accno, c.description, c.link, tk.taxkey_id, tk.tax_id
 
 692        LEFT JOIN taxkeys tk ON (tk.id =
 
 695           WHERE (taxkeys.chart_id = c.id)
 
 697           ORDER BY startdate DESC
 
 700   $form->{chart} = selectall_hashref_query($form, $dbh, $query, conv_date($form->{transdate}));
 
 702   $main::lxdebug->leave_sub();
 
 706   my ($self, $form, $myconfig, $id) = @_;
 
 707   $main::lxdebug->enter_sub();
 
 709   my $rc = SL::DB->client->with_transaction(\&_storno, $self, $form, $myconfig, $id);
 
 711   $::lxdebug->leave_sub;
 
 716   my ($self, $form, $myconfig, $id) = @_;
 
 718   my ($query, $new_id, $storno_row, $acc_trans_rows);
 
 719   my $dbh = SL::DB->client->dbh;
 
 721   $query = qq|SELECT nextval('glid')|;
 
 722   ($new_id) = selectrow_query($form, $dbh, $query);
 
 724   $query = qq|SELECT * FROM gl WHERE id = ?|;
 
 725   $storno_row = selectfirst_hashref_query($form, $dbh, $query, $id);
 
 727   $storno_row->{id}          = $new_id;
 
 728   $storno_row->{storno_id}   = $id;
 
 729   $storno_row->{storno}      = 't';
 
 730   $storno_row->{reference}   = 'Storno-' . $storno_row->{reference};
 
 732   $query = qq|SELECT id FROM employee WHERE login = ?|;
 
 733   my ($employee_id) = selectrow_query($form, $dbh, $query, $::myconfig{login});
 
 734   $storno_row->{employee_id} = $employee_id;
 
 736   delete @$storno_row{qw(itime mtime gldate)};
 
 738   $query = sprintf 'INSERT INTO gl (%s) VALUES (%s)', join(', ', keys %$storno_row), join(', ', map '?', values %$storno_row);
 
 739   do_query($form, $dbh, $query, (values %$storno_row));
 
 741   $query = qq|UPDATE gl SET storno = 't' WHERE id = ?|;
 
 742   do_query($form, $dbh, $query, $id);
 
 744   # now copy acc_trans entries
 
 745   $query = qq|SELECT * FROM acc_trans WHERE trans_id = ?|;
 
 746   my $rowref = selectall_hashref_query($form, $dbh, $query, $id);
 
 748   for my $row (@$rowref) {
 
 749     delete @$row{qw(itime mtime acc_trans_id gldate)};
 
 750     $query = sprintf 'INSERT INTO acc_trans (%s) VALUES (%s)', join(', ', keys %$row), join(', ', map '?', values %$row);
 
 751     $row->{trans_id}   = $new_id;
 
 752     $row->{amount}    *= -1;
 
 753     do_query($form, $dbh, $query, (values %$row));
 
 759 sub get_chart_balances {
 
 760   my ($self, @chart_ids) = @_;
 
 762   return () unless @chart_ids;
 
 764   my $placeholders = join ', ', ('?') x scalar(@chart_ids);
 
 765   my $query = qq|SELECT chart_id, SUM(amount) AS sum
 
 767                  WHERE chart_id IN (${placeholders})
 
 770   my %balances = selectall_as_map($::form, $::form->get_standard_dbh(\%::myconfig), $query, 'chart_id', 'sum', @chart_ids);
 
 775 sub get_active_taxes_for_chart {
 
 776   my ($self, $chart_id, $transdate, $tax_id) = @_;
 
 778   my $chart         = SL::DB::Chart->new(id => $chart_id)->load;
 
 779   my $active_taxkey = $chart->get_active_taxkey($transdate);
 
 781   my $where = [ chart_categories => { like => '%' . $chart->category . '%' } ];
 
 783   if ( defined $tax_id && $tax_id >= 0 ) {
 
 784     $where = [ or => [ chart_categories => { like => '%' . $chart->category . '%' },
 
 790   my $taxes         = SL::DB::Manager::Tax->get_all(
 
 792     sort_by => 'taxkey, rate',
 
 795   my $default_tax            = first { $active_taxkey->tax_id == $_->id } @{ $taxes };
 
 796   $default_tax->{is_default} = 1 if $default_tax;
 
 811 SL::GL - some useful GL functions
 
 817 =item C<get_active_taxes_for_chart> $transdate $tax_id
 
 819 Returns a list of valid taxes for a certain chart.
 
 821 If the optional param transdate exists one entry in the returning list
 
 822 may get the attribute C<is_default> for this specific tax-dependent date.
 
 823 The possible entries are filtered by the charttype of the tax, i.e. only taxes
 
 824 whose chart_categories match the category of the chart will be shown.
 
 826 In the case of existing records, e.g. when opening an old ar record, due to
 
 827 changes in the configurations the desired tax might not be available in the
 
 828 dropdown anymore. If we are loading an old record and know its tax_id (from
 
 829 acc_trans), we can pass $tax_id as the third parameter and be sure that the
 
 830 original tax always appears in the dropdown.
 
 832 The functions returns an array which may be used for building dropdowns in ar/ap/gl code.
 
 844 G. Richardson E<lt>grichardson@kivitec.de<gt>