Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 8b8570b3

Von Kivitendo Admin vor mehr als 7 Jahren hinzugefügt

  • ID 8b8570b36cf3ec8f4c541c7caa3850c40472ca7a
  • Vorgänger b14b1780
  • Nachfolger 0a64ac3d

SL/DATEV.pm für KNE-Export überarbeitet / Zwischendaten eingeführt

_get_transactions war bisher eine interne Funktion von SL::DATEV, die vor dem
DATEV-Export aufgerufen wurde, und die Daten aus der Datenbank ausgelesen und
transformiert hat. In diesem Schritt wurde auch auf DATEV-Fehler geprüft, daher
war diese Funktion prinzipiell schon ausreichend für die DATEV-Checks beim
Buchen, und es muß nicht noch extra eine Datei-Export gestartet werden.

Im ersten Schritt wurde diese Funktion also umbenannt nach generate_datev_data.

Beim Erstellen des KNE-Formats aus den Daten wurde bisher direkt beim
Bearbeiten der Daten die KNE-Datei durch viele add_blocks aufgebaut. Jetzt
werden erst in einem Zwischenschritt alle Daten in einem "neutralen" Array von
Hashes gesammelt, so daß sie von dort in einem Rutsch nach KNE oder z.B. nach
CSV exportiert werden können.

Die so generierten Daten (generate_datev_lines) eignen sich auch gut für Tests.

In diversen Kundenerweiterungen werden auch gerne die zu exportierenden Daten
nochmal modifiziert, z.B. Ersetzen des Sammelkontos durch ein Personenkonto,
dies geschieht am Besten auch bei den neutralen Daten.

Weiterhin gab es beim KNE-Export noch Reste von Code, der die Buchungsdaten auf
mehrere Exportdateien "ED0001, ED0002, ..." verteilen konnte. Aktuell hat das
aber nicht funktioniert, und es wird immer alles nach ED0001 geschrieben, von
daher wurde hier auch Code entfernt. Das Postversendeformat (KNE) von DATEV
wird allerdings sowieso bald eingestellt werden (ab 2018).

Unterschiede anzeigen:

SL/DATEV.pm
364 364
  $_[0] <=> 0;
