Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision eb0c10b0

Von Moritz Bunkus vor fast 16 Jahren hinzugefügt

  • ID eb0c10b0a09da8ff4590c3043a9fa6dd10775ee4
  • Vorgänger 72d93b0b
  • Nachfolger e20f3f0d

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.

Unterschiede anzeigen:

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&uuml;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 &uuml;berpr&uuml;fen Sie die Angaben in config/authentication.pl.',
'CANCELED' => 'Storniert',
'Cc' => 'Cc',
'Change Lx-Office installation settings (all menu entries beneath \'System\')' => 'Ver&auml;ndern der Lx-Office-Installationseinstellungen (Men&uuml;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&auml;ge erfassen und bearbeiten',
'Create and edit sales delivery orders' => 'Lieferscheine f&uuml;r Kunden erfassen und bearbeiten',
'Create and edit sales orders' => 'Auftragsbest&auml;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&auml;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&uuml;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&auml;hlen',
'Select a part' => 'Artikel ausw&auml;hlen',
'Select a vendor' => 'Einen Lieferanten ausw&auml;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 &uuml;berpr&uuml;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&uuml;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&uuml;sselt werden (Fehler bei SSL/TLS-Initialisierung). Bitte &uuml;berpr&uuml;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&auml;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&uuml;gen nicht &uuml;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&uuml;sselwort "DB_config" fehlt.',
'config/authentication.pl: Key "LDAP_config" is missing.' => 'config/authentication.pl: Der Schl&uuml;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&ouml;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&ouml;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',
... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet.

Auch abrufbar als: Unified diff