Benutzerdefinierte Datenexporte zu CSV anlegen und ausführen können
[kivitendo-erp.git] / SL / Controller / CustomDataExport.pm
1 package SL::Controller::CustomDataExport;
2
3 use strict;
4 use utf8;
5
6 use parent qw(SL::Controller::Base);
7
8 use DBI qw(:sql_types);
9 use File::Temp ();
10 use List::UtilsBy qw(sort_by);
11 use POSIX qw(strftime);
12 use Text::CSV_XS;
13
14 use SL::DB::CustomDataExportQuery;
15 use SL::Locale::String qw(t8);
16
17 use Rose::Object::MakeMethods::Generic
18 (
19   scalar                  => [ qw(rows) ],
20   'scalar --get_set_init' => [ qw(query queries parameters today) ],
21 );
22
23 __PACKAGE__->run_before('check_auth');
24 __PACKAGE__->run_before('setup_javascripts');
25
26 #
27 # actions
28 #
29
30 sub action_list {
31   my ($self) = @_;
32
33   $self->render('custom_data_export/list', title => $::locale->text('Execute a custom data export query'));
34 }
35
36 sub action_export {
37   my ($self) = @_;
38
39   if (!$::form->{format}) {
40     $self->setup_export_action_bar;
41     return $self->render('custom_data_export/export', title => t8("Execute custom data export '#1'", $self->query->name));
42   }
43
44   $self->execute_query;
45
46   if (scalar(@{ $self->rows // [] }) == 1) {
47     $self->setup_empty_result_set_action_bar;
48     return $self->render('custom_data_export/empty_result_set', title => t8("Execute custom data export '#1'", $self->query->name));
49   }
50
51
52   my $method = "export_as_" . $::form->{format};
53   $self->$method;
54 }
55
56 #
57 # filters
58 #
59
60 sub check_auth {
61   my ($self) = @_;
62   $::auth->assert($self->query->access_right) if $self->query->access_right;
63 }
64
65 sub setup_javascripts {
66   $::request->layout->add_javascripts('kivi.Validator.js');
67 }
68
69 #
70 # helpers
71 #
72
73 sub init_query      { $::form->{id} ? SL::DB::CustomDataExportQuery->new(id => $::form->{id})->load : SL::DB::CustomDataExportQuery->new }
74 sub init_parameters { [ sort_by { lc $_->name } @{ $_[0]->query->parameters // [] } ] }
75 sub init_today      { DateTime->today_local }
76
77 sub init_queries {
78   my %rights_map     = %{ $::auth->load_rights_for_user($::form->{login}) };
79   my @granted_rights = grep { $rights_map{$_} } keys %rights_map;
80
81   return scalar SL::DB::Manager::CustomDataExportQuery->get_all_sorted(
82     where => [
83       or => [
84         access_right => undef,
85         access_right => '',
86         (access_right => \@granted_rights) x !!@granted_rights,
87       ],
88     ],
89   )
90 }
91
92 sub setup_export_action_bar {
93   my ($self) = @_;
94
95   for my $bar ($::request->layout->get('actionbar')) {
96     $bar->add(
97       action => [
98         t8('Export'),
99         submit    => [ '#form', { action => 'CustomDataExport/export' } ],
100         checks    => [ 'kivi.validate_form' ],
101         accesskey => 'enter',
102       ],
103       action => [
104         t8('Back'),
105         call => [ 'kivi.history_back' ],
106       ],
107     );
108   }
109 }
110
111 sub setup_empty_result_set_action_bar {
112   my ($self) = @_;
113
114   for my $bar ($::request->layout->get('actionbar')) {
115     $bar->add(
116       action => [
117         t8('Back'),
118         call => [ 'kivi.history_back' ],
119       ],
120     );
121   }
122 }
123
124 sub prepare_query {
125   my ($self) = @_;
126
127   my $sql_query = $self->query->sql_query;
128   my @values;
129
130   my %values_by_name;
131
132   foreach my $parameter (@{ $self->query->parameters // [] }) {
133     my $value                           = ($::form->{parameters} // {})->{ $parameter->name };
134     $values_by_name{ $parameter->name } = $parameter->parameter_type eq 'number' ? $::form->parse_amount(\%::myconfig, $value) : $value;
135   }
136
137   while ($sql_query =~ m{<\%(.+?)\%>}) {
138     push @values, $values_by_name{$1};
139     substr($sql_query, $-[0], $+[0] - $-[0], '?');
140   }
141
142   return ($sql_query, @values);
143 }
144
145 sub execute_query {
146   my ($self) = @_;
147
148   my ($sql_query, @values) = $self->prepare_query;
149   my $sth                  = $self->query->db->dbh->prepare($sql_query) || $::form->dberror;
150   $sth->execute(@values)                                                || $::form->dberror;
151
152   my @names = @{ $sth->{NAME} };
153   my @types = @{ $sth->{TYPE} };
154   my @data  = @{ $sth->fetchall_arrayref };
155
156   $sth->finish;
157
158   foreach my $row (@data) {
159     foreach my $col (0..$#types) {
160       my $type = $types[$col];
161
162       if ($type == SQL_NUMERIC) {
163         $row->[$col] = $::form->format_amount(\%::myconfig, $row->[$col]);
164       }
165     }
166   }
167
168   $self->rows([
169     \@names,
170     @data,
171   ]);
172 }
173
174 sub export_as_csv {
175   my ($self) = @_;
176
177   my $csv = Text::CSV_XS->new({
178     binary   => 1,
179     sep_char => ';',
180     eol      => "\n",
181   });
182
183   my ($file_handle, $file_name) = File::Temp::tempfile;
184
185   binmode $file_handle, ":encoding(utf8)";
186
187   $csv->print($file_handle, $_) for @{ $self->rows };
188
189   $file_handle->close;
190
191   my $report_name =  $self->query->name;
192   $report_name    =~ s{[^[:word:]]+}{_}ig;
193   $report_name   .=  strftime('_%Y-%m-%d_%H-%M-%S.csv', localtime());
194
195   $self->send_file(
196     $file_name,
197     content_type => 'text/csv',
198     name         => $report_name,
199   );
200 }
201
202 1;