From 00b6dc2240564add9114864655e96eb7cc6dde4f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Sven=20Sch=C3=B6ling?= Date: Tue, 27 Oct 2015 18:24:15 +0100 Subject: [PATCH] =?utf8?q?GDPDU:=20DATEV-=C3=A4hnlicher=20Buchungsexport?= =?utf8?q?=20Rohversion?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- SL/Controller/Gdpdu.pm | 19 +-- SL/DATEV.pm | 170 +++++++++++++++++++++-- SL/GDPDU.pm | 199 ++++++++++++++++++++++++--- locale/de/all | 5 + templates/webpages/gdpdu/filter.html | 15 -- 5 files changed, 344 insertions(+), 64 deletions(-) diff --git a/SL/Controller/Gdpdu.pm b/SL/Controller/Gdpdu.pm index 8b0cbb063..871b0ab70 100644 --- a/SL/Controller/Gdpdu.pm +++ b/SL/Controller/Gdpdu.pm @@ -12,7 +12,7 @@ use SL::Locale::String qw(t8); use SL::Helper::Flash; use Rose::Object::MakeMethods::Generic ( - 'scalar --get_set_init' => [ qw(from to tables) ], + 'scalar --get_set_init' => [ qw(from to) ], ); __PACKAGE__->run_before('check_auth'); @@ -39,8 +39,7 @@ sub action_export { location => $::instance_conf->get_address, from => $self->from, to => $self->to, - tables => $self->tables, - all_tables => !@{ $self->tables } && $::form->{all_tables}, + all_tables => $::form->{all_tables}, ); my $filename = $gdpdu->generate_export; @@ -57,19 +56,6 @@ sub check_inputs { my $error = 0; - if ($::form->{tables}) { - $self->tables([ keys %{ $::form->{tables} } ]); - # theese three get inferred - push @{ $self->tables }, 'invoice' if $::form->{tables}{ar} || $::form->{tables}{ap}; - push @{ $self->tables }, 'orderitems' if $::form->{tables}{oe}; - push @{ $self->tables }, 'delivery_order_items' if $::form->{tables}{delivery_orders}; - } - - if (!@{ $self->tables } && !$::form->{all_tables}) { - flash('error', t8('No, I really do need checked tables to export.')); - $error = 1; - } - if (!$::form->{from}) { my $epoch = DateTime->new(day => 1, month => 1, year => 1900); flash('info', t8('No start date given, setting to #1', $epoch->to_kivitendo)); @@ -86,6 +72,5 @@ sub check_inputs { sub init_from { DateTime->from_kivitendo($::form->{from}) } sub init_to { DateTime->from_kivitendo($::form->{to}) } -sub init_tables { [ ] } 1; diff --git a/SL/DATEV.pm b/SL/DATEV.pm index be3ec958c..d99bd6600 100644 --- a/SL/DATEV.pm +++ b/SL/DATEV.pm @@ -32,12 +32,17 @@ use strict; use SL::DBUtils; use SL::DATEV::KNEFile; use SL::DB; +use SL::HTML::Util (); use Data::Dumper; use DateTime; use Exporter qw(import); use File::Path; -use List::Util qw(max sum); +use IO::File; +use List::MoreUtils qw(any); +use List::Util qw(min max sum); +use List::UtilsBy qw(partition_by sort_by); +use Text::CSV_XS; use Time::HiRes qw(gettimeofday); { @@ -45,13 +50,14 @@ use Time::HiRes qw(gettimeofday); use constant { DATEV_ET_BUCHUNGEN => $i++, DATEV_ET_STAMM => $i++, + DATEV_ET_CSV => $i++, DATEV_FORMAT_KNE => $i++, DATEV_FORMAT_OBE => $i++, }; } -my @export_constants = qw(DATEV_ET_BUCHUNGEN DATEV_ET_STAMM DATEV_FORMAT_KNE DATEV_FORMAT_OBE); +my @export_constants = qw(DATEV_ET_BUCHUNGEN DATEV_ET_STAMM DATEV_ET_CSV DATEV_FORMAT_KNE DATEV_FORMAT_OBE); our @EXPORT_OK = (@export_constants); our %EXPORT_TAGS = (CONSTANTS => [ @export_constants ]); @@ -324,6 +330,8 @@ sub kne_export { $result = $self->kne_buchungsexport; } elsif ($self->exporttype == DATEV_ET_STAMM) { $result = $self->kne_stammdatenexport; + } elsif ($self->exporttype == DATEV_ET_CSV) { + $result = $self->csv_export_for_tax_accountant; } else { die 'unrecognized exporttype'; } @@ -349,9 +357,10 @@ sub _sign { sub _get_transactions { $main::lxdebug->enter_sub(); - my $self = shift; - my $fromto = shift; - my $progress_callback = shift || sub {}; + + my ($self, %params) = @_; + my $fromto = $params{from_to}; + my $progress_callback = $params{progress_callback} || sub {}; my $form = $main::form; @@ -374,18 +383,21 @@ sub _get_transactions { my %all_taxchart_ids = selectall_as_map($form, $self->dbh, qq|SELECT DISTINCT chart_id, TRUE AS is_set FROM tax|, 'chart_id', 'is_set'); my $query = - qq|SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,ar.id, ac.amount, ac.taxkey, + qq|SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,ar.id, ac.amount, ac.taxkey, ac.memo, ar.invnumber, ar.duedate, ar.amount as umsatz, ar.deliverydate, - ct.name, ct.ustid, - c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, ac.chart_link AS link, + ct.name, ct.ustid, ct.customernumber AS vcnumber, ct.id AS customer_id, NULL AS vendor_id, + c.accno, c.description AS accname, c.taxkey_id as charttax, c.datevautomatik, c.id, ac.chart_link AS link, ar.invoice, t.rate AS taxrate, 'ar' as table + tc.accno AS tax_accno, tc.description AS tax_accname, + ar.notes FROM acc_trans ac LEFT JOIN ar ON (ac.trans_id = ar.id) LEFT JOIN customer ct ON (ar.customer_id = ct.id) LEFT JOIN chart c ON (ac.chart_id = c.id) LEFT JOIN tax t ON (ac.tax_id = t.id) + LEFT JOIN chart tc ON (t.chart_id = tc.id) WHERE (ar.id IS NOT NULL) AND $fromto $trans_id_filter @@ -393,18 +405,21 @@ sub _get_transactions { UNION ALL - SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,ap.id, ac.amount, ac.taxkey, + SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,ap.id, ac.amount, ac.taxkey, ac.memo, ap.invnumber, ap.duedate, ap.amount as umsatz, ap.deliverydate, - ct.name,ct.ustid, - c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, ac.chart_link AS link, + ct.name, ct.ustid, ct.vendornumber AS vcnumber, NULL AS customer_id, ct.id AS vendor_id, + c.accno, c.description AS accname, c.taxkey_id as charttax, c.datevautomatik, c.id, ac.chart_link AS link, ap.invoice, t.rate AS taxrate, 'ap' as table + tc.accno AS tax_accno, tc.description AS tax_accname, + ap.notes FROM acc_trans ac LEFT JOIN ap ON (ac.trans_id = ap.id) LEFT JOIN vendor ct ON (ap.vendor_id = ct.id) LEFT JOIN chart c ON (ac.chart_id = c.id) LEFT JOIN tax t ON (ac.tax_id = t.id) + LEFT JOIN chart tc ON (t.chart_id = tc.id) WHERE (ap.id IS NOT NULL) AND $fromto $trans_id_filter @@ -412,17 +427,20 @@ sub _get_transactions { UNION ALL - SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,gl.id, ac.amount, ac.taxkey, + SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,gl.id, ac.amount, ac.taxkey, ac.memo, gl.reference AS invnumber, gl.transdate AS duedate, ac.amount as umsatz, NULL as deliverydate, - gl.description AS name, NULL as ustid, - c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, ac.chart_link AS link, + gl.description AS name, NULL as ustid, '' AS vcname, NULL AS customer_id, NULL AS vendor_id, + c.accno, c.description AS accname, c.taxkey_id as charttax, c.datevautomatik, c.id, ac.chart_link AS link, FALSE AS invoice, t.rate AS taxrate, 'gl' as table + tc.accno AS tax_accno, tc.description AS tax_accname, + gl.notes FROM acc_trans ac LEFT JOIN gl ON (ac.trans_id = gl.id) LEFT JOIN chart c ON (ac.chart_id = c.id) LEFT JOIN tax t ON (ac.tax_id = t.id) + LEFT JOIN chart tc ON (t.chart_id = tc.id) WHERE (gl.id IS NOT NULL) AND $fromto $trans_id_filter @@ -812,7 +830,7 @@ sub kne_buchungsexport { my $fromto = $self->fromto; - $self->_get_transactions($fromto); + $self->_get_transactions(from_to => $fromto); return if $self->errors; @@ -1082,6 +1100,128 @@ sub kne_stammdatenexport { return { 'download_token' => $self->download_token, 'filenames' => \@filenames }; } +sub _format_accno { + my ($accno) = @_; + return $accno . ('0' x (6 - min(length($accno), 6))); +} + +sub csv_export_for_tax_accountant { + my ($self) = @_; + + $self->_get_transactions(from_to => $self->fromto); + + foreach my $transaction (@{ $self->{DATEV} }) { + foreach my $entry (@{ $transaction }) { + $entry->{sortkey} = join '-', map { lc } (DateTime->from_kivitendo($entry->{transdate})->strftime('%Y%m%d'), $entry->{name}, $entry->{reference}); + } + } + + my %transactions = + partition_by { $_->[0]->{table} } + sort_by { $_->[0]->{sortkey} } + grep { 2 == scalar(@{ $_ }) } + @{ $self->{DATEV} }; + + my %column_defs = ( + acc_trans_id => { 'text' => $::locale->text('ID'), }, + amount => { 'text' => $::locale->text('Amount'), }, + credit_accname => { 'text' => $::locale->text('Credit Account Name'), }, + credit_accno => { 'text' => $::locale->text('Credit Account'), }, + debit_accname => { 'text' => $::locale->text('Debit Account Name'), }, + debit_accno => { 'text' => $::locale->text('Debit Account'), }, + invnumber => { 'text' => $::locale->text('Reference'), }, + name => { 'text' => $::locale->text('Name'), }, + notes => { 'text' => $::locale->text('Notes'), }, + tax => { 'text' => $::locale->text('Tax'), }, + taxkey => { 'text' => $::locale->text('Taxkey'), }, + tax_accname => { 'text' => $::locale->text('Tax Account Name'), }, + tax_accno => { 'text' => $::locale->text('Tax Account'), }, + transdate => { 'text' => $::locale->text('Invoice Date'), }, + vcnumber => { 'text' => $::locale->text('Customer/Vendor Number'), }, + ); + + my @columns = qw( + acc_trans_id name vcnumber + transdate invnumber amount + debit_accno debit_accname + credit_accno credit_accname + tax + tax_accno tax_accname taxkey + notes + ); + + my %filenames_by_type = ( + ar => $::locale->text('AR Transactions'), + ap => $::locale->text('AP Transactions'), + gl => $::locale->text('GL Transactions'), + ); + + my @filenames; + foreach my $type (qw(ap ar)) { + my %csvs = ( + invoices => { + content => '', + filename => sprintf('%s %s - %s.csv', $filenames_by_type{$type}, $self->from->to_kivitendo, $self->to->to_kivitendo), + csv => Text::CSV_XS->new({ + binary => 1, + eol => "\n", + sep_char => ";", + }), + }, + payments => { + content => '', + filename => sprintf('Zahlungen %s %s - %s.csv', $filenames_by_type{$type}, $self->from->to_kivitendo, $self->to->to_kivitendo), + csv => Text::CSV_XS->new({ + binary => 1, + eol => "\n", + sep_char => ";", + }), + }, + ); + + foreach my $csv (values %csvs) { + $csv->{out} = IO::File->new($self->export_path . '/' . $csv->{filename}, '>:encoding(utf8)') ; + $csv->{csv}->print($csv->{out}, [ map { $column_defs{$_}->{text} } @columns ]); + + push @filenames, $csv->{filename}; + } + + foreach my $transaction (@{ $transactions{$type} }) { + my $is_payment = any { $_->{link} =~ m{A[PR]_paid} } @{ $transaction }; + my $csv = $is_payment ? $csvs{payments} : $csvs{invoices}; + + my ($soll, $haben) = map { $transaction->[$_] } ($transaction->[0]->{amount} > 0 ? (1, 0) : (0, 1)); + my $tax = defined($soll->{tax_accno}) ? $soll : $haben; + my $amount = defined($soll->{net_amount}) ? $soll : $haben; + $haben->{notes} = ($haben->{memo} || $soll->{memo}) if $is_payment; + $haben->{notes} //= ''; + $haben->{notes} = SL::HTML::Util->strip($haben->{notes}); + $haben->{notes} =~ s{\r}{}g; + $haben->{notes} =~ s{\n+}{ }g; + + my %row = ( + amount => $::form->format_amount({ numberformat => '1000,00' }, abs($amount->{amount}), 2), + debit_accno => _format_accno($soll->{accno}), + debit_accname => $soll->{accname}, + credit_accno => _format_accno($haben->{accno}), + credit_accname => $haben->{accname}, + tax => $::form->format_amount({ numberformat => '1000,00' }, abs($amount->{amount}) - abs($amount->{net_amount}), 2), + notes => $haben->{notes}, + (map { ($_ => $tax->{$_}) } qw(taxkey tax_accname tax_accno)), + (map { ($_ => ($haben->{$_} // $soll->{$_})) } qw(acc_trans_id invnumber name vcnumber transdate)), + ); + + $csv->{csv}->print($csv->{out}, [ map { $row{$_} } @columns ]); + } + + $_->{out}->close for values %csvs; + } + + $self->add_filenames(@filenames); + + return { download_token => $self->download_token, filenames => \@filenames }; +} + sub DESTROY { clean_temporary_directories(); } diff --git a/SL/GDPDU.pm b/SL/GDPDU.pm index 30d0361f6..0978df9f3 100644 --- a/SL/GDPDU.pm +++ b/SL/GDPDU.pm @@ -13,15 +13,16 @@ use XML::Writer; use Archive::Zip; use File::Temp (); use File::Spec (); -use List::UtilsBy qw(partition_by); +use List::MoreUtils qw(any); +use List::UtilsBy qw(partition_by sort_by); use SL::DB::Helper::ALL; # since we work on meta data, we need everything use SL::DB::Helper::Mappings; use SL::Locale::String qw(t8); use Rose::Object::MakeMethods::Generic ( - scalar => [ qw(from to tables writer company location) ], - 'scalar --get_set_init' => [ qw(files tempfiles export_ids) ], + scalar => [ qw(from to writer company location) ], + 'scalar --get_set_init' => [ qw(files tempfiles export_ids tables) ], ); # in this we find: @@ -32,19 +33,41 @@ use Rose::Object::MakeMethods::Generic ( # keep: arrayref of columns that should be saved for further referencing # tables: arrayref with one column and one or many table.column references that were kept earlier my %known_tables = ( - ar => { name => t8('Invoice'), description => t8('Sales Invoices and Accounts Receivables'), keep => [ qw(id customer_id vendor_id) ], transdate => 'transdate', }, - ap => { name => t8('Purchase Invoice'), description => t8('Purchase Invoices and Accounts Payables'), keep => [ qw(id customer_id vendor_id) ], transdate => 'transdate', }, - oe => { name => t8('Orders'), description => t8('Orders and Quotations, Sales and Purchase'), keep => [ qw(id customer_id vendor_id) ], transdate => 'transdate', }, - delivery_orders => { name => t8('Delivery Orders'), description => t8('Delivery Orders'), keep => [ qw(id customer_id vendor_id) ], transdate => 'transdate', }, - gl => { name => t8('General Ledger'), description => t8('General Ledger Entries'), keep => [ qw(id) ], transdate => 'transdate', }, - invoice => { name => t8('Invoice Positions'), description => t8('Positions for all Invoices'), keep => [ qw(parts_id) ], tables => [ trans_id => "ar.id", "ap.id" ] }, - orderitems => { name => t8('OrderItems'), description => t8('Positions for all Orders'), keep => [ qw(parts_id) ], tables => [ trans_id => "oe.id" ] }, - delivery_order_items => { name => t8('Delivery Order Items'), description => t8('Positions for all Delivery Orders'), keep => [ qw(parts_id) ], tables => [ delivery_order_id => "delivery_orders.id" ] }, - acc_trans => { name => t8('Transactions'), description => t8('All general ledger entries'), keep => [ qw(chart_id) ], tables => [ trans_id => "ar.id", "ap.id", "oe.id", "delivery_orders.id", "gl.id" ] }, - chart => { name => t8('Charts'), description => t8('Chart of Accounts'), tables => [ id => "acc_trans.chart_id" ] }, - customer => { name => t8('Customers'), description => t8('Customer Master Data'), tables => [ id => "ar.customer_id", "ap.customer_id", "oe.customer_id", "delivery_orders.customer_id" ] }, - vendor => { name => t8('Vendors'), description => t8('Vendor Master Data'), tables => [ id => "ar.vendor_id", "ap.vendor_id", "oe.vendor_id", "delivery_orders.vendor_id" ] }, - parts => { name => t8('Parts'), description => t8('Parts, Services, and Assemblies'), tables => [ id => "invoice.parts_id", "orderitems.parts_id", "delivery_order_items.parts_id" ] }, + chart => { name => t8('Charts'), description => t8('Chart of Accounts'), primary_key => 'accno' }, + customer => { name => t8('Customers'), description => t8('Customer Master Data'), }, + vendor => { name => t8('Vendors'), description => t8('Vendor Master Data'), }, +); + +my %datev_column_defs = ( + acc_trans_id => { type => 'Rose::DB::Object::Metadata::Column::Integer', text => t8('ID'), primary_key => 1 }, + amount => { type => 'Rose::DB::Object::Metadata::Column::Numeric', text => t8('Amount'), }, + credit_accname => { type => 'Rose::DB::Object::Metadata::Column::Text', text => t8('Credit Account Name'), }, + credit_accno => { type => 'Rose::DB::Object::Metadata::Column::Text', text => t8('Credit Account'), }, + debit_accname => { type => 'Rose::DB::Object::Metadata::Column::Text', text => t8('Debit Account Name'), }, + debit_accno => { type => 'Rose::DB::Object::Metadata::Column::Text', text => t8('Debit Account'), }, + invnumber => { type => 'Rose::DB::Object::Metadata::Column::Text', text => t8('Reference'), }, + name => { type => 'Rose::DB::Object::Metadata::Column::Text', text => t8('Name'), }, + notes => { type => 'Rose::DB::Object::Metadata::Column::Text', text => t8('Notes'), }, + tax => { type => 'Rose::DB::Object::Metadata::Column::Text', text => t8('Tax'), }, + taxkey => { type => 'Rose::DB::Object::Metadata::Column::Integer', text => t8('Taxkey'), }, + tax_accname => { type => 'Rose::DB::Object::Metadata::Column::Text', text => t8('Tax Account Name'), }, + tax_accno => { type => 'Rose::DB::Object::Metadata::Column::Text', text => t8('Tax Account'), }, + transdate => { type => 'Rose::DB::Object::Metadata::Column::Date', text => t8('Invoice Date'), }, + vcnumber => { type => 'Rose::DB::Object::Metadata::Column::Text', text => t8('Customer/Vendor Number'), }, + customer_id => { type => 'Rose::DB::Object::Metadata::Column::Integer', text => t8('Customer ID'), }, + vendor_id => { type => 'Rose::DB::Object::Metadata::Column::Integer', text => t8('Vendor ID'), }, +); + +my @datev_columns = qw( + acc_trans_id + customer_id vendor_id + name vcnumber + transdate invnumber amount + debit_accno debit_accname + credit_accno credit_accname + tax + tax_accno tax_accname taxkey + notes ); # rows in this listing are tiers. @@ -108,6 +131,8 @@ sub generate_export { $self->do_csv_export($_); } + $self->do_datev_csv_export; + # write xml file $self->do_xml_file; @@ -157,6 +182,7 @@ sub do_xml_file { for (reverse $self->sorted_tables) { $self # see CAVEATS for table order ->table($_) } + $self->do_datev_xml_table; }) }); close($fh); @@ -194,7 +220,11 @@ sub _table_columns { my $package = SL::DB::Helper::Mappings::get_package_for_table($table); # PrimaryKeys must come before regular columns, so partition first - partition_by { 1 * $_->is_primary_key_member } $package->meta->columns; + partition_by { + $known_tables{$table}{primary_key} + ? 1 * ($_ eq $known_tables{$table}{primary_key}) + : 1 * $_->is_primary_key_member + } $package->meta->columns; } sub columns { @@ -255,6 +285,140 @@ sub foreign_keys { } } +sub do_datev_xml_table { + my ($self) = @_; + my $writer = $self->writer; + + $self->tag('Table', sub { $self + ->tag('URL', "transaction.csv") + ->tag('Name', t8('Transactions')) + ->tag('Description', t8('Transactions')) + ->tag('Validity', sub { $self + ->tag('Range', sub { $self + ->tag('From', $self->from->to_kivitendo(dateformat => 'dd.mm.yyyy')) + ->tag('To', $self->to->to_kivitendo(dateformat => 'dd.mm.yyyy')) + }) + ->tag('Format', $date_format) + }) + ->tag('UTF8') + ->tag('DecimalSymbol', '.') + ->tag('DigitGroupingSymbol', '|') # see CAVEATS in documentation + ->tag('VariableLength', sub { $self + ->tag('ColumnDelimiter', ',') # see CAVEATS for missing RecordDelimiter + ->tag('TextEncapsulator', '"') + ->datev_columns + ->datev_foreign_keys + }) + }); +} + +sub datev_columns { + my ($self, $table) = @_; + + my %cols_by_primary_key = partition_by { $datev_column_defs{$_}{primary_key} } @datev_columns; + $::lxdebug->dump(0, "cols", \%cols_by_primary_key); + + for my $column (@{ $cols_by_primary_key{1} }) { + my $type = $column_types{ $datev_column_defs{$column}{type} }; + + die "unknown col type @{[ $column ]}" unless $type; + + $self->tag('VariablePrimaryKey', sub { $self + ->tag('Name', $column); + $type->($self); + }) + } + + for my $column (@{ $cols_by_primary_key{''} }) { + my $type = $column_types{ $datev_column_defs{$column}{type} }; + + die "unknown col type @{[ ref $column]}" unless $type; + + $self->tag('VariableColumn', sub { $self + ->tag('Name', $column); + $type->($self); + }) + } + + $self; +} + +sub datev_foreign_keys { + my ($self) = @_; + # hard code weeee + $self->tag('ForeignKey', sub { $_[0] + ->tag('Name', 'customer_id') + ->tag('References', 'customer') + }); + $self->tag('ForeignKey', sub { $_[0] + ->tag('Name', 'vendor_id') + ->tag('References', 'vendor') + }); + $self->tag('ForeignKey', sub { $_[0] + ->tag('Name', $_) + ->tag('References', 'chart') + }) for qw(debit_accno credit_accno tax_accno); +} + +sub do_datev_csv_export { + my ($self) = @_; + + my $datev = SL::DATEV->new(from => $self->from, to => $self->to); + + $datev->_get_transactions(from_to => $datev->fromto); + + for my $transaction (@{ $datev->{DATEV} }) { + for my $entry (@{ $transaction }) { + $entry->{sortkey} = join '-', map { lc } (DateTime->from_kivitendo($entry->{transdate})->strftime('%Y%m%d'), $entry->{name}, $entry->{reference}); + } + } + + my @transactions = sort_by { $_->[0]->{sortkey} } @{ $datev->{DATEV} }; + + my $csv = Text::CSV_XS->new({ + binary => 1, + eol => "\n", + sep_char => ";", + }); + + my ($fh, $filename) = File::Temp::tempfile(); + binmode($fh, ':utf8'); + + $self->files->{"transactions.csv"} = $filename; + push @{ $self->tempfiles }, $filename; + + for my $transaction (@transactions) { + my $is_payment = any { $_->{link} =~ m{A[PR]_paid} } @{ $transaction }; + + my ($soll, $haben) = map { $transaction->[$_] } ($transaction->[0]->{amount} > 0 ? (1, 0) : (0, 1)); + my $tax = defined($soll->{tax_accno}) ? $soll : $haben; + my $amount = defined($soll->{net_amount}) ? $soll : $haben; + $haben->{notes} = ($haben->{memo} || $soll->{memo}) if $haben->{memo} || $soll->{memo}; + $haben->{notes} //= ''; + $haben->{notes} = SL::HTML::Util->strip($haben->{notes}); + $haben->{notes} =~ s{\r}{}g; + $haben->{notes} =~ s{\n+}{ }g; + + my %row = ( + customer_id => $soll->{customer_id} || $haben->{customer_id}, + vendor_id => $soll->{vendor_id} || $haben->{vendor_id}, + amount => abs($amount->{amount}), + debit_accno => $soll->{accno}, + debit_accname => $soll->{accname}, + credit_accno => $haben->{accno}, + credit_accname => $haben->{accname}, + tax => abs($amount->{amount}) - abs($amount->{net_amount}), + notes => $haben->{notes}, + (map { ($_ => $tax->{$_}) } qw(taxkey tax_accname tax_accno)), + (map { ($_ => ($haben->{$_} // $soll->{$_})) } qw(acc_trans_id invnumber name vcnumber transdate)), + ); + + $csv->print($fh, [ map { $row{$_} } @datev_columns ]); + } + + # and build xml spec for it +} + sub do_csv_export { my ($self, $table) = @_; @@ -382,6 +546,7 @@ sub all_tables { sub init_files { +{} } sub init_export_ids { +{} } sub init_tempfiles { [] } +sub init_tables { [ grep { $known_tables{$_} } @export_table_order ] } sub API_VERSION { DateTime->new(year => 2002, month => 8, day => 14)->to_kivitendo; diff --git a/locale/de/all b/locale/de/all index 5e70cbb5c..a1b99468e 100755 --- a/locale/de/all +++ b/locale/de/all @@ -717,6 +717,7 @@ $self->{texts} = { 'Credit' => 'Haben', 'Credit (one letter abbreviation)' => 'H', 'Credit Account' => 'Habenkonto', + 'Credit Account Name' => 'Haben-Kontoname', 'Credit Limit' => 'Kreditlimit', 'Credit Limit exceeded!!!' => 'Kreditlimit überschritten!', 'Credit Note' => 'Gutschrift', @@ -832,6 +833,7 @@ $self->{texts} = { 'Debit' => 'Soll', 'Debit (one letter abbreviation)' => 'S', 'Debit Account' => 'Sollkonto', + 'Debit Account Name' => 'Soll-Kontoname', 'Debit Starting Balance' => 'EB Passiva', 'Debit Tax' => 'Vorsteuer', 'Debit Tax Account' => 'Vorsteuerkonto', @@ -1270,6 +1272,7 @@ $self->{texts} = { 'Export date' => 'Exportdatum', 'Export date from' => 'Exportdatum von', 'Export date to' => 'Exportdatum bis', + 'Export for tax accountant' => 'Export für Steuerberater', 'Extend automatically by n months' => 'Automatische Verlängerung um x Monate', 'Extended' => 'Gesamt', 'Extended status' => 'Erweiterter Status', @@ -2716,6 +2719,8 @@ $self->{texts} = { 'Task server control' => 'Task-Server-Steuerung', 'Task server status' => 'Task-Server-Status', 'Tax' => 'Steuer', + 'Tax Account' => 'Steuerkonto', + 'Tax Account Name' => 'Steuerkontoname', 'Tax Consultant' => 'Steuerberater/-in', 'Tax ID number' => 'UStID-Nummer', 'Tax Included' => 'Steuer im Preis inbegriffen', diff --git a/templates/webpages/gdpdu/filter.html b/templates/webpages/gdpdu/filter.html index 83c39c7bf..2f3a855ab 100644 --- a/templates/webpages/gdpdu/filter.html +++ b/templates/webpages/gdpdu/filter.html @@ -20,21 +20,6 @@ [% 'To Date' | $T8 %] [% L.date_tag('to', SELF.to) %] - - [% 'Include in Report' | $T8 %] - - [% L.checkbox_tag('tables.ar', label=LxERP.t8('Invoices'), checked=1) %] - [% L.checkbox_tag('tables.ap', label=LxERP.t8('Purchase Invoices'), checked=1) %] - [% L.checkbox_tag('tables.gl', label=LxERP.t8('GL Transactions'), checked=1) %] - [% L.checkbox_tag('tables.delivery_orders', label=LxERP.t8('Delivery Orders'), checked=1) %] - [% L.checkbox_tag('tables.oe', label=LxERP.t8('Quotations and orders'), checked=1) %] - [% L.checkbox_tag('tables.customer', label=LxERP.t8('Customers'), checked=1) %] - [% L.checkbox_tag('tables.vendor', label=LxERP.t8('Vendors'), checked=1) %] - [% L.checkbox_tag('tables.parts', label=LxERP.t8('Parts'), checked=1) %] - [% L.checkbox_tag('tables.acc_trans', label=LxERP.t8('Transactions'), checked=1) %] - [% L.checkbox_tag('tables.chart', label=LxERP.t8('Charts'), checked=1) %] - - [% L.hidden_tag('action', 'Gdpdu/dispatch') %] -- 2.20.1