|
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
|
S/C/PayPostingImport Prototyp
Verarbeitet DATEV Lohnbuchungen aus einer CSV-Datei