1 package AccTransCorrections;
5 use List::Util qw(first);
17 $self->{taxkeys} = Taxkeys->new();
22 sub _fetch_transactions {
23 $main::lxdebug->enter_sub();
28 my $myconfig = \%main::myconfig;
29 my $form = $main::form;
31 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
33 my (@where, @values) = ((), ());
35 if ($params{transdate_from}) {
36 push @where, qq|at.transdate >= ?|;
37 push @values, $params{transdate_from};
40 if ($params{transdate_to}) {
41 push @where, qq|at.transdate <= ?|;
42 push @values, $params{transdate_to};
45 if ($params{trans_id}) {
46 push @where, qq|at.trans_id = ?|;
47 push @values, $params{trans_id};
52 $where = 'WHERE ' . join(' AND ', map { "($_)" } @where);
57 c.accno, c.description AS chartdescription, c.charttype, c.category AS chartcategory, c.link AS chartlink,
58 COALESCE(gl.reference, COALESCE(ap.invnumber, ar.invnumber)) AS reference,
59 COALESCE(ap.invoice, COALESCE(ar.invoice, FALSE)) AS invoice,
61 WHEN gl.id IS NOT NULL THEN gl.storno AND (gl.storno_id IS NOT NULL)
62 WHEN ap.id IS NOT NULL THEN ap.storno AND (ap.storno_id IS NOT NULL)
63 ELSE ar.storno AND (ar.storno_id IS NOT NULL)
66 WHEN gl.id IS NOT NULL THEN 'gl'
67 WHEN ap.id IS NOT NULL THEN 'ap'
72 LEFT JOIN chart c ON (at.chart_id = c.id)
73 LEFT JOIN gl ON (at.trans_id = gl.id)
74 LEFT JOIN ap ON (at.trans_id = ap.id)
75 LEFT JOIN ar ON (at.trans_id = ar.id)
77 ORDER BY at.trans_id, at.acc_trans_id
80 my @transactions = ();
81 my $last_trans = undef;
83 foreach my $entry (@{ selectall_hashref_query($form, $dbh, $query, @values) }) {
84 if (!$last_trans || ($last_trans->[0]->{trans_id} != $entry->{trans_id})) {
86 push @transactions, $last_trans;
89 push @{ $last_trans }, $entry;
92 $main::lxdebug->leave_sub();
98 $main::lxdebug->enter_sub();
103 my $transaction = $params{transaction};
104 my $callback = $params{callback};
106 my $myconfig = \%main::myconfig;
107 my $form = $main::form;
127 foreach my $entry (@{ $transaction }) {
128 $entry->{chartlinks} = { map { $_ => 1 } split(m/:/, $entry->{chartlink}) };
129 delete $entry->{chartlink};
132 # Verknüpfungen zwischen Steuerschlüsseln und zum Zeitpunkt der Transaktion
133 # gültigen Steuersätze
134 my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $transaction->[0]->{transdate});
136 my ($trans_type, $previous_non_tax_entry);
138 my $first_sub_trans = 1;
140 my $storno_mult = $transaction->[0]->{is_storno} ? -1 : 1;
142 # Aufteilung der Buchungspositionen in Soll-, Habenseite sowie
143 # getrennte Auflistung der Positionen, die auf Steuerkonten gebucht werden.
144 foreach my $entry (@{ $transaction }) {
145 if (!$first_sub_trans && ($entry->{chartlinks}->{AP_paid} || $entry->{chartlinks}->{AR_paid})) {
146 push @{ $data->{payments} }, $entry;
150 my $tax_info = $all_taxes{taxkeys}->{ $entry->{taxkey} };
152 $entry->{taxdescription} = $tax_info->{taxdescription} . ' ' . $form->format_amount($myconfig, $tax_info->{taxrate} * 100) . ' %';
155 if ($entry->{chartlinks}->{AP}) {
157 } elsif ($entry->{chartlinks}->{AR}) {
161 my $idx = 0 < ($entry->{amount} * $storno_mult) ? 'credit' : 'debit';
163 if ($entry->{chartlinks}->{AP_tax} || $entry->{chartlinks}->{AR_tax}) {
164 $data->{$idx}->{tax_sum} += $entry->{amount};
165 push @{ $data->{$idx}->{tax_entries} }, $entry;
167 if ($previous_non_tax_entry) {
168 $previous_non_tax_entry->{tax_entry} = $entry;
169 undef $previous_non_tax_entry;
173 $data->{$idx}->{sum} += $entry->{amount};
174 push @{ $data->{$idx}->{entries} }, $entry;
176 $previous_non_tax_entry = $entry;
179 $sum += $entry->{amount};
181 if (abs($sum) < 0.02) {
183 $first_sub_trans = 0;
187 # Alle Einträge entfernen, die die Gegenkonten zu Zahlungsein- und
188 # -ausgängen darstellen.
189 foreach my $payment (@{ $data->{payments} }) {
190 my $idx = 0 < $payment->{amount} ? 'debit' : 'credit';
192 foreach my $i (0 .. scalar(@{ $data->{$idx}->{entries} }) - 1) {
193 my $entry = $data->{$idx}->{entries}->[$i];
195 next if ((($payment->{amount} * -1) != $entry->{amount}) || ($payment->{transdate} ne $entry->{transdate}));
197 splice @{ $data->{$idx}->{entries} }, $i, 1;
203 delete $data->{payments};
205 map { $data->{$_}->{num} = scalar @{ $data->{$_}->{entries} } } qw(credit debit);
207 my $info = $transaction->[0];
208 my $script = ($info->{module} eq 'ar') && $info->{invoice} ? 'is'
209 : ($info->{module} eq 'ap') && $info->{invoice} ? 'ir'
214 'trans_type' => $trans_type,
215 'all_taxes' => { %all_taxes },
216 'transaction' => $transaction,
217 'full_analysis' => $params{full_analysis},
220 'link' => $script . ".pl?action=edit${callback}&id=" . $info->{trans_id},
224 $main::lxdebug->leave_sub();
229 sub _group_sub_transactions {
230 $main::lxdebug->enter_sub();
233 my $transaction = shift;
235 my @sub_transactions = ();
238 foreach my $i (0 .. scalar(@{ $transaction }) - 1) {
239 my $entry = $transaction->[$i];
241 if (abs($sum) <= 0.01) {
242 push @sub_transactions, [];
245 $sum += $entry->{amount};
247 push @{ $sub_transactions[-1] }, $entry;
250 $main::lxdebug->leave_sub();
252 return @sub_transactions;
255 # Problemfall: Verkaufsrechnungen, bei denen Buchungen auf Warenbestandskonten
256 # mit Steuerschlüssel != 0 durchgeführt wurden. Richtig wäre, dass alle
257 # Steuerschlüssel für solche Warenbestandsbuchungen 0 sind.
258 sub _check_trans_invoices_inventory_with_taxkeys {
259 $main::lxdebug->enter_sub();
264 if (!$params{transaction}->[0]->{invoice}) {
265 $main::lxdebug->leave_sub();
269 my @sub_transactions = $self->_group_sub_transactions($params{transaction});
271 foreach my $sub_transaction (@sub_transactions) {
272 my $is_cogs = first { $_->{chartlinks}->{IC_cogs} } @{ $sub_transaction };
273 next unless ($is_cogs);
275 my $needs_fixing = first { $_->{taxkey} != 0 } @{ $sub_transaction };
276 next unless ($needs_fixing);
278 $params{problem}->{type} = 'invoice_inventory_with_taxkeys';
279 push @{ $self->{invoice_inventory_taxkey_problems} }, $params{problem};
281 $main::lxdebug->leave_sub();
286 $main::lxdebug->leave_sub();
291 # Problemfall: Kreditorenbuchungen, bei denen mit Umsatzsteuerschlüsseln
292 # gebucht wurde und Debitorenbuchungen, bei denen mit Vorsteuerschlüsseln
294 sub _check_trans_ap_ar_wrong_taxkeys {
295 $main::lxdebug->enter_sub();
302 if (!$params{transaction}->[0]->{invoice}
303 && (( ($params{transaction}->[0]->{module} eq 'ap')
304 && (first { my $taxkey = $_->{taxkey}; first { $taxkey == $_ } (2, 3, 12, 13) } @{ $params{transaction} }))
306 ( ($params{transaction}->[0]->{module} eq 'ar')
307 && (first { my $taxkey = $_->{taxkey}; first { $taxkey == $_ } (8, 9, 18, 19) } @{ $params{transaction} })))) {
308 $params{problem}->{type} = 'ap_ar_wrong_taxkeys';
309 push @{ $self->{ap_ar_taxkey_problems} }, $params{problem};
314 $main::lxdebug->leave_sub();
319 # Problemfall: Splitbuchungen, die mehrere Haben- und Sollkonten ansprechen.
320 # Aber nur für Debitoren- und Kreditorenbuchungen, weil das bei Einkaufs- und
321 # Verkaufsrechnungen hingegen völlig normal ist.
322 sub _check_trans_split_multiple_credit_and_debit {
323 $main::lxdebug->enter_sub();
330 if ( !$params{transaction}->[0]->{invoice}
331 && (1 < $params{data}->{credit}->{num})
332 && (1 < $params{data}->{debit}->{num})) {
333 $params{problem}->{type} = 'split_multiple_credit_and_debit';
334 push @{ $self->{problems} }, $params{problem};
339 $main::lxdebug->leave_sub();
344 # Problemfall: Buchungen, bei denen Steuersummen nicht mit den Summen
345 # übereinstimmen, die nach ausgewähltem Steuerschlüssel hätten auftreten müssen.
346 sub _check_trans_wrong_taxkeys {
347 $main::lxdebug->enter_sub();
352 my $form = $main::form;
354 my %data = %{ $params{data} };
355 my $transaction = $params{transaction};
357 if ( $transaction->[0]->{invoice}
358 || $transaction->[0]->{ob_transaction}
359 || $transaction->[0]->{cb_transaction}
360 || (!scalar @{ $data{credit}->{entries} } && !scalar @{ $data{debit}->{entries} })
361 || ( ($transaction->[0]->{module} eq 'gl')
362 && (!scalar @{ $data{credit}->{entries} } || !scalar @{ $data{debit}->{entries} }))) {
363 $main::lxdebug->leave_sub();
369 my ($side, $other_side);
370 if ( (grep { $_->{taxkey} * 1 } @{ $data{credit}->{entries} })
371 || (scalar @{ $data{credit}->{tax_entries} })) {
373 $other_side = 'debit';
375 } elsif ( (grep { $_->{taxkey} * 1 } @{ $data{debit}->{entries} })
376 || (scalar @{ $data{debit}->{tax_entries} })) {
378 $other_side = 'credit';
382 $main::lxdebug->leave_sub();
386 my $expected_tax = 0;
387 my %num_entries_per_chart = ();
388 my $num_taxed_entries = 0;
390 foreach my $entry (@{ $data{$side}->{entries} }) {
391 my $taxinfo = $params{all_taxes}->{taxkeys}->{$entry->{taxkey}} || { };
392 $entry->{expected_tax} = $entry->{amount} * $taxinfo->{taxrate};
393 $expected_tax += $entry->{expected_tax};
395 $num_taxed_entries++ if ($taxinfo->{taxrate} * 1);
397 my $chart_key = $entry->{chart_id} . "-" . $entry->{taxkey};
398 $num_entries_per_chart{$chart_key} ||= 0;
399 $num_entries_per_chart{$chart_key}++;
402 # $main::lxdebug->message(0, "side $side trans_id $transaction->[0]->{trans_id} expected tax $expected_tax actual tax $data{$side}->{tax_sum}");
404 if (abs($expected_tax - $data{$side}->{tax_sum}) >= (0.01 * ($num_taxed_entries + 1))) {
405 if ($params{full_analysis}) {
406 my $storno_mult = $data{$side}->{entries}->[0]->{is_storno} ? -1 : 1;
408 foreach my $entry (@{ $data{$other_side}->{entries} }) {
409 $entry->{display_amount} = $form->round_amount(abs($entry->{amount}) * $storno_mult, 2);
412 foreach my $entry (@{ $data{$side}->{entries} }) {
413 $entry->{actual_tax} = $form->round_amount(abs($entry->{tax_entry} ? $entry->{tax_entry}->{amount} : 0), 2);
414 $entry->{expected_tax} = $form->round_amount(abs($entry->{expected_tax}), 2);
415 $entry->{taxkey_error} = ( $entry->{taxkey} && !$entry->{tax_entry})
416 || (!$entry->{taxkey} && $entry->{tax_entry})
417 || (abs($entry->{expected_tax} - $entry->{actual_tax}) >= 0.02);
418 $entry->{tax_entry_acc_trans_id} = $entry->{tax_entry}->{acc_trans_id};
419 delete $entry->{tax_entry};
421 $entry->{display_amount} = $form->round_amount(abs($entry->{amount}) * $storno_mult, 2);
422 $entry->{display_actual_tax} = $entry->{actual_tax} * $storno_mult;
423 $entry->{display_expected_tax} = $entry->{expected_tax} * $storno_mult;
425 if ($entry->{taxkey_error}) {
426 $self->{negative_taxkey_filter} ||= {
427 'ar' => { map { $_ => 1 } ( 8, 9, 18, 19) },
428 'ap' => { map { $_ => 1 } (1, 2, 3, 12, 13) },
432 $entry->{correct_taxkeys} = [];
434 my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $entry->{transdate});
436 foreach my $taxkey (sort { $a <=> $b } keys %{ $all_taxes{taxkeys} }) {
437 next if ($self->{negative_taxkey_filter}->{ $entry->{module} }->{$taxkey});
439 my $tax_info = $all_taxes{taxkeys}->{$taxkey};
441 next if ((!$tax_info || (0 == $tax_info->{taxrate} * 1)) && $entry->{tax_entry_acc_trans_id});
443 push @{ $entry->{correct_taxkeys} }, {
445 'tax' => $form->round_amount(abs($entry->{amount}) * $tax_info->{taxrate}, 2),
446 'description' => sprintf("%s %d%%", $tax_info->{taxdescription}, int($tax_info->{taxrate} * 100)),
453 if (first { $_ > 1 } values %num_entries_per_chart) {
454 $params{problem}->{type} = 'wrong_taxkeys';
456 $params{problem}->{type} = 'wrong_taxes';
459 $params{problem}->{acc_trans} = { %data };
460 push @{ $self->{problems} }, $params{problem};
465 $main::lxdebug->leave_sub();
470 # Inaktiver Code für das Erraten möglicher Verteilungen von
471 # Steuerschlüsseln. Deaktiviert, weil er exponentiell Zeit
474 # if (abs($expected_tax - $data{$side}->{tax_sum}) >= 0.02) {
475 # my @potential_taxkeys = $trans_type eq 'AP' ? (0, 8, 9) : (0, 1, 2, 3);
477 # $main::lxdebug->dump(0, "pota", \@potential_taxkeys);
479 # # Über alle Kombinationen aus Buchungssätzen und potenziellen Steuerschlüsseln
480 # # iterieren und jeweils die Summe ermitteln.
481 # my $num_entries = scalar @{ $data{$side}->{entries} };
482 # my @taxkey_indices = (0) x $num_entries;
484 # my @solutions = ();
486 # my $start_time = time();
488 # $main::lxdebug->message(0, "num_entries $num_entries");
490 # while ($num_entries == scalar @taxkey_indices) {
491 # my @tax_cache = ();
493 # # Berechnen der Steuersumme für die aktuell angenommenen Steuerschlüssel.
495 # foreach my $i (0 .. $num_entries - 1) {
496 # my $taxkey = $potential_taxkeys[$taxkey_indices[$i]];
497 # my $entry = $data{$side}->{entries}->[$i];
498 # my $taxinfo = $all_taxes{taxkeys}->{ $taxkey } || { };
499 # $tax_cache[$i] = $entry->{amount} * $taxinfo->{taxrate};
500 # $tax_sum += $tax_cache[$i];
503 # # Entspricht die Steuersumme mit den aktuell angenommenen Steuerschlüsseln
504 # # der verbuchten Steuersumme? Wenn ja, dann ist das eine potenzielle
506 # if (abs($tax_sum - $data{$side}->{tax_sum}) < 0.02) {
508 # 'taxkeys' => [ @potential_taxkeys[@taxkey_indices] ],
509 # 'taxes' => [ @tax_cache ],
513 # # Weiterzählen der Steuerschlüsselindices zum Interieren über
514 # # alle möglichen Kombinationen.
517 # $taxkey_indices[$i]++;
518 # last if ($taxkey_indices[$i] < scalar @potential_taxkeys);
520 # $taxkey_indices[$i] = 0;
525 # if (($now - $start_time) >= 5) {
526 # $main::lxdebug->message(0, " " . join("", @taxkey_indices));
527 # $start_time = $now;
531 # foreach my $solution (@solutions) {
532 # $solution->{rows} = [];
533 # $solution->{changes} = [];
536 # foreach my $i (0 .. $num_entries - 1) {
537 # if ($solution->{taxes}->[$i]) {
538 # my $tax_rounded = $form->round_amount($solution->{taxes}->[$i] + $error, 2);
539 # $error = $solution->{taxes}->[$i] + $error - $tax_rounded;
540 # $solution->{taxes}->[$i] = $tax_rounded;
543 # my $entry = $data{$side}->{entries}->[$i];
544 # my $tax_entry = $all_taxes{taxkeys}->{ $solution->{taxkeys}->[$i] };
546 # push @{ $solution->{rows} }, {
549 # 'taxamount' => $solution->{taxes}->[$i],
552 # $solution->{rows}->[$i]->{taxdescription} .= ' ' . $form->format_amount(\%myconfig, $tax_entry->{taxrate} * 100) . ' %';
554 # push @{ $solution->{changes} }, {
555 # 'acc_trans_id' => $entry->{acc_trans_id},
556 # 'taxkey' => $solution->{taxkeys}->[$i],
560 # push @{ $solution->{rows} }, @{ $data{$other_side}->{entries} };
562 # delete @{ $solution }{ qw(taxes taxkeys) };
565 # $problem->{type} = 'wrong_taxkeys';
566 # $problem->{solutions} = [ @solutions ];
567 # $problem->{acc_trans} = { %data };
568 # push @problems, $problem;
574 $main::lxdebug->enter_sub();
579 my $myconfig = \%main::myconfig;
580 my $form = $main::form;
582 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
584 my @transactions = $self->_fetch_transactions(%params, 'dbh' => $dbh);
586 if (!scalar @transactions) {
587 $main::lxdebug->leave_sub();
591 my $callback = $params{callback} ? '&callback=' . $params{callback} : '';
593 $self->{problems} = [];
594 $self->{ap_ar_taxkey_problems} = [];
595 $self->{invoice_inventory_taxkey_problems} = [];
597 foreach my $transaction (@transactions) {
598 my %common_args = $self->_prepare_data('transaction' => $transaction, 'callback' => $callback, 'full_analysis' => $params{full_analysis});
600 next if ($self->_check_trans_ap_ar_wrong_taxkeys(%common_args));
601 next if ($self->_check_trans_invoices_inventory_with_taxkeys(%common_args));
602 next if ($self->_check_trans_split_multiple_credit_and_debit(%common_args));
603 next if ($self->_check_trans_wrong_taxkeys(%common_args));
606 my @problems = @{ $self->{problems} };
608 if (0 != scalar @{ $self->{ap_ar_taxkey_problems} }) {
610 'type' => 'ap_ar_wrong_taxkeys',
611 'ap_problems' => [ grep { $_->{data}->{module} eq 'ap' } @{ $self->{ap_ar_taxkey_problems} } ],
612 'ar_problems' => [ grep { $_->{data}->{module} eq 'ar' } @{ $self->{ap_ar_taxkey_problems} } ],
614 unshift @problems, $problem;
617 if (0 != scalar @{ $self->{invoice_inventory_taxkey_problems} }) {
619 'type' => 'invoice_inventory_with_taxkeys',
620 'ap_problems' => [ grep { $_->{data}->{module} eq 'ap' } @{ $self->{invoice_inventory_taxkey_problems} } ],
621 'ar_problems' => [ grep { $_->{data}->{module} eq 'ar' } @{ $self->{invoice_inventory_taxkey_problems} } ],
623 unshift @problems, $problem;
626 $main::lxdebug->leave_sub();
632 sub fix_ap_ar_wrong_taxkeys {
633 $main::lxdebug->enter_sub();
638 my $myconfig = \%main::myconfig;
639 my $form = $main::form;
641 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
643 my $query = qq|SELECT 'ap' AS module,
644 at.acc_trans_id, at.trans_id, at.chart_id, at.amount, at.taxkey, at.transdate,
647 LEFT JOIN chart c ON (at.chart_id = c.id)
648 WHERE (trans_id IN (SELECT id FROM ap WHERE NOT invoice))
649 AND (taxkey IN (2, 3, 12, 13))
653 SELECT 'ar' AS module,
654 at.acc_trans_id, at.trans_id, at.chart_id, at.amount, at.taxkey, at.transdate,
657 LEFT JOIN chart c ON (at.chart_id = c.id)
658 WHERE (trans_id IN (SELECT id FROM ar WHERE NOT invoice))
659 AND (taxkey IN (8, 9, 18, 19))
661 ORDER BY trans_id, acc_trans_id|;
663 my $sth = prepare_execute_query($form, $dbh, $query);
666 while (my $ref = $sth->fetchrow_hashref()) {
667 if ((!scalar @transactions) || ($ref->{trans_id} != $transactions[-1]->[0]->{trans_id})) {
668 push @transactions, [];
671 push @{ $transactions[-1] }, $ref;
676 @transactions = grep { (scalar(@transactions) % 2) == 0 } @transactions;
678 my %taxkey_replacements = (
690 'ap' => { map { $_ => 1 } (2, 3, 12, 13) },
691 'ar' => { map { $_ => 1 } (8, 9, 18, 19) },
694 my @corrections = ();
696 foreach my $transaction (@transactions) {
698 for (my $i = 0; $i < scalar @{ $transaction }; $i += 2) {
699 my ($non_tax_idx, $tax_idx) = abs($transaction->[$i]->{amount}) > abs($transaction->[$i + 1]->{amount}) ? ($i, $i + 1) : ($i + 1, $i);
700 my ($non_tax, $tax) = @{ $transaction }[$non_tax_idx, $tax_idx];
702 last if ($non_tax->{link} =~ m/(:?AP|AR)_tax(:?$|:)/);
703 last if ($tax->{link} !~ m/(:?AP|AR)_tax(:?$|:)/);
705 next if (!$bad_taxkeys{ $non_tax->{module} }->{ $non_tax->{taxkey} });
707 my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $non_tax->{transdate});
709 push @corrections, ({ 'acc_trans_id' => $non_tax->{acc_trans_id},
710 'taxkey' => $taxkey_replacements{$non_tax->{taxkey}},
713 'acc_trans_id' => $tax->{acc_trans_id},
714 'taxkey' => $taxkey_replacements{$non_tax->{taxkey}},
715 'chart_id' => $all_taxes{taxkeys}->{ $taxkey_replacements{$non_tax->{taxkey}} }->{taxchart_id},
720 if (scalar @corrections) {
721 my $q_taxkey_only = qq|UPDATE acc_trans SET taxkey = ? WHERE acc_trans_id = ?|;
722 my $h_taxkey_only = prepare_query($form, $dbh, $q_taxkey_only);
724 my $q_taxkey_chart_id = qq|UPDATE acc_trans SET taxkey = ?, chart_id = ? WHERE acc_trans_id = ?|;
725 my $h_taxkey_chart_id = prepare_query($form, $dbh, $q_taxkey_chart_id);
727 foreach my $entry (@corrections) {
728 if ($entry->{chart_id}) {
729 do_statement($form, $h_taxkey_chart_id, $q_taxkey_chart_id, $entry->{taxkey}, $entry->{chart_id}, $entry->{acc_trans_id});
731 do_statement($form, $h_taxkey_only, $q_taxkey_only, $entry->{taxkey}, $entry->{acc_trans_id});
735 $h_taxkey_only->finish();
736 $h_taxkey_chart_id->finish();
738 $dbh->commit() unless ($params{dbh});
741 $main::lxdebug->leave_sub();
744 sub fix_invoice_inventory_with_taxkeys {
745 $main::lxdebug->enter_sub();
750 my $myconfig = \%main::myconfig;
751 my $form = $main::form;
753 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
755 my $query = qq|SELECT at.*, c.link
757 LEFT JOIN ar ON (at.trans_id = ar.id)
758 LEFT JOIN chart c ON (at.chart_id = c.id)
765 LEFT JOIN ap ON (at.trans_id = ap.id)
766 LEFT JOIN chart c ON (at.chart_id = c.id)
769 ORDER BY trans_id, acc_trans_id|;
771 my $sth = prepare_execute_query($form, $dbh, $query);
774 while (my $ref = $sth->fetchrow_hashref()) {
775 if ((!scalar @transactions) || ($ref->{trans_id} != $transactions[-1]->[0]->{trans_id})) {
776 push @transactions, [];
779 push @{ $transactions[-1] }, $ref;
784 my @corrections = ();
786 foreach my $transaction (@transactions) {
787 my @sub_transactions = $self->_group_sub_transactions($transaction);
789 foreach my $sub_transaction (@sub_transactions) {
790 my $is_cogs = first { $_->{link} =~ m/IC_cogs/ } @{ $sub_transaction };
791 next unless ($is_cogs);
793 foreach my $entry (@{ $sub_transaction }) {
794 next if ($entry->{taxkey} == 0);
795 push @corrections, $entry->{acc_trans_id};
801 $query = qq|UPDATE acc_trans SET taxkey = 0 WHERE acc_trans_id = ?|;
802 $sth = prepare_query($form, $dbh, $query);
804 foreach my $acc_trans_id (@corrections) {
805 do_statement($form, $sth, $query, $acc_trans_id);
810 $dbh->commit() unless ($params{dbh});
814 $main::lxdebug->leave_sub();
817 sub fix_wrong_taxkeys {
818 $main::lxdebug->enter_sub();
823 Common::check_params(\%params, qw(fixes));
825 my $myconfig = \%main::myconfig;
826 my $form = $main::form;
828 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
830 my $q_taxkey_only = qq|UPDATE acc_trans SET taxkey = ? WHERE acc_trans_id = ?|;
831 my $h_taxkey_only = prepare_query($form, $dbh, $q_taxkey_only);
833 my $q_taxkey_chart = qq|UPDATE acc_trans SET taxkey = ?, chart_id = ? WHERE acc_trans_id = ?|;
834 my $h_taxkey_chart = prepare_query($form, $dbh, $q_taxkey_chart);
836 my $q_transdate = qq|SELECT transdate FROM acc_trans WHERE acc_trans_id = ?|;
837 my $h_transdate = prepare_query($form, $dbh, $q_transdate);
839 foreach my $fix (@{ $params{fixes} }) {
840 next unless ($fix->{acc_trans_id});
842 do_statement($form, $h_taxkey_only, $q_taxkey_only, conv_i($fix->{taxkey}), conv_i($fix->{acc_trans_id}));
844 next unless ($fix->{tax_entry_acc_trans_id});
846 do_statement($form, $h_transdate, $q_transdate, conv_i($fix->{tax_entry_acc_trans_id}));
847 my ($transdate) = $h_transdate->fetchrow_array();
849 my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $transdate);
850 my $tax_info = $all_taxes{taxkeys}->{ $fix->{taxkey} };
852 next unless ($tax_info);
854 do_statement($form, $h_taxkey_chart, $q_taxkey_chart, conv_i($fix->{taxkey}), conv_i($tax_info->{taxchart_id}), conv_i($fix->{tax_entry_acc_trans_id}));
857 $h_taxkey_only->finish();
858 $h_taxkey_chart->finish();
859 $h_transdate->finish();
862 $dbh->commit() unless ($params{dbh});
864 $main::lxdebug->leave_sub();
867 sub delete_transaction {
868 $main::lxdebug->enter_sub();
873 Common::check_params(\%params, qw(trans_id));
875 my $myconfig = \%main::myconfig;
876 my $form = $main::form;
878 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
880 do_query($form, $dbh, qq|UPDATE ar SET storno_id = NULL WHERE storno_id = ?|, conv_i($params{trans_id}));
881 do_query($form, $dbh, qq|UPDATE ap SET storno_id = NULL WHERE storno_id = ?|, conv_i($params{trans_id}));
882 do_query($form, $dbh, qq|UPDATE gl SET storno_id = NULL WHERE storno_id = ?|, conv_i($params{trans_id}));
884 do_query($form, $dbh, qq|DELETE FROM ar WHERE id = ?|, conv_i($params{trans_id}));
885 do_query($form, $dbh, qq|DELETE FROM ap WHERE id = ?|, conv_i($params{trans_id}));
886 do_query($form, $dbh, qq|DELETE FROM gl WHERE id = ?|, conv_i($params{trans_id}));
887 do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE trans_id = ?|, conv_i($params{trans_id}));
890 $dbh->commit() unless ($params{dbh});
892 $main::lxdebug->leave_sub();