1 package AccTransCorrections;
6 use List::Util qw(first);
18 $self->{taxkeys} = Taxkeys->new();
23 sub _fetch_transactions {
24 $main::lxdebug->enter_sub();
29 my $myconfig = \%main::myconfig;
30 my $form = $main::form;
32 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
34 my (@where, @values) = ((), ());
36 if ($params{transdate_from}) {
37 push @where, qq|at.transdate >= ?|;
38 push @values, $params{transdate_from};
41 if ($params{transdate_to}) {
42 push @where, qq|at.transdate <= ?|;
43 push @values, $params{transdate_to};
46 if ($params{trans_id}) {
47 push @where, qq|at.trans_id = ?|;
48 push @values, $params{trans_id};
53 $where = 'WHERE ' . join(' AND ', map { "($_)" } @where);
58 c.accno, c.description AS chartdescription, c.charttype, c.category AS chartcategory, c.link AS chartlink,
59 COALESCE(gl.reference, COALESCE(ap.invnumber, ar.invnumber)) AS reference,
60 COALESCE(ap.invoice, COALESCE(ar.invoice, FALSE)) AS invoice,
62 WHEN gl.id IS NOT NULL THEN gl.storno AND (gl.storno_id IS NOT NULL)
63 WHEN ap.id IS NOT NULL THEN ap.storno AND (ap.storno_id IS NOT NULL)
64 ELSE ar.storno AND (ar.storno_id IS NOT NULL)
67 WHEN gl.id IS NOT NULL THEN 'gl'
68 WHEN ap.id IS NOT NULL THEN 'ap'
73 LEFT JOIN chart c ON (at.chart_id = c.id)
74 LEFT JOIN gl ON (at.trans_id = gl.id)
75 LEFT JOIN ap ON (at.trans_id = ap.id)
76 LEFT JOIN ar ON (at.trans_id = ar.id)
78 ORDER BY at.trans_id, at.acc_trans_id
81 my @transactions = ();
82 my $last_trans = undef;
84 foreach my $entry (@{ selectall_hashref_query($form, $dbh, $query, @values) }) {
85 if (!$last_trans || ($last_trans->[0]->{trans_id} != $entry->{trans_id})) {
87 push @transactions, $last_trans;
90 push @{ $last_trans }, $entry;
93 $main::lxdebug->leave_sub();
99 $main::lxdebug->enter_sub();
104 my $transaction = $params{transaction};
105 my $callback = $params{callback};
107 my $myconfig = \%main::myconfig;
108 my $form = $main::form;
128 foreach my $entry (@{ $transaction }) {
129 $entry->{chartlinks} = { map { $_ => 1 } split(m/:/, $entry->{chartlink}) };
130 delete $entry->{chartlink};
133 # Verknüpfungen zwischen Steuerschlüsseln und zum Zeitpunkt der Transaktion
134 # gültigen Steuersätze
135 my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $transaction->[0]->{transdate});
137 my ($trans_type, $previous_non_tax_entry);
139 my $first_sub_trans = 1;
141 my $storno_mult = $transaction->[0]->{is_storno} ? -1 : 1;
143 # Aufteilung der Buchungspositionen in Soll-, Habenseite sowie
144 # getrennte Auflistung der Positionen, die auf Steuerkonten gebucht werden.
145 foreach my $entry (@{ $transaction }) {
146 if (!$first_sub_trans && ($entry->{chartlinks}->{AP_paid} || $entry->{chartlinks}->{AR_paid})) {
147 push @{ $data->{payments} }, $entry;
151 my $tax_info = $all_taxes{taxkeys}->{ $entry->{taxkey} };
153 $entry->{taxdescription} = $tax_info->{taxdescription} . ' ' . $form->format_amount($myconfig, $tax_info->{taxrate} * 100) . ' %';
156 if ($entry->{chartlinks}->{AP}) {
158 } elsif ($entry->{chartlinks}->{AR}) {
162 my $idx = 0 < ($entry->{amount} * $storno_mult) ? 'credit' : 'debit';
164 if ($entry->{chartlinks}->{AP_tax} || $entry->{chartlinks}->{AR_tax}) {
165 $data->{$idx}->{tax_sum} += $entry->{amount};
166 push @{ $data->{$idx}->{tax_entries} }, $entry;
168 if ($previous_non_tax_entry) {
169 $previous_non_tax_entry->{tax_entry} = $entry;
170 undef $previous_non_tax_entry;
174 $data->{$idx}->{sum} += $entry->{amount};
175 push @{ $data->{$idx}->{entries} }, $entry;
177 $previous_non_tax_entry = $entry;
180 $sum += $entry->{amount};
182 if (abs($sum) < 0.02) {
184 $first_sub_trans = 0;
188 # Alle Einträge entfernen, die die Gegenkonten zu Zahlungsein- und
189 # -ausgängen darstellen.
190 foreach my $payment (@{ $data->{payments} }) {
191 my $idx = 0 < $payment->{amount} ? 'debit' : 'credit';
193 foreach my $i (0 .. scalar(@{ $data->{$idx}->{entries} }) - 1) {
194 my $entry = $data->{$idx}->{entries}->[$i];
196 next if ((($payment->{amount} * -1) != $entry->{amount}) || ($payment->{transdate} ne $entry->{transdate}));
198 splice @{ $data->{$idx}->{entries} }, $i, 1;
204 delete $data->{payments};
206 map { $data->{$_}->{num} = scalar @{ $data->{$_}->{entries} } } qw(credit debit);
208 my $info = $transaction->[0];
209 my $script = ($info->{module} eq 'ar') && $info->{invoice} ? 'is'
210 : ($info->{module} eq 'ap') && $info->{invoice} ? 'ir'
215 'trans_type' => $trans_type,
216 'all_taxes' => { %all_taxes },
217 'transaction' => $transaction,
218 'full_analysis' => $params{full_analysis},
221 'link' => $script . ".pl?action=edit${callback}&id=" . $info->{trans_id},
225 $main::lxdebug->leave_sub();
230 sub _group_sub_transactions {
231 $main::lxdebug->enter_sub();
234 my $transaction = shift;
236 my @sub_transactions = ();
239 foreach my $i (0 .. scalar(@{ $transaction }) - 1) {
240 my $entry = $transaction->[$i];
242 if (abs($sum) <= 0.01) {
243 push @sub_transactions, [];
246 $sum += $entry->{amount};
248 push @{ $sub_transactions[-1] }, $entry;
251 $main::lxdebug->leave_sub();
253 return @sub_transactions;
256 # Problemfall: Verkaufsrechnungen, bei denen Buchungen auf Warenbestandskonten
257 # mit Steuerschlüssel != 0 durchgeführt wurden. Richtig wäre, dass alle
258 # Steuerschlüssel für solche Warenbestandsbuchungen 0 sind.
259 sub _check_trans_invoices_inventory_with_taxkeys {
260 $main::lxdebug->enter_sub();
265 if (!$params{transaction}->[0]->{invoice}) {
266 $main::lxdebug->leave_sub();
270 my @sub_transactions = $self->_group_sub_transactions($params{transaction});
272 foreach my $sub_transaction (@sub_transactions) {
273 my $is_cogs = first { $_->{chartlinks}->{IC_cogs} } @{ $sub_transaction };
274 next unless ($is_cogs);
276 my $needs_fixing = first { $_->{taxkey} != 0 } @{ $sub_transaction };
277 next unless ($needs_fixing);
279 $params{problem}->{type} = 'invoice_inventory_with_taxkeys';
280 push @{ $self->{invoice_inventory_taxkey_problems} }, $params{problem};
282 $main::lxdebug->leave_sub();
287 $main::lxdebug->leave_sub();
292 # Problemfall: Verkaufsrechnungen, bei denen Steuern verbucht wurden, obwohl
293 # kein Steuerschlüssel eingetragen ist.
294 sub _check_missing_taxkeys_in_invoices {
295 $::lxdebug->enter_sub;
299 my $transaction = $params{transaction};
300 my $found_broken = 0;
302 $::lxdebug->leave_sub and return 0
303 if !$transaction->[0]->{invoice};
305 my @sub_transactions = $self->_group_sub_transactions($transaction);
307 for my $sub_transaction (@sub_transactions) {
308 $::lxdebug->leave_sub and return 0
309 if _is_split_transaction($sub_transaction)
310 || _is_simple_transaction($sub_transaction);
312 my $split_side_entries = _get_splitted_side($sub_transaction);
315 for my $entry (@{ $split_side_entries }) {
316 my $is_tax = grep { m/(?:AP_tax|AR_tax)/ } keys %{ $entry->{chartlinks} };
318 $num_tax_rows++ if $is_tax;
319 $num_taxed_rows++ if !$is_tax && $entry->{tax_key} != 0;
322 # now if this has tax rows but NO taxed rows, something is wrong.
323 if ($num_tax_rows > 0 && $num_taxed_rows == 0) {
324 $params{problem}->{type} = 'missing_taxkeys_in_invoices';
325 push @{ $self->{missing_taxkeys_in_invoices} ||= [] }, $params{problem};
330 $::lxdebug->leave_sub;
332 return $found_broken;
335 # Problemfall: Kreditorenbuchungen, bei denen mit Umsatzsteuerschlüsseln
336 # gebucht wurde und Debitorenbuchungen, bei denen mit Vorsteuerschlüsseln
338 sub _check_trans_ap_ar_wrong_taxkeys {
339 $main::lxdebug->enter_sub();
346 if (!$params{transaction}->[0]->{invoice}
347 && (( ($params{transaction}->[0]->{module} eq 'ap')
348 && (first { my $taxkey = $_->{taxkey}; first { $taxkey == $_ } (2, 3, 12, 13) } @{ $params{transaction} }))
350 ( ($params{transaction}->[0]->{module} eq 'ar')
351 && (first { my $taxkey = $_->{taxkey}; first { $taxkey == $_ } (8, 9, 18, 19) } @{ $params{transaction} })))) {
352 $params{problem}->{type} = 'ap_ar_wrong_taxkeys';
353 push @{ $self->{ap_ar_taxkey_problems} }, $params{problem};
358 $main::lxdebug->leave_sub();
363 # Problemfall: Splitbuchungen, die mehrere Haben- und Sollkonten ansprechen.
364 # Aber nur für Debitoren- und Kreditorenbuchungen, weil das bei Einkaufs- und
365 # Verkaufsrechnungen hingegen völlig normal ist.
366 sub _check_trans_split_multiple_credit_and_debit {
367 $main::lxdebug->enter_sub();
374 if ( !$params{transaction}->[0]->{invoice}
375 && (1 < $params{data}->{credit}->{num})
376 && (1 < $params{data}->{debit}->{num})) {
377 $params{problem}->{type} = 'split_multiple_credit_and_debit';
378 push @{ $self->{problems} }, $params{problem};
383 $main::lxdebug->leave_sub();
388 # Problemfall: Buchungen, bei denen Steuersummen nicht mit den Summen
389 # übereinstimmen, die nach ausgewähltem Steuerschlüssel hätten auftreten müssen.
390 sub _check_trans_wrong_taxkeys {
391 $main::lxdebug->enter_sub();
396 my $form = $main::form;
398 my %data = %{ $params{data} };
399 my $transaction = $params{transaction};
401 if ( $transaction->[0]->{invoice}
402 || $transaction->[0]->{ob_transaction}
403 || $transaction->[0]->{cb_transaction}
404 || (!scalar @{ $data{credit}->{entries} } && !scalar @{ $data{debit}->{entries} })
405 || ( ($transaction->[0]->{module} eq 'gl')
406 && (!scalar @{ $data{credit}->{entries} } || !scalar @{ $data{debit}->{entries} }))) {
407 $main::lxdebug->leave_sub();
413 my ($side, $other_side);
414 if ( (grep { $_->{taxkey} * 1 } @{ $data{credit}->{entries} })
415 || (scalar @{ $data{credit}->{tax_entries} })) {
417 $other_side = 'debit';
419 } elsif ( (grep { $_->{taxkey} * 1 } @{ $data{debit}->{entries} })
420 || (scalar @{ $data{debit}->{tax_entries} })) {
422 $other_side = 'credit';
426 $main::lxdebug->leave_sub();
430 my $expected_tax = 0;
431 my %num_entries_per_chart = ();
432 my $num_taxed_entries = 0;
434 foreach my $entry (@{ $data{$side}->{entries} }) {
435 my $taxinfo = $params{all_taxes}->{taxkeys}->{$entry->{taxkey}} || { };
436 $entry->{expected_tax} = $entry->{amount} * $taxinfo->{taxrate};
437 $expected_tax += $entry->{expected_tax};
439 $num_taxed_entries++ if ($taxinfo->{taxrate} * 1);
441 my $chart_key = $entry->{chart_id} . "-" . $entry->{taxkey};
442 $num_entries_per_chart{$chart_key} ||= 0;
443 $num_entries_per_chart{$chart_key}++;
446 # $main::lxdebug->message(0, "side $side trans_id $transaction->[0]->{trans_id} expected tax $expected_tax actual tax $data{$side}->{tax_sum}");
448 if (abs($expected_tax - $data{$side}->{tax_sum}) >= (0.01 * ($num_taxed_entries + 1))) {
449 if ($params{full_analysis}) {
450 my $storno_mult = $data{$side}->{entries}->[0]->{is_storno} ? -1 : 1;
452 foreach my $entry (@{ $data{$other_side}->{entries} }) {
453 $entry->{display_amount} = $form->round_amount(abs($entry->{amount}) * $storno_mult, 2);
456 foreach my $entry (@{ $data{$side}->{entries} }) {
457 $entry->{actual_tax} = $form->round_amount(abs($entry->{tax_entry} ? $entry->{tax_entry}->{amount} : 0), 2);
458 $entry->{expected_tax} = $form->round_amount(abs($entry->{expected_tax}), 2);
459 $entry->{taxkey_error} = ( $entry->{taxkey} && !$entry->{tax_entry})
460 || (!$entry->{taxkey} && $entry->{tax_entry})
461 || (abs($entry->{expected_tax} - $entry->{actual_tax}) >= 0.02);
462 $entry->{tax_entry_acc_trans_id} = $entry->{tax_entry}->{acc_trans_id};
463 delete $entry->{tax_entry};
465 $entry->{display_amount} = $form->round_amount(abs($entry->{amount}) * $storno_mult, 2);
466 $entry->{display_actual_tax} = $entry->{actual_tax} * $storno_mult;
467 $entry->{display_expected_tax} = $entry->{expected_tax} * $storno_mult;
469 if ($entry->{taxkey_error}) {
470 $self->{negative_taxkey_filter} ||= {
471 'ar' => { map { $_ => 1 } ( 8, 9, 18, 19) },
472 'ap' => { map { $_ => 1 } (1, 2, 3, 12, 13) },
476 $entry->{correct_taxkeys} = [];
478 my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $entry->{transdate});
480 foreach my $taxkey (sort { $a <=> $b } keys %{ $all_taxes{taxkeys} }) {
481 next if ($self->{negative_taxkey_filter}->{ $entry->{module} }->{$taxkey});
483 my $tax_info = $all_taxes{taxkeys}->{$taxkey};
485 next if ((!$tax_info || (0 == $tax_info->{taxrate} * 1)) && $entry->{tax_entry_acc_trans_id});
487 push @{ $entry->{correct_taxkeys} }, {
489 'tax' => $form->round_amount(abs($entry->{amount}) * $tax_info->{taxrate}, 2),
490 'description' => sprintf("%s %d%%", $tax_info->{taxdescription}, int($tax_info->{taxrate} * 100)),
497 if (first { $_ > 1 } values %num_entries_per_chart) {
498 $params{problem}->{type} = 'wrong_taxkeys';
500 $params{problem}->{type} = 'wrong_taxes';
503 $params{problem}->{acc_trans} = { %data };
504 push @{ $self->{problems} }, $params{problem};
509 $main::lxdebug->leave_sub();
514 # Inaktiver Code für das Erraten möglicher Verteilungen von
515 # Steuerschlüsseln. Deaktiviert, weil er exponentiell Zeit
518 # if (abs($expected_tax - $data{$side}->{tax_sum}) >= 0.02) {
519 # my @potential_taxkeys = $trans_type eq 'AP' ? (0, 8, 9) : (0, 1, 2, 3);
521 # $main::lxdebug->dump(0, "pota", \@potential_taxkeys);
523 # # Über alle Kombinationen aus Buchungssätzen und potenziellen Steuerschlüsseln
524 # # iterieren und jeweils die Summe ermitteln.
525 # my $num_entries = scalar @{ $data{$side}->{entries} };
526 # my @taxkey_indices = (0) x $num_entries;
528 # my @solutions = ();
530 # my $start_time = time();
532 # $main::lxdebug->message(0, "num_entries $num_entries");
534 # while ($num_entries == scalar @taxkey_indices) {
535 # my @tax_cache = ();
537 # # Berechnen der Steuersumme für die aktuell angenommenen Steuerschlüssel.
539 # foreach my $i (0 .. $num_entries - 1) {
540 # my $taxkey = $potential_taxkeys[$taxkey_indices[$i]];
541 # my $entry = $data{$side}->{entries}->[$i];
542 # my $taxinfo = $all_taxes{taxkeys}->{ $taxkey } || { };
543 # $tax_cache[$i] = $entry->{amount} * $taxinfo->{taxrate};
544 # $tax_sum += $tax_cache[$i];
547 # # Entspricht die Steuersumme mit den aktuell angenommenen Steuerschlüsseln
548 # # der verbuchten Steuersumme? Wenn ja, dann ist das eine potenzielle
550 # if (abs($tax_sum - $data{$side}->{tax_sum}) < 0.02) {
552 # 'taxkeys' => [ @potential_taxkeys[@taxkey_indices] ],
553 # 'taxes' => [ @tax_cache ],
557 # # Weiterzählen der Steuerschlüsselindices zum Interieren über
558 # # alle möglichen Kombinationen.
561 # $taxkey_indices[$i]++;
562 # last if ($taxkey_indices[$i] < scalar @potential_taxkeys);
564 # $taxkey_indices[$i] = 0;
569 # if (($now - $start_time) >= 5) {
570 # $main::lxdebug->message(0, " " . join("", @taxkey_indices));
571 # $start_time = $now;
575 # foreach my $solution (@solutions) {
576 # $solution->{rows} = [];
577 # $solution->{changes} = [];
580 # foreach my $i (0 .. $num_entries - 1) {
581 # if ($solution->{taxes}->[$i]) {
582 # my $tax_rounded = $form->round_amount($solution->{taxes}->[$i] + $error, 2);
583 # $error = $solution->{taxes}->[$i] + $error - $tax_rounded;
584 # $solution->{taxes}->[$i] = $tax_rounded;
587 # my $entry = $data{$side}->{entries}->[$i];
588 # my $tax_entry = $all_taxes{taxkeys}->{ $solution->{taxkeys}->[$i] };
590 # push @{ $solution->{rows} }, {
593 # 'taxamount' => $solution->{taxes}->[$i],
596 # $solution->{rows}->[$i]->{taxdescription} .= ' ' . $form->format_amount(\%myconfig, $tax_entry->{taxrate} * 100) . ' %';
598 # push @{ $solution->{changes} }, {
599 # 'acc_trans_id' => $entry->{acc_trans_id},
600 # 'taxkey' => $solution->{taxkeys}->[$i],
604 # push @{ $solution->{rows} }, @{ $data{$other_side}->{entries} };
606 # delete @{ $solution }{ qw(taxes taxkeys) };
609 # $problem->{type} = 'wrong_taxkeys';
610 # $problem->{solutions} = [ @solutions ];
611 # $problem->{acc_trans} = { %data };
612 # push @problems, $problem;
618 $main::lxdebug->enter_sub();
623 my $myconfig = \%main::myconfig;
624 my $form = $main::form;
626 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
628 my @transactions = $self->_fetch_transactions(%params, 'dbh' => $dbh);
630 if (!scalar @transactions) {
631 $main::lxdebug->leave_sub();
635 my $callback = $params{callback} ? '&callback=' . $params{callback} : '';
637 $self->{problems} = [];
638 $self->{ap_ar_taxkey_problems} = [];
639 $self->{invoice_inventory_taxkey_problems} = [];
641 foreach my $transaction (@transactions) {
642 my %common_args = $self->_prepare_data('transaction' => $transaction, 'callback' => $callback, 'full_analysis' => $params{full_analysis});
644 next if ($self->_check_trans_ap_ar_wrong_taxkeys(%common_args));
645 next if ($self->_check_trans_invoices_inventory_with_taxkeys(%common_args));
646 next if ($self->_check_trans_split_multiple_credit_and_debit(%common_args));
647 next if ($self->_check_trans_wrong_taxkeys(%common_args));
650 my @problems = @{ $self->{problems} };
652 map { $self->{$_} ||= [] } qw(ap_ar_taxkey_problems invoice_inventory_taxkey_problems missing_taxkeys_in_invoices);
654 if (0 != scalar @{ $self->{ap_ar_taxkey_problems} }) {
656 'type' => 'ap_ar_wrong_taxkeys',
657 'ap_problems' => [ grep { $_->{data}->{module} eq 'ap' } @{ $self->{ap_ar_taxkey_problems} } ],
658 'ar_problems' => [ grep { $_->{data}->{module} eq 'ar' } @{ $self->{ap_ar_taxkey_problems} } ],
660 unshift @problems, $problem;
663 if (0 != scalar @{ $self->{invoice_inventory_taxkey_problems} }) {
665 'type' => 'invoice_inventory_with_taxkeys',
666 'ap_problems' => [ grep { $_->{data}->{module} eq 'ap' } @{ $self->{invoice_inventory_taxkey_problems} } ],
667 'ar_problems' => [ grep { $_->{data}->{module} eq 'ar' } @{ $self->{invoice_inventory_taxkey_problems} } ],
669 unshift @problems, $problem;
672 if (0 != scalar @{ $self->{missing_taxkeys_in_invoices} }) {
674 'type' => 'missing_taxkeys_in_invoices',
675 'ap_problems' => [ grep { $_->{data}->{module} eq 'ap' } @{ $self->{missing_taxkeys_in_invoices} } ],
676 'ar_problems' => [ grep { $_->{data}->{module} eq 'ar' } @{ $self->{missing_taxkeys_in_invoices} } ],
678 unshift @problems, $problem;
681 $main::lxdebug->leave_sub();
683 # $::lxdebug->dump(0, 'problems:', \@problems);
688 sub fix_ap_ar_wrong_taxkeys {
689 $main::lxdebug->enter_sub();
694 my $myconfig = \%main::myconfig;
695 my $form = $main::form;
697 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
699 my $query = qq|SELECT 'ap' AS module,
700 at.acc_trans_id, at.trans_id, at.chart_id, at.amount, at.taxkey, at.transdate,
703 LEFT JOIN chart c ON (at.chart_id = c.id)
704 WHERE (trans_id IN (SELECT id FROM ap WHERE NOT invoice))
705 AND (taxkey IN (2, 3, 12, 13))
709 SELECT 'ar' AS module,
710 at.acc_trans_id, at.trans_id, at.chart_id, at.amount, at.taxkey, at.transdate,
713 LEFT JOIN chart c ON (at.chart_id = c.id)
714 WHERE (trans_id IN (SELECT id FROM ar WHERE NOT invoice))
715 AND (taxkey IN (8, 9, 18, 19))
717 ORDER BY trans_id, acc_trans_id|;
719 my $sth = prepare_execute_query($form, $dbh, $query);
722 while (my $ref = $sth->fetchrow_hashref()) {
723 if ((!scalar @transactions) || ($ref->{trans_id} != $transactions[-1]->[0]->{trans_id})) {
724 push @transactions, [];
727 push @{ $transactions[-1] }, $ref;
732 @transactions = grep { (scalar(@transactions) % 2) == 0 } @transactions;
734 my %taxkey_replacements = (
746 'ap' => { map { $_ => 1 } (2, 3, 12, 13) },
747 'ar' => { map { $_ => 1 } (8, 9, 18, 19) },
750 my @corrections = ();
752 foreach my $transaction (@transactions) {
754 for (my $i = 0; $i < scalar @{ $transaction }; $i += 2) {
755 my ($non_tax_idx, $tax_idx) = abs($transaction->[$i]->{amount}) > abs($transaction->[$i + 1]->{amount}) ? ($i, $i + 1) : ($i + 1, $i);
756 my ($non_tax, $tax) = @{ $transaction }[$non_tax_idx, $tax_idx];
758 last if ($non_tax->{link} =~ m/(:?AP|AR)_tax(:?$|:)/);
759 last if ($tax->{link} !~ m/(:?AP|AR)_tax(:?$|:)/);
761 next if (!$bad_taxkeys{ $non_tax->{module} }->{ $non_tax->{taxkey} });
763 my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $non_tax->{transdate});
765 push @corrections, ({ 'acc_trans_id' => $non_tax->{acc_trans_id},
766 'taxkey' => $taxkey_replacements{$non_tax->{taxkey}},
769 'acc_trans_id' => $tax->{acc_trans_id},
770 'taxkey' => $taxkey_replacements{$non_tax->{taxkey}},
771 'chart_id' => $all_taxes{taxkeys}->{ $taxkey_replacements{$non_tax->{taxkey}} }->{taxchart_id},
776 if (scalar @corrections) {
777 my $q_taxkey_only = qq|UPDATE acc_trans SET taxkey = ? WHERE acc_trans_id = ?|;
778 my $h_taxkey_only = prepare_query($form, $dbh, $q_taxkey_only);
780 my $q_taxkey_chart_id = qq|UPDATE acc_trans SET taxkey = ?, chart_id = ? WHERE acc_trans_id = ?|;
781 my $h_taxkey_chart_id = prepare_query($form, $dbh, $q_taxkey_chart_id);
783 foreach my $entry (@corrections) {
784 if ($entry->{chart_id}) {
785 do_statement($form, $h_taxkey_chart_id, $q_taxkey_chart_id, $entry->{taxkey}, $entry->{chart_id}, $entry->{acc_trans_id});
787 do_statement($form, $h_taxkey_only, $q_taxkey_only, $entry->{taxkey}, $entry->{acc_trans_id});
791 $h_taxkey_only->finish();
792 $h_taxkey_chart_id->finish();
794 $dbh->commit() unless ($params{dbh});
797 $main::lxdebug->leave_sub();
800 sub fix_invoice_inventory_with_taxkeys {
801 $main::lxdebug->enter_sub();
806 my $myconfig = \%main::myconfig;
807 my $form = $main::form;
809 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
811 my $query = qq|SELECT at.*, c.link
813 LEFT JOIN ar ON (at.trans_id = ar.id)
814 LEFT JOIN chart c ON (at.chart_id = c.id)
821 LEFT JOIN ap ON (at.trans_id = ap.id)
822 LEFT JOIN chart c ON (at.chart_id = c.id)
825 ORDER BY trans_id, acc_trans_id|;
827 my $sth = prepare_execute_query($form, $dbh, $query);
830 while (my $ref = $sth->fetchrow_hashref()) {
831 if ((!scalar @transactions) || ($ref->{trans_id} != $transactions[-1]->[0]->{trans_id})) {
832 push @transactions, [];
835 push @{ $transactions[-1] }, $ref;
840 my @corrections = ();
842 foreach my $transaction (@transactions) {
843 my @sub_transactions = $self->_group_sub_transactions($transaction);
845 foreach my $sub_transaction (@sub_transactions) {
846 my $is_cogs = first { $_->{link} =~ m/IC_cogs/ } @{ $sub_transaction };
847 next unless ($is_cogs);
849 foreach my $entry (@{ $sub_transaction }) {
850 next if ($entry->{taxkey} == 0);
851 push @corrections, $entry->{acc_trans_id};
857 $query = qq|UPDATE acc_trans SET taxkey = 0 WHERE acc_trans_id = ?|;
858 $sth = prepare_query($form, $dbh, $query);
860 foreach my $acc_trans_id (@corrections) {
861 do_statement($form, $sth, $query, $acc_trans_id);
866 $dbh->commit() unless ($params{dbh});
870 $main::lxdebug->leave_sub();
873 sub fix_wrong_taxkeys {
874 $main::lxdebug->enter_sub();
879 Common::check_params(\%params, qw(fixes));
881 my $myconfig = \%main::myconfig;
882 my $form = $main::form;
884 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
886 my $q_taxkey_only = qq|UPDATE acc_trans SET taxkey = ? WHERE acc_trans_id = ?|;
887 my $h_taxkey_only = prepare_query($form, $dbh, $q_taxkey_only);
889 my $q_taxkey_chart = qq|UPDATE acc_trans SET taxkey = ?, chart_id = ? WHERE acc_trans_id = ?|;
890 my $h_taxkey_chart = prepare_query($form, $dbh, $q_taxkey_chart);
892 my $q_transdate = qq|SELECT transdate FROM acc_trans WHERE acc_trans_id = ?|;
893 my $h_transdate = prepare_query($form, $dbh, $q_transdate);
895 foreach my $fix (@{ $params{fixes} }) {
896 next unless ($fix->{acc_trans_id});
898 do_statement($form, $h_taxkey_only, $q_taxkey_only, conv_i($fix->{taxkey}), conv_i($fix->{acc_trans_id}));
900 next unless ($fix->{tax_entry_acc_trans_id});
902 do_statement($form, $h_transdate, $q_transdate, conv_i($fix->{tax_entry_acc_trans_id}));
903 my ($transdate) = $h_transdate->fetchrow_array();
905 my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $transdate);
906 my $tax_info = $all_taxes{taxkeys}->{ $fix->{taxkey} };
908 next unless ($tax_info);
910 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}));
913 $h_taxkey_only->finish();
914 $h_taxkey_chart->finish();
915 $h_transdate->finish();
918 $dbh->commit() unless ($params{dbh});
920 $main::lxdebug->leave_sub();
923 sub delete_transaction {
924 $main::lxdebug->enter_sub();
929 Common::check_params(\%params, qw(trans_id));
931 my $myconfig = \%main::myconfig;
932 my $form = $main::form;
934 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
936 do_query($form, $dbh, qq|UPDATE ar SET storno_id = NULL WHERE storno_id = ?|, conv_i($params{trans_id}));
937 do_query($form, $dbh, qq|UPDATE ap SET storno_id = NULL WHERE storno_id = ?|, conv_i($params{trans_id}));
938 do_query($form, $dbh, qq|UPDATE gl SET storno_id = NULL WHERE storno_id = ?|, conv_i($params{trans_id}));
940 do_query($form, $dbh, qq|DELETE FROM ar WHERE id = ?|, conv_i($params{trans_id}));
941 do_query($form, $dbh, qq|DELETE FROM ap WHERE id = ?|, conv_i($params{trans_id}));
942 do_query($form, $dbh, qq|DELETE FROM gl WHERE id = ?|, conv_i($params{trans_id}));
943 do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE trans_id = ?|, conv_i($params{trans_id}));
946 $dbh->commit() unless ($params{dbh});
948 $main::lxdebug->leave_sub();