Revision eb0c10b0
Von Moritz Bunkus vor fast 16 Jahren hinzugefügt
SL/AccTransCorrections.pm | ||
---|---|---|
package AccTransCorrections;
|
||
|
||
use strict;
|
||
|
||
use List::Util qw(first);
|
||
|
||
use SL::DBUtils;
|
||
use SL::Taxkeys;
|
||
|
||
sub new {
|
||
my $type = shift;
|
||
|
||
my $self = {};
|
||
|
||
bless $self, $type;
|
||
|
||
$self->{taxkeys} = Taxkeys->new();
|
||
|
||
return $self;
|
||
}
|
||
|
||
sub _fetch_transactions {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
my $myconfig = \%main::myconfig;
|
||
my $form = $main::form;
|
||
|
||
my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
|
||
|
||
my (@where, @values) = ((), ());
|
||
|
||
if ($params{transdate_from}) {
|
||
push @where, qq|at.transdate >= ?|;
|
||
push @values, $params{transdate_from};
|
||
}
|
||
|
||
if ($params{transdate_to}) {
|
||
push @where, qq|at.transdate <= ?|;
|
||
push @values, $params{transdate_to};
|
||
}
|
||
# $params{trans_id} = 3150;
|
||
if ($params{trans_id}) {
|
||
push @where, qq|at.trans_id = ?|;
|
||
push @values, $params{trans_id};
|
||
}
|
||
|
||
my $where = '';
|
||
if (scalar @where) {
|
||
$where = 'WHERE ' . join(' AND ', map { "($_)" } @where);
|
||
}
|
||
|
||
my $query = qq!
|
||
SELECT at.oid, at.*,
|
||
c.accno, c.description AS chartdescription, c.charttype, c.category AS chartcategory, c.link AS chartlink,
|
||
COALESCE(gl.reference, COALESCE(ap.invnumber, ar.invnumber)) AS reference,
|
||
COALESCE(ap.invoice, COALESCE(ar.invoice, FALSE)) AS invoice,
|
||
CASE
|
||
WHEN gl.id IS NOT NULL THEN gl.storno AND (gl.storno_id IS NOT NULL)
|
||
WHEN ap.id IS NOT NULL THEN ap.storno AND (ap.storno_id IS NOT NULL)
|
||
ELSE ar.storno AND (ar.storno_id IS NOT NULL)
|
||
END AS is_storno,
|
||
CASE
|
||
WHEN gl.id IS NOT NULL THEN 'gl'
|
||
WHEN ap.id IS NOT NULL THEN 'ap'
|
||
ELSE 'ar'
|
||
END AS module
|
||
|
||
FROM acc_trans at
|
||
LEFT JOIN chart c ON (at.chart_id = c.id)
|
||
LEFT JOIN gl ON (at.trans_id = gl.id)
|
||
LEFT JOIN ap ON (at.trans_id = ap.id)
|
||
LEFT JOIN ar ON (at.trans_id = ar.id)
|
||
$where
|
||
ORDER BY at.trans_id, at.oid
|
||
!;
|
||
|
||
my @transactions = ();
|
||
my $last_trans = undef;
|
||
|
||
foreach my $entry (@{ selectall_hashref_query($form, $dbh, $query, @values) }) {
|
||
if (!$last_trans || ($last_trans->[0]->{trans_id} != $entry->{trans_id})) {
|
||
$last_trans = [];
|
||
push @transactions, $last_trans;
|
||
}
|
||
|
||
push @{ $last_trans }, $entry;
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
return @transactions;
|
||
}
|
||
|
||
sub _prepare_data {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
my $transaction = $params{transaction};
|
||
my $callback = $params{callback};
|
||
|
||
my $myconfig = \%main::myconfig;
|
||
my $form = $main::form;
|
||
|
||
my $data = {
|
||
'credit' => {
|
||
'num' => 0,
|
||
'sum' => 0,
|
||
'entries' => [],
|
||
'tax_sum' => 0,
|
||
'tax_entries' => [],
|
||
},
|
||
'debit' => {
|
||
'num' => 0,
|
||
'sum' => 0,
|
||
'entries' => [],
|
||
'tax_sum' => 0,
|
||
'tax_entries' => [],
|
||
},
|
||
'payments' => [],
|
||
};
|
||
|
||
foreach my $entry (@{ $transaction }) {
|
||
$entry->{chartlinks} = { map { $_ => 1 } split(m/:/, $entry->{chartlink}) };
|
||
delete $entry->{chartlink};
|
||
}
|
||
|
||
# Verkn?pfungen zwischen Steuerschl?sseln und zum Zeitpunkt der Transaktion
|
||
# g?ltigen Steuers?tze
|
||
my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $transaction->[0]->{transdate});
|
||
|
||
my ($trans_type, $previous_non_tax_entry);
|
||
my $sum = 0;
|
||
my $first_sub_trans = 1;
|
||
|
||
my $storno_mult = $transaction->[0]->{is_storno} ? -1 : 1;
|
||
|
||
# Aufteilung der Buchungspositionen in Soll-, Habenseite sowie
|
||
# getrennte Auflistung der Positionen, die auf Steuerkonten gebucht werden.
|
||
foreach my $entry (@{ $transaction }) {
|
||
if (!$first_sub_trans && ($entry->{chartlinks}->{AP_paid} || $entry->{chartlinks}->{AR_paid})) {
|
||
push @{ $data->{payments} }, $entry;
|
||
next;
|
||
}
|
||
|
||
my $tax_info = $all_taxes{taxkeys}->{ $entry->{taxkey} };
|
||
if ($tax_info) {
|
||
$entry->{taxdescription} = $tax_info->{taxdescription} . ' ' . $form->format_amount($myconfig, $tax_info->{taxrate} * 100) . ' %';
|
||
}
|
||
|
||
if ($entry->{chartlinks}->{AP}) {
|
||
$trans_type = 'AP';
|
||
} elsif ($entry->{chartlinks}->{AR}) {
|
||
$trans_type = 'AR';
|
||
}
|
||
|
||
my $idx = 0 < ($entry->{amount} * $storno_mult) ? 'credit' : 'debit';
|
||
|
||
if ($entry->{chartlinks}->{AP_tax} || $entry->{chartlinks}->{AR_tax}) {
|
||
$data->{$idx}->{tax_sum} += $entry->{amount};
|
||
push @{ $data->{$idx}->{tax_entries} }, $entry;
|
||
|
||
if ($previous_non_tax_entry) {
|
||
$previous_non_tax_entry->{tax_entry} = $entry;
|
||
undef $previous_non_tax_entry;
|
||
}
|
||
|
||
} else {
|
||
$data->{$idx}->{sum} += $entry->{amount};
|
||
push @{ $data->{$idx}->{entries} }, $entry;
|
||
|
||
$previous_non_tax_entry = $entry;
|
||
}
|
||
|
||
$sum += $entry->{amount};
|
||
|
||
if (abs($sum) < 0.02) {
|
||
$sum = 0;
|
||
$first_sub_trans = 0;
|
||
}
|
||
}
|
||
|
||
# Alle Eintr?ge entfernen, die die Gegenkonten zu Zahlungsein- und
|
||
# -ausg?ngen darstellen.
|
||
foreach my $payment (@{ $data->{payments} }) {
|
||
my $idx = 0 < $payment->{amount} ? 'debit' : 'credit';
|
||
|
||
foreach my $i (0 .. scalar(@{ $data->{$idx}->{entries} }) - 1) {
|
||
my $entry = $data->{$idx}->{entries}->[$i];
|
||
|
||
next if ((($payment->{amount} * -1) != $entry->{amount}) || ($payment->{transdate} ne $entry->{transdate}));
|
||
|
||
splice @{ $data->{$idx}->{entries} }, $i, 1;
|
||
|
||
last;
|
||
}
|
||
}
|
||
|
||
delete $data->{payments};
|
||
|
||
map { $data->{$_}->{num} = scalar @{ $data->{$_}->{entries} } } qw(credit debit);
|
||
|
||
my $info = $transaction->[0];
|
||
my $script = ($info->{module} eq 'ar') && $info->{invoice} ? 'is'
|
||
: ($info->{module} eq 'ap') && $info->{invoice} ? 'ir'
|
||
: $info->{module};
|
||
|
||
my %common_args = (
|
||
'data' => $data,
|
||
'trans_type' => $trans_type,
|
||
'all_taxes' => { %all_taxes },
|
||
'transaction' => $transaction,
|
||
'full_analysis' => $params{full_analysis},
|
||
'problem' => {
|
||
'data' => $info,
|
||
'link' => $script . ".pl?action=edit${callback}&id=" . $info->{trans_id},
|
||
},
|
||
);
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
return %common_args;
|
||
}
|
||
|
||
sub _group_sub_transactions {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my $transaction = shift;
|
||
|
||
my @sub_transactions = ();
|
||
my $sum = 0;
|
||
|
||
foreach my $i (0 .. scalar(@{ $transaction }) - 1) {
|
||
my $entry = $transaction->[$i];
|
||
|
||
if (abs($sum) <= 0.01) {
|
||
push @sub_transactions, [];
|
||
$sum = 0;
|
||
}
|
||
$sum += $entry->{amount};
|
||
|
||
push @{ $sub_transactions[-1] }, $entry;
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
return @sub_transactions;
|
||
}
|
||
|
||
# Problemfall: Verkaufsrechnungen, bei denen Buchungen auf Warenbestandskonten
|
||
# mit Steuerschl?ssel != 0 durchgef?hrt wurden. Richtig w?re, dass alle
|
||
# Steuerschl?ssel f?r solche Warenbestandsbuchungen 0 sind.
|
||
sub _check_trans_invoices_inventory_with_taxkeys {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
if (!$params{transaction}->[0]->{invoice}) {
|
||
$main::lxdebug->leave_sub();
|
||
return 0;
|
||
}
|
||
|
||
my @sub_transactions = $self->_group_sub_transactions($params{transaction});
|
||
|
||
foreach my $sub_transaction (@sub_transactions) {
|
||
my $is_cogs = first { $_->{chartlinks}->{IC_cogs} } @{ $sub_transaction };
|
||
next unless ($is_cogs);
|
||
|
||
my $needs_fixing = first { $_->{taxkey} != 0 } @{ $sub_transaction };
|
||
next unless ($needs_fixing);
|
||
|
||
$params{problem}->{type} = 'invoice_inventory_with_taxkeys';
|
||
push @{ $self->{invoice_inventory_taxkey_problems} }, $params{problem};
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
return 1;
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
return 0;
|
||
}
|
||
|
||
# Problemfall: Kreditorenbuchungen, bei denen mit Umsatzsteuerschl?sseln
|
||
# gebucht wurde und Debitorenbuchungen, bei denen mit Vorsteuerschl?sseln
|
||
# gebucht wurde.
|
||
sub _check_trans_ap_ar_wrong_taxkeys {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
my $retval = 0;
|
||
|
||
if (!$params{transaction}->[0]->{invoice}
|
||
&& (( ($params{transaction}->[0]->{module} eq 'ap')
|
||
&& (first { my $taxkey = $_->{taxkey}; first { $taxkey == $_ } (2, 3, 12, 13) } @{ $params{transaction} }))
|
||
||
|
||
( ($params{transaction}->[0]->{module} eq 'ar')
|
||
&& (first { my $taxkey = $_->{taxkey}; first { $taxkey == $_ } (8, 9, 18, 19) } @{ $params{transaction} })))) {
|
||
$params{problem}->{type} = 'ap_ar_wrong_taxkeys';
|
||
push @{ $self->{ap_ar_taxkey_problems} }, $params{problem};
|
||
|
||
$retval = 1;
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
return $retval;
|
||
}
|
||
|
||
# Problemfall: Splitbuchungen, die mehrere Haben- und Sollkonten ansprechen.
|
||
# Aber nur f?r Debitoren- und Kreditorenbuchungen, weil das bei Einkaufs- und
|
||
# Verkaufsrechnungen hingegen v?llig normal ist.
|
||
sub _check_trans_split_multiple_credit_and_debit {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
my $retval = 0;
|
||
|
||
if ( !$params{transaction}->[0]->{invoice}
|
||
&& (1 < $params{data}->{credit}->{num})
|
||
&& (1 < $params{data}->{debit}->{num})) {
|
||
$params{problem}->{type} = 'split_multiple_credit_and_debit';
|
||
push @{ $self->{problems} }, $params{problem};
|
||
|
||
$retval = 1;
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
return $retval;
|
||
}
|
||
|
||
# Problemfall: Buchungen, bei denen Steuersummen nicht mit den Summen
|
||
# ?bereinstimmen, die nach ausgew?hltem Steuerschl?ssel h?tten auftreten m?ssen.
|
||
sub _check_trans_wrong_taxkeys {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
my $form = $main::form;
|
||
|
||
my %data = %{ $params{data} };
|
||
my $transaction = $params{transaction};
|
||
|
||
if ( $transaction->[0]->{invoice}
|
||
|| $transaction->[0]->{ob_transaction}
|
||
|| $transaction->[0]->{cb_transaction}
|
||
|| (!scalar @{ $data{credit}->{entries} } && !scalar @{ $data{debit}->{entries} })
|
||
|| ( ($transaction->[0]->{module} eq 'gl')
|
||
&& (!scalar @{ $data{credit}->{entries} } || !scalar @{ $data{debit}->{entries} }))) {
|
||
$main::lxdebug->leave_sub();
|
||
return 0;
|
||
}
|
||
|
||
my $retval = 0;
|
||
|
||
my ($side, $other_side);
|
||
if ( (grep { $_->{taxkey} * 1 } @{ $data{credit}->{entries} })
|
||
|| (scalar @{ $data{credit}->{tax_entries} })) {
|
||
$side = 'credit';
|
||
$other_side = 'debit';
|
||
|
||
} elsif ( (grep { $_->{taxkey} * 1 } @{ $data{debit}->{entries} })
|
||
|| (scalar @{ $data{debit}->{tax_entries} })) {
|
||
$side = 'debit';
|
||
$other_side = 'credit';
|
||
}
|
||
|
||
if (!$side) {
|
||
$main::lxdebug->leave_sub();
|
||
return 0;
|
||
}
|
||
|
||
my $expected_tax = 0;
|
||
my %num_entries_per_chart = ();
|
||
my $num_taxed_entries = 0;
|
||
|
||
foreach my $entry (@{ $data{$side}->{entries} }) {
|
||
my $taxinfo = $params{all_taxes}->{taxkeys}->{$entry->{taxkey}} || { };
|
||
$entry->{expected_tax} = $entry->{amount} * $taxinfo->{taxrate};
|
||
$expected_tax += $entry->{expected_tax};
|
||
|
||
$num_taxed_entries++ if ($taxinfo->{taxrate} * 1);
|
||
|
||
my $chart_key = $entry->{chart_id} . "-" . $entry->{taxkey};
|
||
$num_entries_per_chart{$chart_key} ||= 0;
|
||
$num_entries_per_chart{$chart_key}++;
|
||
}
|
||
|
||
# $main::lxdebug->message(0, "side $side trans_id $transaction->[0]->{trans_id} expected tax $expected_tax actual tax $data{$side}->{tax_sum}");
|
||
|
||
if (abs($expected_tax - $data{$side}->{tax_sum}) >= (0.01 * ($num_taxed_entries + 1))) {
|
||
if ($params{full_analysis}) {
|
||
my $storno_mult = $data{$side}->{entries}->[0]->{is_storno} ? -1 : 1;
|
||
|
||
foreach my $entry (@{ $data{$other_side}->{entries} }) {
|
||
$entry->{display_amount} = $form->round_amount(abs($entry->{amount}) * $storno_mult, 2);
|
||
}
|
||
|
||
foreach my $entry (@{ $data{$side}->{entries} }) {
|
||
$entry->{actual_tax} = $form->round_amount(abs($entry->{tax_entry} ? $entry->{tax_entry}->{amount} : 0), 2);
|
||
$entry->{expected_tax} = $form->round_amount(abs($entry->{expected_tax}), 2);
|
||
$entry->{taxkey_error} = ( $entry->{taxkey} && !$entry->{tax_entry})
|
||
|| (!$entry->{taxkey} && $entry->{tax_entry})
|
||
|| (abs($entry->{expected_tax} - $entry->{actual_tax}) >= 0.02);
|
||
$entry->{tax_entry_oid} = $entry->{tax_entry}->{oid};
|
||
delete $entry->{tax_entry};
|
||
|
||
$entry->{display_amount} = $form->round_amount(abs($entry->{amount}) * $storno_mult, 2);
|
||
$entry->{display_actual_tax} = $entry->{actual_tax} * $storno_mult;
|
||
$entry->{display_expected_tax} = $entry->{expected_tax} * $storno_mult;
|
||
|
||
if ($entry->{taxkey_error}) {
|
||
$self->{negative_taxkey_filter} ||= {
|
||
'ar' => { map { $_ => 1 } ( 8, 9, 18, 19) },
|
||
'ap' => { map { $_ => 1 } (1, 2, 3, 12, 13) },
|
||
'gl' => { },
|
||
};
|
||
|
||
$entry->{correct_taxkeys} = [];
|
||
|
||
my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $entry->{transdate});
|
||
|
||
foreach my $taxkey (sort { $a <=> $b } keys %{ $all_taxes{taxkeys} }) {
|
||
next if ($self->{negative_taxkey_filter}->{ $entry->{module} }->{$taxkey});
|
||
|
||
my $tax_info = $all_taxes{taxkeys}->{$taxkey};
|
||
|
||
next if ((!$tax_info || (0 == $tax_info->{taxrate} * 1)) && $entry->{tax_entry_oid});
|
||
|
||
push @{ $entry->{correct_taxkeys} }, {
|
||
'taxkey' => $taxkey,
|
||
'tax' => $form->round_amount(abs($entry->{amount}) * $tax_info->{taxrate}, 2),
|
||
'description' => sprintf("%s %d%%", $tax_info->{taxdescription}, int($tax_info->{taxrate} * 100)),
|
||
};
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (first { $_ > 1 } values %num_entries_per_chart) {
|
||
$params{problem}->{type} = 'wrong_taxkeys';
|
||
} else {
|
||
$params{problem}->{type} = 'wrong_taxes';
|
||
}
|
||
|
||
$params{problem}->{acc_trans} = { %data };
|
||
push @{ $self->{problems} }, $params{problem};
|
||
|
||
$retval = 1;
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
return $retval;
|
||
}
|
||
|
||
# Inaktiver Code f?r das Erraten m?glicher Verteilungen von
|
||
# Steuerschl?sseln. Deaktiviert, weil er exponentiell Zeit
|
||
# ben?tigt.
|
||
|
||
# if (abs($expected_tax - $data{$side}->{tax_sum}) >= 0.02) {
|
||
# my @potential_taxkeys = $trans_type eq 'AP' ? (0, 8, 9) : (0, 1, 2, 3);
|
||
|
||
# $main::lxdebug->dump(0, "pota", \@potential_taxkeys);
|
||
|
||
# # ?ber alle Kombinationen aus Buchungss?tzen und potenziellen Steuerschl?sseln
|
||
# # iterieren und jeweils die Summe ermitteln.
|
||
# my $num_entries = scalar @{ $data{$side}->{entries} };
|
||
# my @taxkey_indices = (0) x $num_entries;
|
||
|
||
# my @solutions = ();
|
||
|
||
# my $start_time = time();
|
||
|
||
# $main::lxdebug->message(0, "num_entries $num_entries");
|
||
|
||
# while ($num_entries == scalar @taxkey_indices) {
|
||
# my @tax_cache = ();
|
||
|
||
# # Berechnen der Steuersumme f?r die aktuell angenommenen Steuerschl?ssel.
|
||
# my $tax_sum = 0;
|
||
# foreach my $i (0 .. $num_entries - 1) {
|
||
# my $taxkey = $potential_taxkeys[$taxkey_indices[$i]];
|
||
# my $entry = $data{$side}->{entries}->[$i];
|
||
# my $taxinfo = $all_taxes{taxkeys}->{ $taxkey } || { };
|
||
# $tax_cache[$i] = $entry->{amount} * $taxinfo->{taxrate};
|
||
# $tax_sum += $tax_cache[$i];
|
||
# }
|
||
|
||
# # Entspricht die Steuersumme mit den aktuell angenommenen Steuerschl?sseln
|
||
# # der verbuchten Steuersumme? Wenn ja, dann ist das eine potenzielle
|
||
# # L?sung.
|
||
# if (abs($tax_sum - $data{$side}->{tax_sum}) < 0.02) {
|
||
# push @solutions, {
|
||
# 'taxkeys' => [ @potential_taxkeys[@taxkey_indices] ],
|
||
# 'taxes' => [ @tax_cache ],
|
||
# }
|
||
# }
|
||
|
||
# # Weiterz?hlen der Steuerschl?sselindices zum Interieren ?ber
|
||
# # alle m?glichen Kombinationen.
|
||
# my $i = 0;
|
||
# while (1) {
|
||
# $taxkey_indices[$i]++;
|
||
# last if ($taxkey_indices[$i] < scalar @potential_taxkeys);
|
||
|
||
# $taxkey_indices[$i] = 0;
|
||
# $i++;
|
||
# }
|
||
|
||
# my $now = time();
|
||
# if (($now - $start_time) >= 5) {
|
||
# $main::lxdebug->message(0, " " . join("", @taxkey_indices));
|
||
# $start_time = $now;
|
||
# }
|
||
# }
|
||
|
||
# foreach my $solution (@solutions) {
|
||
# $solution->{rows} = [];
|
||
# $solution->{changes} = [];
|
||
# my $error = 0;
|
||
|
||
# foreach my $i (0 .. $num_entries - 1) {
|
||
# if ($solution->{taxes}->[$i]) {
|
||
# my $tax_rounded = $form->round_amount($solution->{taxes}->[$i] + $error, 2);
|
||
# $error = $solution->{taxes}->[$i] + $error - $tax_rounded;
|
||
# $solution->{taxes}->[$i] = $tax_rounded;
|
||
# }
|
||
|
||
# my $entry = $data{$side}->{entries}->[$i];
|
||
# my $tax_entry = $all_taxes{taxkeys}->{ $solution->{taxkeys}->[$i] };
|
||
|
||
# push @{ $solution->{rows} }, {
|
||
# %{ $entry },
|
||
# %{ $tax_entry },
|
||
# 'taxamount' => $solution->{taxes}->[$i],
|
||
# };
|
||
|
||
# $solution->{rows}->[$i]->{taxdescription} .= ' ' . $form->format_amount(\%myconfig, $tax_entry->{taxrate} * 100) . ' %';
|
||
|
||
# push @{ $solution->{changes} }, {
|
||
# 'oid' => $entry->{oid},
|
||
# 'taxkey' => $solution->{taxkeys}->[$i],
|
||
# };
|
||
# }
|
||
|
||
# push @{ $solution->{rows} }, @{ $data{$other_side}->{entries} };
|
||
|
||
# delete @{ $solution }{ qw(taxes taxkeys) };
|
||
# }
|
||
|
||
# $problem->{type} = 'wrong_taxkeys';
|
||
# $problem->{solutions} = [ @solutions ];
|
||
# $problem->{acc_trans} = { %data };
|
||
# push @problems, $problem;
|
||
|
||
# next;
|
||
# }
|
||
|
||
sub analyze {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
my $myconfig = \%main::myconfig;
|
||
my $form = $main::form;
|
||
|
||
my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
|
||
|
||
my @transactions = $self->_fetch_transactions(%params, 'dbh' => $dbh);
|
||
|
||
if (!scalar @transactions) {
|
||
$main::lxdebug->leave_sub();
|
||
return ();
|
||
}
|
||
|
||
my $callback = $params{callback} ? '&callback=' . $params{callback} : '';
|
||
|
||
$self->{problems} = [];
|
||
$self->{ap_ar_taxkey_problems} = [];
|
||
$self->{invoice_inventory_taxkey_problems} = [];
|
||
|
||
foreach my $transaction (@transactions) {
|
||
my %common_args = $self->_prepare_data('transaction' => $transaction, 'callback' => $callback, 'full_analysis' => $params{full_analysis});
|
||
|
||
next if ($self->_check_trans_ap_ar_wrong_taxkeys(%common_args));
|
||
next if ($self->_check_trans_invoices_inventory_with_taxkeys(%common_args));
|
||
next if ($self->_check_trans_split_multiple_credit_and_debit(%common_args));
|
||
next if ($self->_check_trans_wrong_taxkeys(%common_args));
|
||
}
|
||
|
||
my @problems = @{ $self->{problems} };
|
||
|
||
if (0 != scalar @{ $self->{ap_ar_taxkey_problems} }) {
|
||
my $problem = {
|
||
'type' => 'ap_ar_wrong_taxkeys',
|
||
'ap_problems' => [ grep { $_->{data}->{module} eq 'ap' } @{ $self->{ap_ar_taxkey_problems} } ],
|
||
'ar_problems' => [ grep { $_->{data}->{module} eq 'ar' } @{ $self->{ap_ar_taxkey_problems} } ],
|
||
};
|
||
unshift @problems, $problem;
|
||
}
|
||
|
||
if (0 != scalar @{ $self->{invoice_inventory_taxkey_problems} }) {
|
||
my $problem = {
|
||
'type' => 'invoice_inventory_with_taxkeys',
|
||
'ap_problems' => [ grep { $_->{data}->{module} eq 'ap' } @{ $self->{invoice_inventory_taxkey_problems} } ],
|
||
'ar_problems' => [ grep { $_->{data}->{module} eq 'ar' } @{ $self->{invoice_inventory_taxkey_problems} } ],
|
||
};
|
||
unshift @problems, $problem;
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
|
||
return @problems;
|
||
}
|
||
|
||
sub fix_ap_ar_wrong_taxkeys {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
my $myconfig = \%main::myconfig;
|
||
my $form = $main::form;
|
||
|
||
my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
|
||
|
||
my $query = qq|SELECT 'ap' AS module,
|
||
at.oid, at.trans_id, at.chart_id, at.amount, at.taxkey, at.transdate,
|
||
c.link
|
||
FROM acc_trans at
|
||
LEFT JOIN chart c ON (at.chart_id = c.id)
|
||
WHERE (trans_id IN (SELECT id FROM ap WHERE NOT invoice))
|
||
AND (taxkey IN (2, 3, 12, 13))
|
||
|
||
UNION
|
||
|
||
SELECT 'ar' AS module,
|
||
at.oid, at.trans_id, at.chart_id, at.amount, at.taxkey, at.transdate,
|
||
c.link
|
||
FROM acc_trans at
|
||
LEFT JOIN chart c ON (at.chart_id = c.id)
|
||
WHERE (trans_id IN (SELECT id FROM ar WHERE NOT invoice))
|
||
AND (taxkey IN (8, 9, 18, 19))
|
||
|
||
ORDER BY trans_id, oid|;
|
||
|
||
my $sth = prepare_execute_query($form, $dbh, $query);
|
||
my @transactions;
|
||
|
||
while (my $ref = $sth->fetchrow_hashref()) {
|
||
if ((!scalar @transactions) || ($ref->{trans_id} != $transactions[-1]->[0]->{trans_id})) {
|
||
push @transactions, [];
|
||
}
|
||
|
||
push @{ $transactions[-1] }, $ref;
|
||
}
|
||
|
||
$sth->finish();
|
||
|
||
@transactions = grep { (scalar(@transactions) % 2) == 0 } @transactions;
|
||
|
||
my %taxkey_replacements = (
|
||
2 => 8,
|
||
3 => 9,
|
||
8 => 2,
|
||
9 => 3,
|
||
12 => 18,
|
||
13 => 19,
|
||
18 => 12,
|
||
19 => 13,
|
||
);
|
||
|
||
my %bad_taxkeys = (
|
||
'ap' => { map { $_ => 1 } (2, 3, 12, 13) },
|
||
'ar' => { map { $_ => 1 } (8, 9, 18, 19) },
|
||
);
|
||
|
||
my @corrections = ();
|
||
|
||
foreach my $transaction (@transactions) {
|
||
|
||
for (my $i = 0; $i < scalar @{ $transaction }; $i += 2) {
|
||
my ($non_tax_idx, $tax_idx) = abs($transaction->[$i]->{amount}) > abs($transaction->[$i + 1]->{amount}) ? ($i, $i + 1) : ($i + 1, $i);
|
||
my ($non_tax, $tax) = @{ $transaction }[$non_tax_idx, $tax_idx];
|
||
|
||
last if ($non_tax->{link} =~ m/(:?AP|AR)_tax(:?$|:)/);
|
||
last if ($tax->{link} !~ m/(:?AP|AR)_tax(:?$|:)/);
|
||
|
||
next if (!$bad_taxkeys{ $non_tax->{module} }->{ $non_tax->{taxkey} });
|
||
|
||
my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $non_tax->{transdate});
|
||
|
||
push @corrections, ({ 'oid' => $non_tax->{oid},
|
||
'taxkey' => $taxkey_replacements{$non_tax->{taxkey}},
|
||
},
|
||
{
|
||
'oid' => $tax->{oid},
|
||
'taxkey' => $taxkey_replacements{$non_tax->{taxkey}},
|
||
'chart_id' => $all_taxes{taxkeys}->{ $taxkey_replacements{$non_tax->{taxkey}} }->{taxchart_id},
|
||
});
|
||
}
|
||
}
|
||
|
||
if (scalar @corrections) {
|
||
my $q_taxkey_only = qq|UPDATE acc_trans SET taxkey = ? WHERE oid = ?|;
|
||
my $h_taxkey_only = prepare_query($form, $dbh, $q_taxkey_only);
|
||
|
||
my $q_taxkey_chart_id = qq|UPDATE acc_trans SET taxkey = ?, chart_id = ? WHERE oid = ?|;
|
||
my $h_taxkey_chart_id = prepare_query($form, $dbh, $q_taxkey_chart_id);
|
||
|
||
foreach my $entry (@corrections) {
|
||
if ($entry->{chart_id}) {
|
||
do_statement($form, $h_taxkey_chart_id, $q_taxkey_chart_id, $entry->{taxkey}, $entry->{chart_id}, $entry->{oid});
|
||
} else {
|
||
do_statement($form, $h_taxkey_only, $q_taxkey_only, $entry->{taxkey}, $entry->{oid});
|
||
}
|
||
}
|
||
|
||
$h_taxkey_only->finish();
|
||
$h_taxkey_chart_id->finish();
|
||
|
||
$dbh->commit() unless ($params{dbh});
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
}
|
||
|
||
sub fix_invoice_inventory_with_taxkeys {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
my $myconfig = \%main::myconfig;
|
||
my $form = $main::form;
|
||
|
||
my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
|
||
|
||
my $query = qq|SELECT at.oid, at.*, c.link
|
||
FROM acc_trans at
|
||
LEFT JOIN ar ON (at.trans_id = ar.id)
|
||
LEFT JOIN chart c ON (at.chart_id = c.id)
|
||
WHERE (ar.invoice)
|
||
|
||
UNION
|
||
|
||
SELECT at.oid, at.*, c.link
|
||
FROM acc_trans at
|
||
LEFT JOIN ap ON (at.trans_id = ap.id)
|
||
LEFT JOIN chart c ON (at.chart_id = c.id)
|
||
WHERE (ap.invoice)
|
||
|
||
ORDER BY trans_id, oid|;
|
||
|
||
my $sth = prepare_execute_query($form, $dbh, $query);
|
||
my @transactions;
|
||
|
||
while (my $ref = $sth->fetchrow_hashref()) {
|
||
if ((!scalar @transactions) || ($ref->{trans_id} != $transactions[-1]->[0]->{trans_id})) {
|
||
push @transactions, [];
|
||
}
|
||
|
||
push @{ $transactions[-1] }, $ref;
|
||
}
|
||
|
||
$sth->finish();
|
||
|
||
my @corrections = ();
|
||
|
||
foreach my $transaction (@transactions) {
|
||
my @sub_transactions = $self->_group_sub_transactions($transaction);
|
||
|
||
foreach my $sub_transaction (@sub_transactions) {
|
||
my $is_cogs = first { $_->{link} =~ m/IC_cogs/ } @{ $sub_transaction };
|
||
next unless ($is_cogs);
|
||
|
||
foreach my $entry (@{ $sub_transaction }) {
|
||
next if ($entry->{taxkey} == 0);
|
||
push @corrections, $entry->{oid};
|
||
}
|
||
}
|
||
}
|
||
|
||
if (@corrections) {
|
||
$query = qq|UPDATE acc_trans SET taxkey = 0 WHERE oid = ?|;
|
||
$sth = prepare_query($form, $dbh, $query);
|
||
|
||
foreach my $oid (@corrections) {
|
||
do_statement($form, $sth, $query, $oid);
|
||
}
|
||
|
||
$sth->finish();
|
||
|
||
$dbh->commit() unless ($params{dbh});
|
||
# $dbh->rollback();
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
}
|
||
|
||
sub fix_wrong_taxkeys {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
Common::check_params(\%params, qw(fixes));
|
||
|
||
my $myconfig = \%main::myconfig;
|
||
my $form = $main::form;
|
||
|
||
my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
|
||
|
||
my $q_taxkey_only = qq|UPDATE acc_trans SET taxkey = ? WHERE oid = ?|;
|
||
my $h_taxkey_only = prepare_query($form, $dbh, $q_taxkey_only);
|
||
|
||
my $q_taxkey_chart = qq|UPDATE acc_trans SET taxkey = ?, chart_id = ? WHERE oid = ?|;
|
||
my $h_taxkey_chart = prepare_query($form, $dbh, $q_taxkey_chart);
|
||
|
||
my $q_transdate = qq|SELECT transdate FROM acc_trans WHERE oid = ?|;
|
||
my $h_transdate = prepare_query($form, $dbh, $q_transdate);
|
||
|
||
foreach my $fix (@{ $params{fixes} }) {
|
||
next unless ($fix->{oid});
|
||
|
||
do_statement($form, $h_taxkey_only, $q_taxkey_only, conv_i($fix->{taxkey}), conv_i($fix->{oid}));
|
||
|
||
next unless ($fix->{tax_entry_oid});
|
||
|
||
do_statement($form, $h_transdate, $q_transdate, conv_i($fix->{tax_entry_oid}));
|
||
my ($transdate) = $h_transdate->fetchrow_array();
|
||
|
||
my %all_taxes = $self->{taxkeys}->get_full_tax_info('transdate' => $transdate);
|
||
my $tax_info = $all_taxes{taxkeys}->{ $fix->{taxkey} };
|
||
|
||
next unless ($tax_info);
|
||
|
||
do_statement($form, $h_taxkey_chart, $q_taxkey_chart, conv_i($fix->{taxkey}), conv_i($tax_info->{taxchart_id}), conv_i($fix->{tax_entry_oid}));
|
||
}
|
||
|
||
$h_taxkey_only->finish();
|
||
$h_taxkey_chart->finish();
|
||
$h_transdate->finish();
|
||
|
||
# $dbh->rollback();
|
||
$dbh->commit() unless ($params{dbh});
|
||
|
||
$main::lxdebug->leave_sub();
|
||
}
|
||
|
||
sub delete_transaction {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
Common::check_params(\%params, qw(trans_id));
|
||
|
||
my $myconfig = \%main::myconfig;
|
||
my $form = $main::form;
|
||
|
||
my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
|
||
|
||
do_query($form, $dbh, qq|UPDATE ar SET storno_id = NULL WHERE storno_id = ?|, conv_i($params{trans_id}));
|
||
do_query($form, $dbh, qq|UPDATE ap SET storno_id = NULL WHERE storno_id = ?|, conv_i($params{trans_id}));
|
||
do_query($form, $dbh, qq|UPDATE gl SET storno_id = NULL WHERE storno_id = ?|, conv_i($params{trans_id}));
|
||
|
||
do_query($form, $dbh, qq|DELETE FROM ar WHERE id = ?|, conv_i($params{trans_id}));
|
||
do_query($form, $dbh, qq|DELETE FROM ap WHERE id = ?|, conv_i($params{trans_id}));
|
||
do_query($form, $dbh, qq|DELETE FROM gl WHERE id = ?|, conv_i($params{trans_id}));
|
||
do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE trans_id = ?|, conv_i($params{trans_id}));
|
||
|
||
# $dbh->rollback();
|
||
$dbh->commit() unless ($params{dbh});
|
||
|
||
$main::lxdebug->leave_sub();
|
||
}
|
||
|
||
1;
|
SL/Locale.pm | ||
---|---|---|
$self->{charset} = Common::DEFAULT_CHARSET;
|
||
}
|
||
|
||
my $db_charset = $main::dbcharset || Common::DEFAULT_CHARSET;
|
||
my $db_charset = $main::dbcharset || Common::DEFAULT_CHARSET;
|
||
|
||
$self->{iconv} = Text::Iconv->new($self->{charset}, $db_charset);
|
||
$self->{iconv_reverse} = Text::Iconv->new($db_charset, $self->{charset});
|
||
$self->{iconv_english} = Text::Iconv->new('ASCII', $db_charset);
|
||
$self->{iconv_iso8859} = Text::Iconv->new('ISO-8859-15', $db_charset);
|
||
$self->{iconv} = Text::Iconv->new($self->{charset}, $db_charset);
|
||
$self->{iconv_reverse} = Text::Iconv->new($db_charset, $self->{charset});
|
||
$self->{iconv_english} = Text::Iconv->new('ASCII', $db_charset);
|
||
$self->{iconv_iso8859} = Text::Iconv->new('ISO-8859-15', $db_charset);
|
||
$self->{iconv_to_iso8859} = Text::Iconv->new($db_charset, 'ISO-8859-15');
|
||
|
||
$self->_read_special_chars_file($country);
|
||
}
|
SL/Taxkeys.pm | ||
---|---|---|
package Taxkeys;
|
||
|
||
use strict;
|
||
|
||
use Memoize;
|
||
|
||
use SL::DBUtils;
|
||
|
||
sub new {
|
||
my $type = shift;
|
||
|
||
my $self = {};
|
||
|
||
bless $self, $type;
|
||
|
||
return $self->_init();
|
||
}
|
||
|
||
sub DESTROY {
|
||
my $self = shift;
|
||
|
||
$self->_finish_statements();
|
||
}
|
||
|
||
sub _init {
|
||
my $self = shift;
|
||
|
||
$self->{handles} = { };
|
||
$self->{queries} = { };
|
||
|
||
memoize 'get_tax_info';
|
||
memoize 'get_full_tax_info';
|
||
|
||
return $self;
|
||
}
|
||
|
||
sub _finish_statements {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
|
||
foreach my $idx (keys %{ $self->{handles} }) {
|
||
$self->{handles}->{$idx}->finish();
|
||
delete $self->{handles}->{$idx};
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
}
|
||
|
||
sub get_tax_info {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
Common::check_params(\%params, qw(transdate taxkey));
|
||
|
||
my $myconfig = \%main::myconfig;
|
||
my $form = $main::form;
|
||
|
||
if (!$self->{handles}->{get_tax_info}) {
|
||
$self->{queries}->{get_tax_info} = qq|
|
||
SELECT t.rate AS taxrate, t.taxnumber, t.taxdescription, t.chart_id AS taxchart_id,
|
||
c.accno AS taxaccno, c.description AS taxaccount
|
||
FROM taxkeys tk
|
||
LEFT JOIN tax t ON (tk.tax_id = t.id)
|
||
LEFT JOIN chart c ON (t.chart_id = c.id)
|
||
WHERE tk.id =
|
||
(SELECT id
|
||
FROM taxkeys
|
||
WHERE (taxkey_id = ?)
|
||
AND (startdate <= ?)
|
||
ORDER BY startdate DESC
|
||
LIMIT 1)
|
||
|;
|
||
|
||
$self->{handles}->{get_tax_info} = prepare_query($form, $params{dbh} || $form->get_standard_dbh($myconfig), $self->{queries}->{get_tax_info});
|
||
}
|
||
|
||
my $sth = $self->{handles}->{get_tax_info};
|
||
do_statement($form, $sth, $self->{queries}->{get_tax_info}, $params{taxkey}, $params{transdate});
|
||
|
||
my $ref = $sth->fetchrow_hashref() || { };
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
return $ref;
|
||
}
|
||
|
||
sub get_full_tax_info {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $self = shift;
|
||
my %params = @_;
|
||
|
||
Common::check_params(\%params, qw(transdate));
|
||
|
||
my $myconfig = \%main::myconfig;
|
||
my $form = $main::form;
|
||
|
||
my %tax_info = (
|
||
'taxkeys' => { },
|
||
'taxchart_ids' => { },
|
||
);
|
||
|
||
my @all_taxkeys = map { $_->{taxkey} } (selectall_hashref_query($form, $form->get_standard_dbh(), qq|SELECT DISTINCT taxkey FROM tax WHERE taxkey IS NOT NULL|));
|
||
|
||
foreach my $taxkey (@all_taxkeys) {
|
||
my $ref = $self->get_tax_info('transdate' => $params{transdate}, 'taxkey' => $taxkey);
|
||
|
||
$tax_info{taxkeys}->{$taxkey} = $ref;
|
||
$tax_info{accnos}->{$ref->{taxchart_id}} = $ref if ($ref->{taxchart_id});
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
||
return %tax_info;
|
||
}
|
||
|
||
1;
|
SL/Template/Plugin/LxERP.pm | ||
---|---|---|
}
|
||
}
|
||
|
||
sub abs {
|
||
my $self = shift;
|
||
my $var = shift;
|
||
|
||
return $var < 0 ? $var * -1 : $var;
|
||
}
|
||
|
||
1;
|
acctranscorrections.pl | ||
---|---|---|
link am.pl
|
bin/mozilla/acctranscorrections.pl | ||
---|---|---|
use SL::AccTransCorrections;
|
||
use SL::Form;
|
||
use SL::User;
|
||
use Data::Dumper;
|
||
use YAML;
|
||
|
||
require "bin/mozilla/common.pl";
|
||
|
||
sub analyze_filter {
|
||
$lxdebug->enter_sub();
|
||
|
||
$form->{jsscript} = 1;
|
||
$form->{title} = $locale->text('General ledger corrections');
|
||
$form->header();
|
||
print $form->parse_html_template('acctranscorrections/analyze_filter');
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub analyze {
|
||
$lxdebug->enter_sub();
|
||
|
||
$form->{title} = $locale->text('General ledger corrections');
|
||
|
||
delete @{ $form }{qw(transdate_from transdate_to)} if ($form->{scope} eq 'full');
|
||
|
||
my $callback = 'acctranscorrections.pl?action=analyze';
|
||
map { $callback .= "&${_}=" . $form->escape($form->{$_}) } grep { $form->{$_} } qw(transdate_from transdate_to);
|
||
$callback = $form->escape($callback);
|
||
|
||
my $analyzer = AccTransCorrections->new();
|
||
|
||
my %params = map { $_ => $form->{$_} } qw(transdate_from transdate_to);
|
||
my @problems = $analyzer->analyze(%params, 'callback' => $callback);
|
||
|
||
if (!scalar @problems) {
|
||
$form->show_generic_information($locale->text('No problems were recognized.'));
|
||
|
||
$lxdebug->leave_sub();
|
||
return;
|
||
}
|
||
|
||
$form->header();
|
||
print $form->parse_html_template('acctranscorrections/analyze_overview',
|
||
{ 'PROBLEMS' => \@problems,
|
||
'callback' => $callback,
|
||
});
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub assistant {
|
||
$lxdebug->enter_sub();
|
||
|
||
$form->{title} = $locale->text('Assistant for general ledger corrections');
|
||
|
||
$form->isblank('trans_id', $locale->text('Transaction ID missing.'));
|
||
|
||
my $analyzer = AccTransCorrections->new();
|
||
my ($problem) = $analyzer->analyze('trans_id' => $form->{trans_id}, 'full_analysis' => 1);
|
||
|
||
if (!$problem) {
|
||
my $module =
|
||
$form->{trans_module} eq 'ar' ? $locale->text('AR Transaction')
|
||
: $form->{trans_module} eq 'ap' ? $locale->text('AP Transaction')
|
||
: $locale->text('General Ledger Transaction');
|
||
|
||
$form->show_generic_information($locale->text('The assistant could not find anything wrong with #1. Maybe the problem has been solved in the meantime.',
|
||
"$module $form->{trans_reference}"));
|
||
|
||
$lxdebug->leave_sub();
|
||
return;
|
||
}
|
||
|
||
if ($problem->{type} eq 'wrong_taxkeys') {
|
||
assistant_for_wrong_taxkeys($problem);
|
||
|
||
} elsif ($problem->{type} eq 'wrong_taxes') {
|
||
assistant_for_wrong_taxkeys($problem);
|
||
# assistant_for_wrong_taxes($problem);
|
||
|
||
} else {
|
||
$form->show_generic_error($locale->text('Unknown problem type.'));
|
||
}
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub assistant_for_ap_ar_wrong_taxkeys {
|
||
$lxdebug->enter_sub();
|
||
|
||
$form->{title} = $locale->text('Assistant for general ledger corrections');
|
||
|
||
$form->header();
|
||
print $form->parse_html_template('acctranscorrections/assistant_for_ap_ar_wrong_taxkeys');
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub fix_ap_ar_wrong_taxkeys {
|
||
$lxdebug->enter_sub();
|
||
|
||
my $analyzer = AccTransCorrections->new();
|
||
$analyzer->fix_ap_ar_wrong_taxkeys();
|
||
|
||
$form->{title} = $locale->text('Assistant for general ledger corrections');
|
||
$form->header();
|
||
print $form->parse_html_template('acctranscorrections/fix_ap_ar_wrong_taxkeys');
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub assistant_for_invoice_inventory_with_taxkeys {
|
||
$lxdebug->enter_sub();
|
||
|
||
$form->{title} = $locale->text('Assistant for general ledger corrections');
|
||
|
||
$form->header();
|
||
print $form->parse_html_template('acctranscorrections/assistant_for_invoice_inventory_with_taxkeys');
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub fix_invoice_inventory_with_taxkeys {
|
||
$lxdebug->enter_sub();
|
||
|
||
my $analyzer = AccTransCorrections->new();
|
||
$analyzer->fix_invoice_inventory_with_taxkeys();
|
||
|
||
$form->{title} = $locale->text('Assistant for general ledger corrections');
|
||
$form->header();
|
||
print $form->parse_html_template('acctranscorrections/fix_invoice_inventory_with_taxkeys');
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub assistant_for_wrong_taxes {
|
||
$lxdebug->enter_sub();
|
||
|
||
my $problem = shift;
|
||
|
||
$form->{title} = $locale->text('Assistant for general ledger corrections');
|
||
|
||
$form->header();
|
||
print $form->parse_html_template('acctranscorrections/assistant_for_wrong_taxes', { 'problem' => $problem, });
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub assistant_for_wrong_taxkeys {
|
||
$lxdebug->enter_sub();
|
||
|
||
my $problem = shift;
|
||
|
||
$form->{title} = $locale->text('Assistant for general ledger corrections');
|
||
|
||
$form->header();
|
||
print $form->parse_html_template('acctranscorrections/assistant_for_wrong_taxkeys', { 'problem' => $problem, });
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub fix_wrong_taxkeys {
|
||
$lxdebug->enter_sub();
|
||
|
||
my $fixes = ref $form->{fixes} eq 'ARRAY' ? $form->{fixes} : [];
|
||
|
||
my $analyzer = AccTransCorrections->new();
|
||
$analyzer->fix_wrong_taxkeys('fixes' => $fixes);
|
||
|
||
$form->{title} = $locale->text('Assistant for general ledger corrections');
|
||
$form->header();
|
||
print $form->parse_html_template('acctranscorrections/fix_wrong_taxkeys');
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub delete_transaction {
|
||
$lxdebug->enter_sub();
|
||
|
||
$form->{title} = $locale->text('Delete transaction');
|
||
$form->header();
|
||
|
||
if (!$form->{confirmation}) {
|
||
print $form->parse_html_template('acctranscorrections/delete_transaction_confirmation');
|
||
} else {
|
||
my $analyzer = AccTransCorrections->new();
|
||
$analyzer->delete_transaction('trans_id' => $form->{trans_id});
|
||
|
||
print $form->parse_html_template('acctranscorrections/delete_transaction');
|
||
}
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub redirect {
|
||
$lxdebug->enter_sub();
|
||
|
||
$form->redirect('Missing callbcak');
|
||
|
||
$lxdebug->leave_sub();
|
||
}
|
||
|
||
sub dispatcher {
|
||
foreach my $action (qw(fix_wrong_taxkeys delete_transaction)) {
|
||
if ($form->{"action_${action}"}) {
|
||
call_sub($action);
|
||
return;
|
||
}
|
||
}
|
||
|
||
$form->error($locale->text('No action defined.'));
|
||
}
|
||
|
||
1;
|
locale/de/acctranscorrections | ||
---|---|---|
#!/usr/bin/perl
|
||
|
||
$self->{texts} = {
|
||
'ADDED' => 'Hinzugef?gt',
|
||
'AP' => 'Einkauf',
|
||
'AP Transaction' => 'Kreditorenbuchung',
|
||
'AR' => 'Verkauf',
|
||
'AR Transaction' => 'Debitorenbuchung',
|
||
'Address' => 'Adresse',
|
||
'Advance turnover tax return' => 'Umsatzsteuervoranmeldung',
|
||
'All reports' => 'Alle Berichte (Kontenübersicht, Summen- u. Saldenliste, GuV, BWA, Bilanz, Projektbuchungen)',
|
||
'Assistant for general ledger corrections' => 'Assistent f?r die Korrektur von Hauptbucheintr?gen',
|
||
'Attempt to call an undefined sub named \'%s\'' => 'Es wurde versucht, eine nicht definierte Unterfunktion namens \'%s\' aufzurufen.',
|
||
'Bcc' => 'Bcc',
|
||
'Bin List' => 'Lagerliste',
|
||
'Binding to the LDAP server as "#1" failed. Please check config/authentication.pl.' => 'Die Anmeldung am LDAP-Server als "#1" schlug fehl. Bitte überprüfen Sie die Angaben in config/authentication.pl.',
|
||
'CANCELED' => 'Storniert',
|
||
'Cc' => 'Cc',
|
||
'Change Lx-Office installation settings (all menu entries beneath \'System\')' => 'Verändern der Lx-Office-Installationseinstellungen (Menüpunkte unterhalb von \'System\')',
|
||
'Confirmation' => 'Auftragsbest?tigung',
|
||
'Contact' => 'Kontakt',
|
||
'Create and edit RFQs' => 'Lieferantenanfragen erfassen und bearbeiten',
|
||
'Create and edit customers and vendors' => 'Kunden und Lieferanten erfassen und bearbeiten',
|
||
'Create and edit dunnings' => 'Mahnungen erfassen und bearbeiten',
|
||
'Create and edit invoices and credit notes' => 'Rechnungen und Gutschriften erfassen und bearbeiten',
|
||
'Create and edit parts, services, assemblies' => 'Artikel, Dienstleistungen, Erzeugnisse erfassen und bearbeiten',
|
||
'Create and edit projects' => 'Projekte erfassen und bearbeiten',
|
||
'Create and edit purchase delivery orders' => 'Lieferscheine von Lieferanten erfassen und bearbeiten',
|
||
'Create and edit purchase orders' => 'Lieferantenaufträge erfassen und bearbeiten',
|
||
'Create and edit sales delivery orders' => 'Lieferscheine für Kunden erfassen und bearbeiten',
|
||
'Create and edit sales orders' => 'Auftragsbestätigungen erfassen und bearbeiten',
|
||
'Create and edit sales quotations' => 'Angebote erfassen und bearbeiten',
|
||
'Create and edit vendor invoices' => 'Eingangsrechnungen erfassen und bearbeiten',
|
||
'Credit Note' => 'Gutschrift',
|
||
'Customer Number' => 'Kundennummer',
|
||
'Customer details' => 'Kundendetails',
|
||
'DATEV Export' => 'DATEV-Export',
|
||
'DELETED' => 'Gel?scht',
|
||
'DUNNING STARTED' => 'Mahnprozess gestartet',
|
||
'Dataset upgrade' => 'Datenbankaktualisierung',
|
||
'Date' => 'Datum',
|
||
'Delete transaction' => 'Buchung l?schen',
|
||
'Delivery Order' => 'Lieferschein',
|
||
'Dependency loop detected:' => 'Schleife in den Abhängigkeiten entdeckt:',
|
||
'Directory' => 'Verzeichnis',
|
||
'ELSE' => 'Zusatz',
|
||
'Enter longdescription' => 'Langtext eingeben',
|
||
'Error in database control file \'%s\': %s' => 'Fehler in Datenbankupgradekontrolldatei \'%s\': %s',
|
||
'File' => 'Datei',
|
||
'General Ledger Transaction' => 'Dialogbuchung',
|
||
'General ledger and cash' => 'Finanzbuchhaltung und Zahlungsverkehr',
|
||
'General ledger corrections' => 'Korrekturen im Hauptbuch',
|
||
'History' => 'Historie',
|
||
'Invoice' => 'Rechnung',
|
||
'MAILED' => 'Gesendet',
|
||
'Manage license keys' => 'Lizenzschlüssel verwalten',
|
||
'Mark as paid?' => 'Als bezahlt markieren?',
|
||
'Marked as paid' => 'Als bezahlt markiert',
|
||
'Master Data' => 'Stammdaten',
|
||
'May set the BCC field when sending emails' => 'Beim Verschicken von Emails das Feld \'BCC\' setzen',
|
||
'Message' => 'Nachricht',
|
||
'Missing \'description\' field.' => 'Fehlendes Feld \'description\'.',
|
||
'Missing \'tag\' field.' => 'Fehlendes Feld \'tag\'.',
|
||
'Missing parameter #1 in call to sub #2.' => 'Fehlernder Parameter \'#1\' in Funktionsaufruf \'#2\'.',
|
||
'Missing parameter (at least one of #1) in call to sub #2.' => 'Fehlernder Parameter (mindestens einer aus \'#1\') in Funktionsaufruf \'#2\'.',
|
||
'More than one control file with the tag \'%s\' exist.' => 'Es gibt mehr als eine Kontrolldatei mit dem Tag \'%s\'.',
|
||
'Name' => 'Name',
|
||
'No %s was found matching the search parameters.' => 'Es wurde kein %s gefunden, auf den die Suchparameter zutreffen.',
|
||
'No Customer was found matching the search parameters.' => 'Zu dem Suchbegriff wurde kein Endkunde gefunden',
|
||
'No Vendor was found matching the search parameters.' => 'Zu dem Suchbegriff wurde kein H?ndler gefunden',
|
||
'No action defined.' => 'Keine Aktion definiert.',
|
||
'No customer has been selected yet.' => 'Es wurde noch kein Kunde ausgew?hlt.',
|
||
'No or an unknown authenticantion module specified in "config/authentication.pl".' => 'Es wurde kein oder ein unbekanntes Authentifizierungsmodul in "config/authentication.pl" angegeben.',
|
||
'No part was found matching the search parameters.' => 'Es wurde kein Artikel gefunden, auf den die Suchparameter zutreffen.',
|
||
'No problems were recognized.' => 'Es wurden keine Probleme gefunden.',
|
||
'No vendor has been selected yet.' => 'Es wurde noch kein Lieferant ausgew?hlt.',
|
||
'Others' => 'Andere',
|
||
'PAYMENT POSTED' => 'Rechung gebucht',
|
||
'POSTED' => 'Gebucht',
|
||
'POSTED AS NEW' => 'Als neu gebucht',
|
||
'PRINTED' => 'Gedruckt',
|
||
'Packing List' => 'Lieferschein',
|
||
'Part Number' => 'Artikelnummer',
|
||
'Part description' => 'Artikelbeschreibung',
|
||
'Pick List' => 'Packliste',
|
||
'Please enter values' => 'Bitte Werte eingeben',
|
||
'Preview' => 'Druckvorschau',
|
||
'Proforma Invoice' => 'Proformarechnung',
|
||
'Purchase Order' => 'Lieferantenauftrag',
|
||
'Quotation' => 'Angebot',
|
||
'RFQ' => 'Anfrage',
|
||
'Receipt, payment, reconciliation' => 'Zahlungseingang, Zahlungsausgang, Kontenabgleich',
|
||
'Reports' => 'Berichte',
|
||
'SAVED' => 'Gespeichert',
|
||
'SAVED FOR DUNNING' => 'Gespeichert',
|
||
'SCREENED' => 'Angezeigt',
|
||
'Select a Customer' => 'Endkunde ausw?hlen',
|
||
'Select a customer' => 'Einen Kunden auswählen',
|
||
'Select a part' => 'Artikel auswählen',
|
||
'Select a vendor' => 'Einen Lieferanten auswählen',
|
||
'Storno Invoice' => 'Stornorechnung',
|
||
'Storno Packing List' => 'Stornolieferschein',
|
||
'Subject' => 'Betreff',
|
||
'The \'tag\' field must only consist of alphanumeric characters or the carachters - _ ( )' => 'Das Feld \'tag\' darf nur aus alphanumerischen Zeichen und den Zeichen - _ ( ) bestehen.',
|
||
'The LDAP server "#1:#2" is unreachable. Please check config/authentication.pl.' => 'Der LDAP-Server "#1:#2" ist nicht erreichbar. Bitte überprüfen Sie die Angaben in config/authentication.pl.',
|
||
'The assistant could not find anything wrong with #1. Maybe the problem has been solved in the meantime.' => 'Der Korrekturassistent konnte kein Problem bei #1 feststellen. Eventuell wurde das Problem in der Zwischenzeit bereits behoben.',
|
||
'The config file "config/authentication.pl" contained invalid Perl code:' => 'Die Konfigurationsdatei "config/authentication.pl" enthielt ungütigen Perl-Code:',
|
||
'The config file "config/authentication.pl" was not found.' => 'Die Konfigurationsdatei "config/authentication.pl" wurde nicht gefunden.',
|
||
'The connection to the LDAP server cannot be encrypted (SSL/TLS startup failure). Please check config/authentication.pl.' => 'Die Verbindung zum LDAP-Server kann nicht verschlüsselt werden (Fehler bei SSL/TLS-Initialisierung). Bitte überprüfen Sie die Angaben in config/authentication.pl.',
|
||
'The connection to the authentication database failed:' => 'Die Verbindung zur Authentifizierungsdatenbank schlug fehl:',
|
||
'The connection to the template database failed:' => 'Die Verbindung zur Vorlagendatenbank schlug fehl:',
|
||
'The creation of the authentication database failed:' => 'Das Anlegen der Authentifizierungsdatenbank schlug fehl:',
|
||
'To (email)' => 'An',
|
||
'Transaction ID missing.' => 'Die Buchungs-ID fehlt.',
|
||
'Transactions, AR transactions, AP transactions' => 'Dialogbuchen, Debitorenrechnungen, Kreditorenrechnungen',
|
||
'Trying to call a sub without a name' => 'Es wurde versucht, eine Unterfunktion ohne Namen aufzurufen.',
|
||
'Unit' => 'Einheit',
|
||
'Unknown dependency \'%s\'.' => 'Unbekannte Abhängigkeit \'%s\'.',
|
||
'Unknown problem type.' => 'Unbekannter Problem-Typ',
|
||
'Value' => 'Wert',
|
||
'Variable' => 'Variable',
|
||
'Vendor details' => 'Lieferantendetails',
|
||
'View warehouse content' => 'Lagerbestand ansehen',
|
||
'Warehouse management' => 'Lagerverwaltung/Bestandsver?nderung',
|
||
'You do not have the permissions to access this function.' => 'Sie verfügen nicht über die notwendigen Rechte, um auf diese Funktion zuzugreifen.',
|
||
'[email]' => '[email]',
|
||
'bin_list' => 'Lagerliste',
|
||
'config/authentication.pl: Key "DB_config" is missing.' => 'config/authentication.pl: Das Schlüsselwort "DB_config" fehlt.',
|
||
'config/authentication.pl: Key "LDAP_config" is missing.' => 'config/authentication.pl: Der Schlüssel "LDAP_config" fehlt.',
|
||
'config/authentication.pl: Missing parameters in "DB_config". Required parameters are "host", "db" and "user".' => 'config/authentication.pl: Fehlende Parameter in "DB_config". Benötigte Parameter sind "host", "db" und "user".',
|
||
'config/authentication.pl: Missing parameters in "LDAP_config". Required parameters are "host", "attribute" and "base_dn".' => 'config/authentication.pl: Fehlende Parameter in "LDAP_config". Benötigt werden "host", "attribute" und "base_dn".',
|
||
'customer' => 'Kunde',
|
||
'invoice' => 'Rechnung',
|
||
'no' => 'nein',
|
||
'packing_list' => 'Versandliste',
|
||
'pick_list' => 'Entnahmeliste',
|
||
'proforma' => 'Proforma',
|
||
'purchase_order' => 'Auftrag',
|
||
'request_quotation' => 'Angebotsanforderung',
|
||
'sales_order' => 'Kundenauftrag',
|
||
'sales_quotation' => 'Verkaufsangebot',
|
||
'vendor' => 'Lieferant',
|
||
'yes' => 'ja',
|
||
};
|
||
|
||
$self->{subs} = {
|
||
'E' => 'E',
|
||
'H' => 'H',
|
||
'J' => 'J',
|
||
'NTI' => 'NTI',
|
||
'Q' => 'Q',
|
||
'analyze' => 'analyze',
|
||
'analyze_filter' => 'analyze_filter',
|
||
'assistant' => 'assistant',
|
||
'assistant_for_ap_ar_wrong_taxkeys' => 'assistant_for_ap_ar_wrong_taxkeys',
|
||
'assistant_for_invoice_inventory_with_taxkeys' => 'assistant_for_invoice_inventory_with_taxkeys',
|
||
'assistant_for_wrong_taxes' => 'assistant_for_wrong_taxes',
|
||
'assistant_for_wrong_taxkeys' => 'assistant_for_wrong_taxkeys',
|
||
'build_std_url' => 'build_std_url',
|
||
'calculate_qty' => 'calculate_qty',
|
||
'call_sub' => 'call_sub',
|
||
'cov_selection_internal' => 'cov_selection_internal',
|
||
'delete_transaction' => 'delete_transaction',
|
||
'delivery_customer_selection' => 'delivery_customer_selection',
|
||
'dispatcher' => 'dispatcher',
|
||
'fix_ap_ar_wrong_taxkeys' => 'fix_ap_ar_wrong_taxkeys',
|
||
'fix_invoice_inventory_with_taxkeys' => 'fix_invoice_inventory_with_taxkeys',
|
||
'fix_wrong_taxkeys' => 'fix_wrong_taxkeys',
|
||
'format_dates' => 'format_dates',
|
||
'mark_as_paid_common' => 'mark_as_paid_common',
|
||
'part_selection_internal' => 'part_selection_internal',
|
||
'redirect' => 'redirect',
|
||
'reformat_numbers' => 'reformat_numbers',
|
||
'retrieve_partunits' => 'retrieve_partunits',
|
||
'select_part' => 'select_part',
|
||
'select_part_internal' => 'select_part_internal',
|
||
'set_longdescription' => 'set_longdescription',
|
||
'show_history' => 'show_history',
|
Auch abrufbar als: Unified diff
Korrekturmodul für das Hauptbuch implementiert
Frührere Lx-Office-Versionen enthalten einige Bugs und Features,
die den Export von Buchungsdaten ins DATEV-Format verhindern und
allgemein zu ungültigen und/oder unlogischen Einträgen in acc_trans
führen. Mit Hilfe dieses Modules, das über den Menüpunkt "System ->
Korrekturen -> Korrekturen im Hauptbuch" erreichbar ist, können die
Auswirkungen dieser Bugs und Features teils automatisch, teils manuell
behoben werden.