Revision 1d559eff
Von Jan Büren vor mehr als 6 Jahren hinzugefügt
SL/DATEV/CSV.pm | ||
---|---|---|
4 | 4 |
|
5 | 5 |
use SL::Locale::String qw(t8); |
6 | 6 |
use SL::DB::Datev; |
7 |
use SL::Helper::DateTime; |
|
7 | 8 |
|
8 | 9 |
use Carp; |
9 | 10 |
use DateTime; |
... | ... | |
205 | 206 |
}, # pos 40 |
206 | 207 |
); |
207 | 208 |
|
209 |
sub new { |
|
210 |
my $class = shift; |
|
211 |
my %data = @_; |
|
212 |
|
|
213 |
my $obj = bless {}, $class; |
|
214 |
|
|
215 |
croak(t8('We need a valid from date')) unless (ref $data{from} eq 'DateTime'); |
|
216 |
croak(t8('We need a valid to date')) unless (ref $data{to} eq 'DateTime'); |
|
217 |
croak(t8('We need a array of datev_lines')) unless (ref $data{datev_lines} eq 'ARRAY'); |
|
218 |
|
|
219 |
# TODO no params here, better class variables/values |
|
220 |
return _csv_buchungsexport(from => $data{from}, |
|
221 |
to => $data{to}, |
|
222 |
datev_lines => $data{datev_lines}, |
|
223 |
locked => $data{locked}, |
|
224 |
); |
|
225 |
|
|
226 |
$obj; |
|
227 |
} |
|
228 |
|
|
208 | 229 |
sub check_encoding { |
209 | 230 |
my ($test) = @_; |
210 | 231 |
return undef unless $test; |
... | ... | |
216 | 237 |
} |
217 | 238 |
} |
218 | 239 |
|
219 |
sub kivitendo_to_datev { |
|
240 |
sub _kivitendo_to_datev {
|
|
220 | 241 |
my ($self) = @_; |
221 | 242 |
|
222 | 243 |
my $entries = scalar (@kivitendo_to_datev); |
... | ... | |
224 | 245 |
return @kivitendo_to_datev; |
225 | 246 |
} |
226 | 247 |
|
227 |
sub generate_csv_header { |
|
228 |
my ($self, %params) = @_;
|
|
248 |
sub _generate_csv_header {
|
|
249 |
my %params = @_;
|
|
229 | 250 |
|
230 | 251 |
# we need from and to in YYYYDDMM |
231 |
croak "Wrong format for from" unless $params{from} =~ m/^[0-9]{8}$/; |
|
232 |
croak "Wrong format for to" unless $params{to} =~ m/^[0-9]{8}$/; |
|
252 |
croak "Wrong format for from $params{from}" unless $params{from} =~ m/^[0-9]{8}$/;
|
|
253 |
croak "Wrong format for to $params{to}" unless $params{to} =~ m/^[0-9]{8}$/;
|
|
233 | 254 |
|
234 | 255 |
# who knows if we want locking and when our fiscal year starts |
235 |
croak "Wrong state of locking" unless $params{locked} =~ m/(0|1)/; |
|
256 |
# croak "Wrong state of locking" unless $params{locked} =~ m/^(0|1)$/; |
|
257 |
my $locked = defined($params{locked}) ? 1 : 0; |
|
236 | 258 |
croak "No startdate of fiscal year" unless $params{first_day_of_fiscal_year} =~ m/^[0-9]{8}$/; |
237 | 259 |
|
238 | 260 |
|
... | ... | |
260 | 282 |
"EXTF", "300", 21, "Buchungsstapel", 7, $created_on, "", "ki", |
261 | 283 |
"kivitendo-datev", "", $meta_datev{beraternr}, $meta_datev{mandantennr}, |
262 | 284 |
$params{first_day_of_fiscal_year}, $length_of_accounts, |
263 |
$params{from}, $params{to}, "", "", 1, "", $params{locked},
|
|
285 |
$params{from}, $params{to}, "", "", 1, "", $locked,
|
|
264 | 286 |
$default_curr, "", "", "","" |
265 | 287 |
); |
266 | 288 |
|
267 | 289 |
return @header; |
268 | 290 |
} |
269 | 291 |
|
292 |
sub _csv_buchungsexport { |
|
293 |
my %params = @_; |
|
294 |
|
|
295 |
my @csv_columns = _kivitendo_to_datev(); |
|
296 |
my @csv_headers = _generate_csv_header( |
|
297 |
from => $params{from}->ymd(''), |
|
298 |
to => $params{to}->ymd(''), |
|
299 |
first_day_of_fiscal_year => $params{to}->year . '0101', |
|
300 |
locked => $params{locked} |
|
301 |
); |
|
302 |
|
|
303 |
my @array_of_datev; |
|
304 |
|
|
305 |
# 2 Headers |
|
306 |
push @array_of_datev, \@csv_headers; |
|
307 |
push @array_of_datev, [ map { $_->{csv_header_name} } @csv_columns ]; |
|
308 |
|
|
309 |
my @warnings; |
|
310 |
foreach my $row (@{ $params{datev_lines} }) { |
|
311 |
my @current_datev_row; |
|
312 |
|
|
313 |
# shorten strings |
|
314 |
if ($row->{belegfeld1}) { |
|
315 |
$row->{buchungsbes} = $row->{belegfeld1} if $row->{belegfeld1}; |
|
316 |
$row->{belegfeld1} = substr($row->{belegfeld1}, 0, 12); |
|
317 |
$row->{buchungsbes} = substr($row->{buchungsbes}, 0, 60); |
|
318 |
} |
|
319 |
|
|
320 |
$row->{datum} = DateTime->from_kivitendo($row->{datum})->strftime('%d%m'); |
|
321 |
|
|
322 |
$row->{kost1} = substr($row->{kost1}, 0, 8) if $row->{kost1}; |
|
323 |
$row->{kost2} = substr($row->{kost2}, 0, 8) if $row->{kost2}; |
|
324 |
|
|
325 |
# , as decimal point and trim for UstID |
|
326 |
$row->{umsatz} = _format_amount($row->{umsatz}); |
|
327 |
$row->{ustid} =~ s/\s//g if $row->{ustid}; # trim whitespace |
|
328 |
|
|
329 |
foreach my $column (@csv_columns) { |
|
330 |
if (exists $column->{max_length} && $column->{kivi_datev_name} ne 'not yet implemented') { |
|
331 |
# check max length |
|
332 |
die "Incorrect length of field" if length($row->{ $column->{kivi_datev_name} }) > $column->{max_length}; |
|
333 |
} |
|
334 |
if (exists $column->{valid_check} && $column->{kivi_datev_name} ne 'not yet implemented') { |
|
335 |
# more checks, listed as user warnings |
|
336 |
push @warnings, t8("Wrong field value '#1' for field '#2' for the transaction" . |
|
337 |
" with amount '#3'",$row->{ $column->{kivi_datev_name} }, |
|
338 |
$column->{kivi_datev_name},$row->{umsatz}) |
|
339 |
unless ($column->{valid_check}->($row->{ $column->{kivi_datev_name} })); |
|
340 |
} |
|
341 |
push @current_datev_row, $row->{ $column->{kivi_datev_name} }; |
|
342 |
} |
|
343 |
push @array_of_datev, \@current_datev_row; |
|
344 |
} |
|
345 |
return (\@array_of_datev, \@warnings); |
|
346 |
} |
|
347 |
|
|
270 | 348 |
sub _format_amount { |
271 | 349 |
$::form->format_amount({ numberformat => '1000,00' }, @_); |
272 | 350 |
} |
... | ... | |
283 | 361 |
|
284 | 362 |
=head1 SYNOPSIS |
285 | 363 |
|
364 |
use SL::DATEV qw(:CONSTANTS); |
|
365 |
use SL::DATEV::CSV; |
|
366 |
|
|
367 |
my $startdate = DateTime->new(year => 2014, month => 9, day => 1); |
|
368 |
my $enddate = DateTime->new(year => 2014, month => 9, day => 31); |
|
369 |
my $datev = SL::DATEV->new( |
|
370 |
exporttype => DATEV_ET_BUCHUNGEN, |
|
371 |
format => DATEV_FORMAT_CSV, |
|
372 |
from => $startdate, |
|
373 |
to => $enddate, |
|
374 |
); |
|
375 |
$datev->generate_datev_data; |
|
376 |
|
|
377 |
my $datev_ref = SL::DATEV::CSV->new(datev_lines => $datev->generate_datev_lines, |
|
378 |
from => $datev->from, |
|
379 |
to => $datev->to, |
|
380 |
locked => $datev->locked, |
|
381 |
); |
|
382 |
|
|
383 |
=head1 DESCRIPTION |
|
384 |
|
|
286 | 385 |
The parsing of the DATEV CSV is index based, therefore the correct |
287 | 386 |
column must be present at the corresponding index, i.e.: |
288 | 387 |
Index 2 |
... | ... | |
348 | 447 |
|
349 | 448 |
=over 4 |
350 | 449 |
|
450 |
=item new PARAMS |
|
451 |
|
|
452 |
Constructor for CSV-DATEV export. |
|
453 |
Checks mandantory params as described in section synopsis. |
|
454 |
|
|
351 | 455 |
=item check_encoding |
352 | 456 |
|
353 | 457 |
Helper function, returns true if a string is not empty and cp1252 encoded |
... | ... | |
366 | 470 |
C<params{from}>, C<params{to}> |
367 | 471 |
and C<params{first_day_of_fiscal_year}> have to be in YYYYDDMM date string |
368 | 472 |
format. |
369 |
Furthermore C<params{locked}> needs to be a boolean in number format (0|1).
|
|
473 |
Furthermore C<params{locked}> is a perlish boolean.
|
|
370 | 474 |
|
371 | 475 |
|
372 | 476 |
=item kivitendo_to_datev |
... | ... | |
379 | 483 |
Expects a number in kivitendo database format and returns the same number |
380 | 484 |
in DATEV format. |
381 | 485 |
|
486 |
=item _csv_buchungsexport |
|
487 |
|
|
488 |
Generates the CSV-Format data for the CSV DATEV export and returns |
|
489 |
an 2-dimensional array as an array_ref. |
|
490 |
May additionally return a second array_ref with warnings. |
|
491 |
|
|
492 |
Requires the same date fields as the constructor for a valid DATEV header. |
|
493 |
|
|
494 |
Furthermore we assume that the first day of the fiscal year is |
|
495 |
the first of January and we cannot guarantee that our data in kivitendo |
|
496 |
is locked, that means a booking cannot be modified after a defined (vat tax) |
|
497 |
period. |
|
498 |
Some validity checks (max_length and regex) will be done if the |
|
499 |
data structure contains them and the field is defined. |
|
500 |
|
|
501 |
To add or alter the structure of the data take a look at the C<@kivitendo_to_datev> structure. |
|
502 |
|
|
503 |
|
|
382 | 504 |
=back |
Auch abrufbar als: Unified diff
DATEV: csv_buchungsexport nach DATEV::CSV.pm ausgelagert
Testfälle angepasst. POD angepasst.
Details:
DATEV.pm
- Klassenvariable locked hinzugefügt.
- Aufruf der CSV-Klasse anstatt der internen Methode
CSV.pm
- Konstruktor wie in DATEV.pm ergänzt und um minimale
Pflichtfeldprüfung ergänzt.
- datetofour durch SL::Helper::DateTime ersetzt
- Helper _format_amount auch aufrufen
- Routinen umbenannt (pseudoprivat mit Unterstrich)
- Prüfung auf locked als perlish boolean
- _csv_buchungsexport um zweiten return array_ref mit warnungen ergänzt
t/datev/*
- Testfälle enstprechend dem neuen API-Call umgeschrieben
- Einen Testfall zur Überprüfung von keiner Warnung ergänzt