tags
crm
+/users/datev-export*
my ($notsplitindex);
my @errors = ();
+ $form->{net_gross_differences} = [];
+ $form->{sum_net_gross_differences} = 0;
+
$fromto =~ s/transdate/ac\.transdate/g;
my $taxkeys = Taxkeys->new();
my $ml = ($trans->[0]->{'umsatz'} > 0) ? 1 : -1;
my $rounding_error = 0;
+ my @taxed;
for my $j (0 .. (scalar(@{$trans}) - 1)) {
if ( ($j != $notsplitindex)
$absumsatz += -1 * $new_trans{'amount'};
} else {
- my $unrounded = $trans->[$j]->{'amount'} * (1 + $tax_rate) * -1; # + $rounding_error;
+ my $unrounded = $trans->[$j]->{'amount'} * (1 + $tax_rate) * -1 + $rounding_error;
my $rounded = $form->round_amount($unrounded, 2);
- $rounding_error += $unrounded - $rounded;
+
+ $rounding_error = $unrounded - $rounded;
$new_trans{'amount'} = $rounded;
- $new_trans{'umsatz'} = abs($form->round_amount(($trans->[$j]->{'amount'} * (1 + $tax_rate)), 2)) * $ml;
- $trans->[$j]->{'umsatz'} = abs($form->round_amount(($trans->[$j]->{'amount'} * (1 + $tax_rate)), 2)) * $ml;
- $absumsatz += $form->round_amount($trans->[$j]->{'amount'} + $trans->[$j]->{'amount'} * $tax_rate, 2);
+ $new_trans{'umsatz'} = abs($rounded) * $ml;
+ $trans->[$j]->{'umsatz'} = $new_trans{umsatz};
+ $absumsatz -= $rounded;
}
push @{ $form->{DATEV} }, [ \%new_trans, $trans->[$j] ];
+ push @taxed, $form->{DATEV}->[-1];
}
}
my $idx = 0;
my $correction = 0;
- our @taxed; # most likely defunct
- while (abs($absumsatz) >= 0.01) {
+ while ((abs($absumsatz) >= 0.01) && (abs($absumsatz) < 1.00)) {
if ($idx >= scalar @taxed) {
last if (!$correction);
$idx++;
}
- if (abs($absumsatz) >= 0.01) {
- push @errors, "Datev-Export fehlgeschlagen! Bei Transaktion $trans->[0]->{trans_id} ($absumsatz, Rundungsfehler $rounding_error)\n";
+ $absumsatz = $form->round_amount($absumsatz, 2);
+ if (abs($absumsatz) >= (0.01 * (1 + scalar @taxed))) {
+ push @errors, "Datev-Export fehlgeschlagen! Bei Transaktion $trans->[0]->{trans_id} ($absumsatz)\n";
+
+ } elsif (abs($absumsatz) >= 0.01) {
+ push @{ $form->{net_gross_differences} }, $absumsatz;
+ $form->{sum_net_gross_differences} += $absumsatz;
}
}
print(qq|<br><b>| . $locale->text('KNE-Export erfolgreich!') . qq|</b><br><br><a href="$link">Download</a>|);
+ print $form->parse_html_template('datev/net_gross_difference') if @{ $form->{net_gross_differences} };
+
} else {
$form->error("KNE-Export schlug fehl.");
}
'Number missing in Row' => 'Nummer fehlt in Zeile',
'Number of bins' => 'Anzahl Lagerplätze',
'Number of copies' => 'Anzahl Kopien',
+ 'Number of entries changed: #1' => 'Anzahl geänderter Einträge: #1',
'Number of new bins' => 'Anzahl neuer Lagerplätze',
'Number pages' => 'Seiten nummerieren',
'Number variables: \'PRECISION=n\' forces numbers to be shown with exactly n decimal places.' => 'Zahlenvariablen: Mit \'PRECISION=n\' erzwingt man, dass Zahlen mit n Nachkommastellen formatiert werden.',
--- /dev/null
+#!/usr/bin/perl
+
+$self->{texts} = {
+ 'Database update error:' => 'Fehler beim Datenbankupgrade:',
+ 'Number of entries changed: #1' => 'Anzahl geänderter Einträge: #1',
+};
+
+$self->{subs} = {
+ 'do_query' => 'do_query',
+ 'do_update' => 'do_update',
+ 'mydberror' => 'mydberror',
+};
+
+1;
--- /dev/null
+# @tag: fix_acc_trans_ap_taxkey_bug
+# @description: Korrektur falscher Steuerschlüssel in acc_trans bei Eingangsrechnungen
+# @depends: release_2_6_0
+
+use strict;
+
+die "This script cannot be run from the command line." unless $::form;
+
+sub mydberror {
+ my $msg = shift;
+ die $dbup_locale->text("Database update error:") . "<br>$msg<br>" . $DBI::errstr;
+}
+
+sub do_query {
+ my $query = shift;
+ my $may_fail = shift;
+
+ if (!$dbh->do($query)) {
+ mydberror($query) unless $may_fail;
+ $dbh->rollback();
+ $dbh->begin_work();
+ }
+}
+
+sub do_update {
+ my $q_find = <<SQL;
+ SELECT * FROM (
+ SELECT
+ -- Einige Felder zum Debuggen:
+ ap.id, c.accno, c.description AS chartdescription,
+ -- Felder, die zum eigentlichen Vergleich und zum spaeteren Update
+ -- benoetigt werden:
+ ac.acc_trans_id, ac.taxkey AS actual_taxkey,
+ -- Zum Rechnungsdatum gueltigen Steuerschluessel fuer Konto auslesen:
+ (SELECT tk.taxkey_id
+ FROM taxkeys tk
+ WHERE (tk.chart_id = c.id)
+ AND (tk.startdate <= ap.transdate)
+ ORDER BY tk.startdate DESC
+ LIMIT 1) AS wanted_taxkey
+ FROM acc_trans ac
+ LEFT JOIN ap ON (ac.trans_id = ap.id)
+ LEFT JOIN chart c ON (ac.chart_id = c.id)
+ WHERE
+ -- Nur Einkaufsrechnungen, aber keine Kreditorenbuchungen betrachten.
+ (ap.id IS NOT NULL)
+ AND ap.invoice
+ -- Nur Eintraege betrachten, die Konten bebuchen, die fuer die
+ -- jeweils aktuelle Rechnung in der dazugehoerigen Buchungsgruppe
+ -- angesprochen werden. Die Buchungsgruppen sind all diejenigen,
+ -- die in der Rechnung in mindestens einer Position ueber die
+ -- Parts verlinkt sind.
+ AND (ac.chart_id IN (
+ -- Teil 1: Aufwandskonto der Buchungsgruppe fuer die in der
+ -- aktuellen Rechnung ausgewaehlte Steuerzone
+ SELECT
+ CASE
+ WHEN ap.taxzone_id = 0 THEN bg.expense_accno_id_0
+ WHEN ap.taxzone_id = 1 THEN bg.expense_accno_id_1
+ WHEN ap.taxzone_id = 2 THEN bg.expense_accno_id_2
+ ELSE bg.expense_accno_id_3
+ END
+ FROM invoice i
+ LEFT JOIN parts p ON (i.parts_id = p.id)
+ LEFT JOIN buchungsgruppen bg ON (p.buchungsgruppen_id = bg.id)
+ WHERE (i.trans_id = ap.id)
+
+ UNION
+
+ -- Teil 2: Inventarkonto der Buchungsgruppe fuer Nicht-Dienstleistungen
+ SELECT bg.inventory_accno_id
+ FROM invoice i
+ LEFT JOIN parts p ON (i.parts_id = p.id)
+ LEFT JOIN buchungsgruppen bg ON (p.buchungsgruppen_id = bg.id)
+ WHERE (i.trans_id = ap.id)
+ AND (COALESCE(p.inventory_accno_id, 0) <> 0)
+ ))
+ ORDER BY ap.id
+ ) AS the_query
+ WHERE the_query.actual_taxkey <> the_query.wanted_taxkey
+SQL
+
+ my $q_change = <<SQL;
+ UPDATE acc_trans
+ SET taxkey = ?
+ WHERE acc_trans_id = ?
+SQL
+
+ my $h_find = $dbh->prepare($q_find) || mydberror($q_find);
+ my $h_change = $dbh->prepare($q_change) || mydberror($q_change);
+
+ $h_find->execute() || mydberror($q_find);
+
+ my $num_changed = 0;
+
+ while (my $ref = $h_find->fetchrow_hashref()) {
+ # $::lxdebug->dump(0, "ref", $ref);
+ $h_change->execute($ref->{wanted_taxkey}, $ref->{acc_trans_id}) || mydberror($q_change);
+ $num_changed++;
+ }
+
+ $h_find->finish();
+ $h_change->finish();
+
+ print $dbup_locale->text('Number of entries changed: #1', $num_changed) . "<br/>\n";
+}
+
+do_update();
--- /dev/null
+[% USE HTML %][% USE LxERP %]
+
+<p>
+ <b>Hinweis:</b>
+</p>
+
+<p>
+ Lx-Office speichert Buchungsdaten als aggregierte Nettowerte.
+
+ Das DATEV-Dateiformat hingegen erwartet aufgeteilte Buchungssätze mit
+ Bruttowerten.
+
+ Es ist deshalb technisch nicht immer möglich, aus den vorhandenen
+ aggregierten Nettowerten solche Bruttowerte zu errechnen, sodass für
+ einen Beleg betrachtet die von Lx-Office angezeigten und die in den
+ DATEV-Export geschriebenen Bruttowerte exakt übereinstimmen.
+
+ Abweichungen im Cent-Bereich sind in beide Richtungen möglich (Brutto
+ Lx-Office ist größer als Brutto DATEV und umgekehrt), werden aber von
+ den Steuerbehörden anstandslos akzeptiert.
+</p>
+
+<p>
+ In dem gerade durchgeführten Export gab es [% net_gross_differences.size %]
+ solcher Fälle. Die Summe aller Abweichungen beläuft sich auf
+ [% LxERP.format_amount(sum_net_gross_differences, 2) %].
+</p>
--- /dev/null
+[% USE HTML %][% USE LxERP %]
+
+<p>
+ <b>Hinweis:</b>
+</p>
+
+<p>
+ Lx-Office speichert Buchungsdaten als aggregierte Nettowerte.
+
+ Das DATEV-Dateiformat hingegen erwartet aufgeteilte Buchungssätze mit
+ Bruttowerten.
+
+ Es ist deshalb technisch nicht immer möglich, aus den vorhandenen
+ aggregierten Nettowerten solche Bruttowerte zu errechnen, sodass für
+ einen Beleg betrachtet die von Lx-Office angezeigten und die in den
+ DATEV-Export geschriebenen Bruttowerte exakt übereinstimmen.
+
+ Abweichungen im Cent-Bereich sind in beide Richtungen möglich (Brutto
+ Lx-Office ist größer als Brutto DATEV und umgekehrt), werden aber von
+ den Steuerbehörden anstandslos akzeptiert.
+</p>
+
+<p>
+ In dem gerade durchgeführten Export gab es [% net_gross_differences.size %]
+ solcher Fälle. Die Summe aller Abweichungen beläuft sich auf
+ [% LxERP.format_amount(sum_net_gross_differences, 2) %].
+</p>