365 365
}
366 366

  
367
sub _get_transactions {
367
sub generate_datev_data {
368 368
  $main::lxdebug->enter_sub();
369 369

  
370 370
  my ($self, %params)   = @_;
......
821 821
  return $ev_header;
822 822
}
823 823

  
824
sub kne_buchungsexport {
825
  $main::lxdebug->enter_sub();
826

  
824
sub generate_datev_lines {
827 825
  my ($self) = @_;
828 826

  
829
  my $form = $::form;
830

  
831
  my @filenames;
832

  
833
  my $filename    = "ED00000";
834
  my $evfile      = "EV01";
835
  my @ed_versionset;
836
  my $fileno = 0;
837

  
838
  my $fromto = $self->fromto;
839

  
840
  $self->_get_transactions(from_to => $fromto);
841

  
842
  return if $self->errors;
843

  
844
  my $counter = 0;
845

  
846
  while (scalar(@{ $self->{DATEV} || [] })) {
847
    my $umsatzsumme = 0;
848
    $filename++;
849
    my $ed_filename = $self->export_path . $filename;
850
    push(@filenames, $filename);
851

  
852
    # transform $self->{DATEV} into an array of hashrefs containing all the
853
    # necessary information for the actual DATEV export, storing it in @kne_lines.
854
    my @kne_lines = ();
855
    while (scalar(@{ $self->{DATEV} }) > 0) {
856
      my %kne_data = ();
857
      my $transaction = shift @{ $self->{DATEV} };
858
      my $trans_lines = scalar(@{$transaction});
859
      $counter++;
860

  
861
      my $umsatz         = 0;
862
      my $gegenkonto     = "";
863
      my $konto          = "";
864
      my $belegfeld1     = "";
865
      my $datum          = "";
866
      my $waehrung       = "";
867
      my $buchungstext   = "";
868
      my $belegfeld2     = "";
869
      my $datevautomatik = 0;
870
      my $taxkey         = 0;
871
      my $charttax       = 0;
872
      my $ustid          ="";
873
      my ($haben, $soll);
874
      for (my $i = 0; $i < $trans_lines; $i++) {
875
        if ($trans_lines == 2) {
876
          if (abs($transaction->[$i]->{'amount'}) > abs($umsatz)) {
877
            $umsatz = $transaction->[$i]->{'amount'};
878
          }
879
        } else {
880
          if (abs($transaction->[$i]->{'umsatz'}) > abs($umsatz)) {
881
            $umsatz = $transaction->[$i]->{'umsatz'};
882
          }
883
        }
884
        if ($transaction->[$i]->{'datevautomatik'}) {
885
          $datevautomatik = 1;
886
        }
887
        if ($transaction->[$i]->{'taxkey'}) {
888
          $taxkey = $transaction->[$i]->{'taxkey'};
827
  my @datev_lines = ();
828

  
829
  foreach my $transaction ( @{ $self->{DATEV} } ) {
830

  
831
    # each $transaction entry contains data from several acc_trans entries
832
    # belonging to the same trans_id
833

  
834
    my %datev_data = (); # data for one transaction
835
    my $trans_lines = scalar(@{$transaction});
836

  
837
    my $umsatz         = 0;
838
    my $gegenkonto     = "";
839
    my $konto          = "";
840
    my $belegfeld1     = "";
841
    my $datum          = "";
842
    my $waehrung       = "";
843
    my $buchungstext   = "";
844
    my $belegfeld2     = "";
845
    my $datevautomatik = 0;
846
    my $taxkey         = 0;
847
    my $charttax       = 0;
848
    my $ustid          ="";
849
    my ($haben, $soll);
850
    for (my $i = 0; $i < $trans_lines; $i++) {
851
      if ($trans_lines == 2) {
852
        if (abs($transaction->[$i]->{'amount'}) > abs($umsatz)) {
853
          $umsatz = $transaction->[$i]->{'amount'};
889 854
        }
890
        if ($transaction->[$i]->{'charttax'}) {
891
          $charttax = $transaction->[$i]->{'charttax'};
892
        }
893
        if ($transaction->[$i]->{'amount'} > 0) {
894
          $haben = $i;
895
        } else {
896
          $soll = $i;
855
      } else {
856
        if (abs($transaction->[$i]->{'umsatz'}) > abs($umsatz)) {
857
          $umsatz = $transaction->[$i]->{'umsatz'};
897 858
        }
898 859
      }
860
      if ($transaction->[$i]->{'datevautomatik'}) {
861
        $datevautomatik = 1;
862
      }
863
      if ($transaction->[$i]->{'taxkey'}) {
864
        $taxkey = $transaction->[$i]->{'taxkey'};
865
      }
866
      if ($transaction->[$i]->{'charttax'}) {
867
        $charttax = $transaction->[$i]->{'charttax'};
868
      }
869
      if ($transaction->[$i]->{'amount'} > 0) {
870
        $haben = $i;
871
      } else {
872
        $soll = $i;
873
      }
874
    }
899 875

  
900
      if ($trans_lines >= 2) {
901

  
902
        $kne_data{'gegenkonto'} = $transaction->[$haben]->{'accno'};
903
        $kne_data{'konto'}      = $transaction->[$soll]->{'accno'};
904
        if ($transaction->[$haben]->{'invnumber'} ne "") {
905
          $kne_data{belegfeld1} = $transaction->[$haben]->{'invnumber'};
906
        }
907
        $kne_data{datum} = $transaction->[$haben]->{'transdate'};
908
        $kne_data{waehrung} = 'EUR';
876
    if ($trans_lines >= 2) {
909 877

  
910
        if ($transaction->[$haben]->{'name'} ne "") {
911
          $kne_data{buchungstext} = $transaction->[$haben]->{'name'};
912
        }
913
        if (($transaction->[$haben]->{'ustid'} // '') ne "") {
914
          $kne_data{ustid} = $transaction->[$haben]->{'ustid'};
915
        }
916
        if (($transaction->[$haben]->{'duedate'} // '') ne "") {
917
          $kne_data{belegfeld2} = $transaction->[$haben]->{'duedate'};
918
        }
878
      $datev_data{'gegenkonto'} = $transaction->[$haben]->{'accno'};
879
      $datev_data{'konto'}      = $transaction->[$soll]->{'accno'};
880
      if ($transaction->[$haben]->{'invnumber'} ne "") {
881
        $datev_data{belegfeld1} = $transaction->[$haben]->{'invnumber'};
919 882
      }
883
      $datev_data{datum} = $transaction->[$haben]->{'transdate'};
884
      $datev_data{waehrung} = 'EUR';
920 885

  
921
      $kne_data{umsatz} = abs($umsatz); # sales invoices without tax have a different sign???
922
      $umsatzsumme += $kne_data{umsatz}; #umsatz; # add the abs amount
923

  
924
      # Dies ist die einzige Stelle die datevautomatik auswertet. Was soll gesagt werden?
925
      # Im Prinzip hat jeder acc_trans Eintrag einen Steuerschlüssel, außer, bei gewissen Fällen
926
      # wie: Kreditorenbuchung mit negativen Vorzeichen, SEPA-Export oder Rechnungen die per
927
      # Skript angelegt werden.
928
      # Also falls ein Steuerschlüssel da ist und NICHT datevautomatik diesen Block hinzufügen.
929
      # Oder aber datevautomatik ist WAHR, aber der Steuerschlüssel in der acc_trans weicht
930
      # von dem in der Chart ab: Also wahrscheinlich Programmfehler (NULL übergeben, statt
931
      # DATEV-Steuerschlüssel) oder der Steuerschlüssel des Kontos weicht WIRKLICH von dem Eintrag in der
932
      # acc_trans ab. Gibt es für diesen Fall eine plausiblen Grund?
933
      #
934

  
935
      # only set buchungsschluessel if the following conditions are met:
936
      if (   ( $datevautomatik || $taxkey)
937
          && (!$datevautomatik || ($datevautomatik && ($charttax ne $taxkey)))) {
938
        # $kne_data{buchungsschluessel} = !$datevautomatik ? $taxkey : "4";
939
        $kne_data{buchungsschluessel} = $taxkey;
886
      if ($transaction->[$haben]->{'name'} ne "") {
887
        $datev_data{buchungstext} = $transaction->[$haben]->{'name'};
888
      }
889
      if (($transaction->[$haben]->{'ustid'} // '') ne "") {
890
        $datev_data{ustid} = $transaction->[$haben]->{'ustid'};
940 891
      }
892
      if (($transaction->[$haben]->{'duedate'} // '') ne "") {
893
        $datev_data{belegfeld2} = $transaction->[$haben]->{'duedate'};
894
      }
895
    }
941 896

  
942
      push(@kne_lines, \%kne_data);
897
    $datev_data{umsatz} = abs($umsatz); # sales invoices without tax have a different sign???
898

  
899
    # Dies ist die einzige Stelle die datevautomatik auswertet. Was soll gesagt werden?
900
    # Im Prinzip hat jeder acc_trans Eintrag einen Steuerschlüssel, außer, bei gewissen Fällen
901
    # wie: Kreditorenbuchung mit negativen Vorzeichen, SEPA-Export oder Rechnungen die per
902
    # Skript angelegt werden.
903
    # Also falls ein Steuerschlüssel da ist und NICHT datevautomatik diesen Block hinzufügen.
904
    # Oder aber datevautomatik ist WAHR, aber der Steuerschlüssel in der acc_trans weicht
905
    # von dem in der Chart ab: Also wahrscheinlich Programmfehler (NULL übergeben, statt
906
    # DATEV-Steuerschlüssel) oder der Steuerschlüssel des Kontos weicht WIRKLICH von dem Eintrag in der
907
    # acc_trans ab. Gibt es für diesen Fall eine plausiblen Grund?
908
    #
909

  
910
    # only set buchungsschluessel if the following conditions are met:
911
    if (   ( $datevautomatik || $taxkey)
912
        && (!$datevautomatik || ($datevautomatik && ($charttax ne $taxkey)))) {
913
      # $datev_data{buchungsschluessel} = !$datevautomatik ? $taxkey : "4";
914
      $datev_data{buchungsschluessel} = $taxkey;
943 915
    }
944 916

  
945
    # the data in @kne_lines is now ready to be transformed to a kne file, or even to csv
917
    push(@datev_lines, \%datev_data);
918
  }
946 919

  
947
    my $iconv   = $::locale->{iconv_utf8};
948
    my %umlaute = ($iconv->convert('ä') => 'ae',
949
                   $iconv->convert('ö') => 'oe',
950
                   $iconv->convert('ü') => 'ue',
951
                   $iconv->convert('Ä') => 'Ae',
952
                   $iconv->convert('Ö') => 'Oe',
953
                   $iconv->convert('Ü') => 'Ue',
954
                   $iconv->convert('ß') => 'sz');
920
  # example of modifying export data:
921
  # foreach my $datev_line ( @datev_lines ) {
922
  #   if ( $datev_line{"konto"} eq '1234' ) {
923
  #     $datev_line{"konto"} = '9999';
924
  #   }
925
  # }
926
  #
955 927

  
956
    my $header = $self->make_kne_data_header($form);
928
  return \@datev_lines;
929
}
957 930

  
958
    my $kne_file = SL::DATEV::KNEFile->new();
959
    $kne_file->add_block($header);
960
    # add the data from @kne_lines to the kne_file, formatting as needed
961
    foreach my $kne (@kne_lines) {
962 931

  
963
      $kne_file->add_block("+" . $kne_file->format_amount(abs($kne->{umsatz}), 0));
932
sub kne_buchungsexport {
933
  $main::lxdebug->enter_sub();
964 934

  
965
      # only add buchungsschluessel if it was previously defined
966
      $kne_file->add_block("\x6C" . $kne->{buchungsschluessel}) if defined $kne->{buchungsschluessel};
935
  my ($self) = @_;
967 936

  
968
      # ($kne->{gegenkonto}) = $kne->{gegenkonto} =~ /^(\d+)/;
969
      $kne_file->add_block("a" . trim_leading_zeroes($kne->{gegenkonto}));
937
  my $form = $::form;
970 938

  
971
      if ( $kne->{belegfeld1} ) {
972
        my $invnumber = $kne->{belegfeld1};
973
        foreach my $umlaut (keys(%umlaute)) {
974
          $invnumber =~ s/${umlaut}/${umlaute{$umlaut}}/g;
975
        }
976
        $invnumber =~ s/[^0-9A-Za-z\$\%\&\*\+\-\/]//g;
977
        $invnumber =  substr($invnumber, 0, 12);
978
        $invnumber =~ s/\ *$//;
979
        $kne_file->add_block("\xBD" . $invnumber . "\x1C");
980
      }
939
  my @filenames;
940

  
941
  my $filename    = "ED00001";
942
  my $evfile      = "EV01";
943
  my @ed_versionset;
944
  my $fileno      = 1;
945
  my $ed_filename = $self->export_path . $filename;
981 946

  
982
      $kne_file->add_block("\xBE" . &datetofour($kne->{belegfeld2},1) . "\x1C");
947
  my $fromto = $self->fromto;
948

  
949
  $self->generate_datev_data(from_to => $self->fromto); # fetches data from db, transforms data and fills $self->{DATEV}
950
  return if $self->errors;
983 951

  
984
      $kne_file->add_block("d" . &datetofour($kne->{datum},0));
952
  my @datev_lines = @{ $self->generate_datev_lines };
985 953

  
986
      # ($kne->{konto}) = $kne->{konto} =~ /^(\d+)/;
987
      $kne_file->add_block("e" . trim_leading_zeroes($kne->{konto}));
988 954

  
989
      my $name = $kne->{buchungstext};
955
  my $umsatzsumme = sum map { $_->{umsatz} } @datev_lines;
956

  
957
  # prepare kne file, everything gets stored in ED00001
958
  my $header = $self->make_kne_data_header($form);
959
  my $kne_file = SL::DATEV::KNEFile->new();
960
  $kne_file->add_block($header);
961

  
962
  my $iconv   = $::locale->{iconv_utf8};
963
  my %umlaute = ($iconv->convert('ä') => 'ae',
964
                 $iconv->convert('ö') => 'oe',
965
                 $iconv->convert('ü') => 'ue',
966
                 $iconv->convert('Ä') => 'Ae',
967
                 $iconv->convert('Ö') => 'Oe',
968
                 $iconv->convert('Ü') => 'Ue',
969
                 $iconv->convert('ß') => 'sz');
970

  
971
  # add the data from @datev_lines to the kne_file, formatting as needed
972
  foreach my $kne ( @datev_lines ) {
973
    $kne_file->add_block("+" . $kne_file->format_amount(abs($kne->{umsatz}), 0));
974

  
975
    # only add buchungsschluessel if it was previously defined
976
    $kne_file->add_block("\x6C" . $kne->{buchungsschluessel}) if defined $kne->{buchungsschluessel};
977

  
978
    # ($kne->{gegenkonto}) = $kne->{gegenkonto} =~ /^(\d+)/;
979
    $kne_file->add_block("a" . trim_leading_zeroes($kne->{gegenkonto}));
980

  
981
    if ( $kne->{belegfeld1} ) {
982
      my $invnumber = $kne->{belegfeld1};
990 983
      foreach my $umlaut (keys(%umlaute)) {
991
        $name =~ s/${umlaut}/${umlaute{$umlaut}}/g;
984
        $invnumber =~ s/${umlaut}/${umlaute{$umlaut}}/g;
992 985
      }
993
      $name =~ s/[^0-9A-Za-z\$\%\&\*\+\-\ \/]//g;
994
      $name =  substr($name, 0, 30);
995
      $name =~ s/\ *$//;
996
      $kne_file->add_block("\x1E" . $name . "\x1C");
986
      $invnumber =~ s/[^0-9A-Za-z\$\%\&\*\+\-\/]//g;
987
      $invnumber =  substr($invnumber, 0, 12);
988
      $invnumber =~ s/\ *$//;
989
      $kne_file->add_block("\xBD" . $invnumber . "\x1C");
990
    }
997 991

  
998
      $kne_file->add_block("\xBA" . $kne->{'ustid'}    . "\x1C") if $kne->{'ustid'};
992
    $kne_file->add_block("\xBE" . &datetofour($kne->{belegfeld2},1) . "\x1C");
999 993

  
1000
      $kne_file->add_block("\xB3" . $kne->{'waehrung'} . "\x1C" . "\x79");
1001
    };
994
    $kne_file->add_block("d" . &datetofour($kne->{datum},0));
1002 995

  
1003
    $umsatzsumme          = $kne_file->format_amount(abs($umsatzsumme), 0);
1004
    my $mandantenendsumme = "x" . $kne_file->format_amount($umsatzsumme / 100.0, 14) . "\x79\x7a";
996
    # ($kne->{konto}) = $kne->{konto} =~ /^(\d+)/;
997
    $kne_file->add_block("e" . trim_leading_zeroes($kne->{konto}));
1005 998

  
1006
    $kne_file->add_block($mandantenendsumme);
1007
    $kne_file->flush();
999
    my $name = $kne->{buchungstext};
1000
    foreach my $umlaut (keys(%umlaute)) {
1001
      $name =~ s/${umlaut}/${umlaute{$umlaut}}/g;
1002
    }
1003
    $name =~ s/[^0-9A-Za-z\$\%\&\*\+\-\ \/]//g;
1004
    $name =  substr($name, 0, 30);
1005
    $name =~ s/\ *$//;
1006
    $kne_file->add_block("\x1E" . $name . "\x1C");
1008 1007

  
1009
    open(ED, ">", $ed_filename) or die "can't open outputfile: $!\n";
1010
    print(ED $kne_file->get_data());
1011
    close(ED);
1008
    $kne_file->add_block("\xBA" . $kne->{'ustid'}    . "\x1C") if $kne->{'ustid'};
1012 1009

  
1013
    $ed_versionset[$fileno] = $self->make_ed_versionset($header, $filename, $kne_file->get_block_count());
1014
    $fileno++;
1015
  }
1010
    $kne_file->add_block("\xB3" . $kne->{'waehrung'} . "\x1C" . "\x79");
1011
  };
1012

  
1013
  $umsatzsumme          = $kne_file->format_amount(abs($umsatzsumme), 0);
1014
  my $mandantenendsumme = "x" . $kne_file->format_amount($umsatzsumme / 100.0, 14) . "\x79\x7a";
1015

  
1016
  $kne_file->add_block($mandantenendsumme);
1017
  $kne_file->flush();
1018

  
1019
  open(ED, ">", $ed_filename) or die "can't open outputfile: $!\n";
1020
  print(ED $kne_file->get_data());
1021
  close(ED);
1022

  
1023
  $ed_versionset[$fileno] = $self->make_ed_versionset($header, $filename, $kne_file->get_block_count());
1016 1024

  
1017 1025
  #Make EV Verwaltungsdatei
1018
  my $ev_header = $self->make_ev_header($form, $fileno);
1026
  my $ev_header   = $self->make_ev_header($form, $fileno);
1019 1027
  my $ev_filename = $self->export_path . $evfile;
1020 1028
  push(@filenames, $evfile);
1021 1029
  open(EV, ">", $ev_filename) or die "can't open outputfile: EV01\n";
......
1330 1338
  my $path     = $datev->export_path;
1331 1339
  my @files    = glob("$path/*");
1332 1340

  
1341
  # Only test the datev data of a specific trans_id, without generating an
1342
  # export file, but filling $datev->errors if errors exist
1343

  
1344
  my $datev = SL::DATEV->new(
1345
    trans_id   => $invoice->trans_id,
1346
  );
1347
  $datev->generate_datev_data;
1348
  # if ($datev->errors) { ...
1349

  
1350

  
1333 1351
=head1 DESCRIPTION
1334 1352

  
1335 1353
This module implements the DATEV export standard. For usage see above.
......
1342 1360

  
1343 1361
Generic constructor. See section attributes for information about what to pass.
1344 1362

  
1363
=item generate_datev_data
1364

  
1365
Fetches all transactions from the database (via a trans_id or a date range),
1366
and does an initial transformation (e.g. filters out tax, determines
1367
the brutto amount, checks split transactions ...) and stores this data in
1368
$self->{DATEV}.
1369

  
1370
If any errors are found these are collected in $self->errors.
1371

  
1372
This function is needed for all the exports, but can be also called
1373
independently in order to check transactions for DATEV compatibility.
1374

  
1375
=item generate_datev_lines
1376

  
1377
Parse the data in $self->{DATEV} and transform it into a format that can be
1378
used by DATEV, e.g. determines Konto and Gegenkonto, the taxkey, ...
1379

  
1380
The transformed data is returned as an arrayref, which is ready to be converted
1381
to a DATEV data format, e.g. KNE, OBE, CSV, ...
1382

  
1383
At this stage the "DATEV rule" has already been applied to the taxkeys, i.e.
1384
entries with datevautomatik have an empty taxkey, as the taxkey is already
1385
determined by the chart.
1386

  
1345 1387
=item get_datev_stamm
1346 1388

  
1347 1389
Loads DATEV Stammdaten and returns as hashref.

Auch abrufbar als: Unified diff