Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision fb2cef4d

Von Jan Büren vor mehr als 3 Jahren hinzugefügt

  • ID fb2cef4de45ef83c47c01bd3af283ca2b2dd26ed
  • Vorgänger 33e90020
  • Nachfolger f5e39f21

S/C/PayPostingImport Prototyp

Verarbeitet DATEV Lohnbuchungen aus einer CSV-Datei

Unterschiede anzeigen:

SL/Controller/PayPostingImport.pm
1
package SL::Controller::PayPostingImport;
2
use strict;
3
use parent qw(SL::Controller::Base);
4

  
5
use SL::File;
6
use SL::Helper::DateTime;
7
use SL::Helper::Flash qw(flash_later);
8
use SL::Locale::String qw(t8);
9

  
10
use Carp;
11
use Text::CSV_XS;
12

  
13
__PACKAGE__->run_before('check_auth');
14

  
15

  
16
sub action_upload_pay_postings {
17
  my ($self, %params) = @_;
18

  
19
  $self->setup_pay_posting_action_bar;
20
  $self->render('pay_posting_import/form', title => $::locale->text('Import Pay Postings'));
21
}
22

  
23
sub action_import_datev_pay_postings {
24
  my ($self, %params) = @_;
25

  
26
  die t8("missing file for action import") unless ($::form->{file});
27

  
28
  my $filename= $::form->{ATTACHMENTS}{file}{filename};
29

  
30
  # check name and first fields of CSV data
31
  die t8("Wrong file name, expects name like: DTVF_*_LOHNBUCHUNG*.csv") unless $filename =~ /^DTVF_.*_LOHNBUCHUNGEN_LUG.*\.csv$/;
32
  die t8("not a valid DTVF file, expected first field in A1 'DTVF'")   unless ($::form->{file} =~ m/^"DTVF";/);
33
  die t8("not a valid DTVF file, expected field header start with 'Umsatz; (..) ;Konto;Gegenkonto'")
34
    unless ($::form->{file} =~ m/Umsatz;S\/H;;;;;Konto;Gegenkonto.*;;Belegdatum;Belegfeld 1;Belegfeld 2;;Buchungstext/);
35

  
36
  # check if file is already imported
37
  my $acc_trans_doc = SL::DB::Manager::AccTransaction->get_first(source => $filename);
38
  die t8("Already imported") if ref $acc_trans_doc eq 'SL::DB::AccTransaction';
39

  
40
  if (parse_and_import($::form->{file}, $filename)) {
41
    flash_later('info', t8("All pay postings successfully imported."));
42
  }
43
  # $self->redirect_to("gl.pl?action=search", source => $filename);
44
}
45

  
46
sub parse_and_import {
47
  my $doc      = shift;
48

  
49
  my $csv = Text::CSV_XS->new ({ binary => 0, auto_diag => 1, sep_char => ";" });
50
  open my $fh, "<:encoding(cp1252)", \$doc;
51
  # Read/parse CSV
52
  # Umsatz S/H Konto Gegenkonto (ohne BU-Schlüssel) Belegdatum Belegfeld 1 Belegfeld 2 Buchungstext
53
  my $year = substr($csv->getline($fh)->[12], 0, 4);
54

  
55
  # whole import or nothing
56
  my $current_transaction;
57
  SL::DB->client->with_transaction(sub {
58
    while (my $row = $csv->getline($fh)) {
59
      next unless $row->[0] =~ m/\d/;
60
      my ($credit, $debit, $dt_to_kivi, $length, $accno_credit, $accno_debit,
61
          $department_name, $department);
62

  
63
      # check valid soll/haben kennzeichen
64
      croak("No valid debit/credit sign") unless $row->[1] =~ m/^(S|H)$/;
65

  
66
      # check transaction date can be 4 or 3 digit (leading 0 omitted)
67
      $length = length $row->[9] == 4 ? 2 : 1;
68
      $dt_to_kivi = DateTime->new(year  => $year,
69
                                  month => substr ($row->[9], -2),
70
                                  day   => substr($row->[9],0, $length))->to_kivitendo;
71

  
72
      croak("Something wrong with date conversion") unless $dt_to_kivi;
73

  
74
      $accno_credit = $row->[1] eq 'S' ? $row->[7] : $row->[6];
75
      $accno_debit  = $row->[1] eq 'S' ? $row->[6] : $row->[7];
76
      $credit   = SL::DB::Manager::Chart->find_by(accno => $accno_credit);
77
      $debit    = SL::DB::Manager::Chart->find_by(accno => $accno_debit);
78

  
79
      croak("No such Chart $accno_credit") unless ref $credit eq 'SL::DB::Chart';
80
      croak("No such Chart $accno_debit")  unless ref $debit  eq 'SL::DB::Chart';
81

  
82
      # optional KOST1 - KOST2 ?
83
      $department_name = $row->[36];
84
      $department    = SL::DB::Manager::Department->get_first(description => { like =>  $department_name . '%' });
85

  
86
      my $amount = $::form->parse_amount({ numberformat => '1000,00' }, $row->[0]);
87

  
88
      $current_transaction = SL::DB::GLTransaction->new(
89
          employee_id    => $::form->{employee_id},
90
          transdate      => $dt_to_kivi,
91
          description    => $row->[13],
92
          reference      => $row->[13],
93
          department_id  => ref $department eq 'SL::DB::Department' ?  $department->id : undef,
94
          imported       => 1,
95
          taxincluded    => 1,
96
        )->add_chart_booking(
97
          chart  => $credit,
98
          credit => $amount,
99
          source => $::form->{ATTACHMENTS}{file}{filename},
100
        )->add_chart_booking(
101
          chart  => $debit,
102
          debit  => $amount,
103
          source => $::form->{ATTACHMENTS}{file}{filename},
104
      )->post;
105

  
106
      # push @rows, $current_transaction->id;
107

  
108
      if ($::instance_conf->get_doc_storage) {
109
        my $file = SL::File->save(object_id   => $current_transaction->id,
110
                       object_type => 'gl_transaction',
111
                       mime_type   => 'text/csv',
112
                       source      => 'uploaded',
113
                       file_type   => 'attachment',
114
                       file_name   => $::form->{ATTACHMENTS}{file}{filename},
115
                       file_contents   => $doc
116
                      );
117
      }
118
    }
119

  
120
    1;
121

  
122
  }) or do { die t8("Cannot add Booking, reason: #1 DB: #2 ", $@, SL::DB->client->error) };
123
}
124

  
125
sub check_auth {
126
  $::auth->assert('general_ledger');
127
}
128

  
129
sub setup_pay_posting_action_bar {
130
  my ($self) = @_;
131

  
132
  for my $bar ($::request->layout->get('actionbar')) {
133
    $bar->add(
134
      action => [
135
        $::locale->text('Import'),
136
        submit    => [ '#form', { action => 'PayPostingImport/import_datev_pay_postings' } ],
137
        accesskey => 'enter',
138
      ],
139
    );
140
  }
141
}
142

  
143
1;
144

  
145
__END__
146

  
147
=pod
148

  
149
=encoding utf8
150

  
151
=head1 NAME
152

  
153
SL::Controller::PayPostingImport
154
Controller for importing pay postings.
155
Currently only DATEV format is supported.
156

  
157

  
158
=head1 FUNCTIONS
159

  
160
=over 2
161

  
162
=item C<action_upload_pay_postings>
163

  
164
Simple upload form. HTML Form allows only CSV files.
165

  
166

  
167
=item C<action_import_datev_pay_postings>
168

  
169
Does some sanity checks for the CSV file according to the expected DATEV data structure
170
If successful calls the parse_and_import function
171

  
172
=item C<parse_and_import>
173

  
174
Internal function for parsing and importing every line of the CSV data as a GL Booking.
175
Adds the attribute imported for the GL Booking.
176
If a chart which uses a tax automatic is assigned the tax will be calculated with the
177
'tax_included' option, which defaults to the DATEV format.
178

  
179
Furthermore adds the original CSV filename for every AccTransaction and puts the CSV in every GL Booking
180
if the feature DMS is active.
181
If a Chart is missing or any kind of different error occurs the whole import including the DMS addition
182
will be aborted
183

  
184
=back
menus/user/00-erp.yaml
646 646
  access: ap_transactions
647 647
  params:
648 648
    action: ZUGFeRD/upload_zugferd
649
- parent: general_ledger
650
  id: pay_posting_import
651
  name: DATEV - Pay Postings Import
652
  icon: datev
653
  order: 495
654
  access: datev_export & client/feature_datev
655
  params:
656
    action: PayPostingImport/upload_pay_postings
649 657
- parent: general_ledger
650 658
  id: general_ledger_reports
651 659
  name: Reports
templates/webpages/pay_posting_import/form.html
1
[%- USE HTML %]
2
[%- USE LxERP %]
3
[%- USE L %]
4
[%- USE T8 %]
5
[%- INCLUDE 'common/flash.html' %]
6
<div class="listtop">[% FORM.title %]</div>
7
<p>
8
 [% "Import a File:" | $T8 %]
9
</p>
10
<form method="post" action="controller.pl" enctype="multipart/form-data" id="form">
11
  [% L.input_tag('file', '', type => 'file', accept => '.csv') %]
12
</form>

Auch abrufbar als: Unified diff