Brieffunktion: Nutzung von SL::Webdav zur Speicherung im Webdav
[kivitendo-erp.git] / SL / Controller / Letter.pm
1 package SL::Controller::Letter;
2
3 use strict;
4 use parent qw(SL::Controller::Base);
5
6 use Carp;
7 use File::Basename;
8 use POSIX qw(strftime);
9 use SL::Controller::Helper::GetModels;
10 use SL::Controller::Helper::ReportGenerator;
11 use SL::CT;
12 use SL::DB::Letter;
13 use SL::DB::LetterDraft;
14 use SL::DB::Employee;
15 use SL::Helper::Flash qw(flash flash_later);
16 use SL::Helper::CreatePDF;
17 use SL::Helper::PrintOptions;
18 use SL::Locale::String qw(t8);
19 use SL::IS;
20 use SL::ReportGenerator;
21 use SL::Webdav;
22 use SL::Webdav::File;
23
24 use Rose::Object::MakeMethods::Generic (
25   'scalar --get_set_init' => [ qw(letter all_employees models) ],
26 );
27
28 __PACKAGE__->run_before('check_auth_edit');
29 __PACKAGE__->run_before('check_auth_report', only => [ qw(list) ]);
30
31 use constant TEXT_CREATED_FOR_VALUES => (qw(presskit fax letter));
32 use constant PAGE_CREATED_FOR_VALUES => (qw(sketch 1 2));
33
34 my %sort_columns = (
35   date                  => t8('Date'),
36   subject               => t8('Subject'),
37   letternumber          => t8('Letternumber'),
38   vc_id                 => t8('Customer'),
39   contact               => t8('Contact'),
40 );
41
42 sub action_add {
43   my ($self, %params) = @_;
44
45   return if $self->load_letter_draft(%params);
46
47   $self->letter->employee_id(SL::DB::Manager::Employee->current->id);
48   $self->letter->salesman_id(SL::DB::Manager::Employee->current->id);
49
50   $self->_display(
51     title       => t8('Add Letter'),
52     language_id => $params{language_id},
53   );
54 }
55
56 sub action_edit {
57   my ($self, %params) = @_;
58
59   return $self->action_add
60     unless $::form->{letter} || $::form->{draft};
61
62   $self->letter(SL::DB::Letter->new_from_draft($::form->{draft}{id}))
63     if $::form->{draft};
64
65   $self->_display(
66     title  => t8('Edit Letter'),
67   );
68 }
69
70 sub action_save {
71   my ($self, %params) = @_;
72
73   my $letter = $self->_update;
74
75   if (!$self->check_letter($letter)) {
76     return $self->_display;
77   }
78
79   $self->check_number;
80
81   if (!$letter->save) {
82     flash('error', t8('There was an error saving the letter'));
83     return $self->_display;
84   }
85
86   flash('info', t8('Letter saved!'));
87
88   $self->_display;
89 }
90
91 sub action_update_contacts {
92   my ($self) = @_;
93
94   my $letter = $self->letter;
95
96   if (!$self->letter->vc_id || !$self->letter->customer) {
97     return $self->js
98       ->replaceWith(
99         '#letter_cp_id',
100         SL::Presenter->get->select_tag('letter.cp_id', [], value_key => 'cp_id', title_key => 'full_name')
101       )
102       ->render;
103   }
104
105   my $contacts = $letter->customer->contacts;
106
107   my $default;
108   if (   $letter->contact
109       && $letter->contact->cp_cv_id
110       && $letter->contact->cp_cv_id == $letter->vc_id) {
111     $default = $letter->contact->cp_id;
112   } else {
113     $default = '';
114   }
115
116   $self->js
117     ->replaceWith(
118       '#letter_cp_id',
119       SL::Presenter->get->select_tag('letter.cp_id', $contacts, default => $default, value_key => 'cp_id', title_key => 'full_name')
120     )
121     ->render;
122 }
123
124 sub action_save_letter_draft {
125   my ($self, %params) = @_;
126
127   $self->check_letter;
128
129   my $letter_draft = SL::DB::LetterDraft->new_from_letter($self->_update);
130
131   if (!$letter_draft->save) {
132     flash('error', t8('There was an error saving the letter draft'));
133     return $self->_display;
134   }
135
136   flash('info', t8('Draft for this Letter saved!'));
137
138   $self->_display;
139 }
140
141 sub action_delete {
142   my ($self, %params) = @_;
143
144   if (!$self->letter->delete) {
145     flash('error', t8('An error occured. Letter could not be deleted.'));
146     return $self->action_update;
147   }
148
149   flash_later('info', t8('Letter deleted'));
150   $self->redirect_to(action => 'list');
151 }
152
153 sub action_delete_letter_drafts {
154   my ($self, %params) = @_;
155
156   my @ids =  grep { /^checked_(.*)/ && $::form->{$_} } keys %$::form;
157
158   SL::DB::Manager::LetterDraft->delete_all(query => [ ids => \@ids ]) if @ids;
159
160   $self->redirect_to(action => 'add');
161 }
162
163 sub action_list {
164   my ($self, %params) = @_;
165
166   $self->make_filter_summary;
167   $self->prepare_report;
168
169   my $letters = $self->models->get;
170   $self->report_generator_list_objects(report => $self->{report}, objects => $letters);
171
172 }
173
174 sub action_print_letter {
175   my ($self, $old_form) = @_;
176
177   my $display_form = $::form->{display_form} || "display_form";
178   my $letter       = $self->_update;
179
180   $self->export_letter_to_form($letter);
181   $::form->{formname} = "letter";
182   $::form->{type}     = "letter";
183   $::form->{format}   = "pdf";
184
185   my $language_saved      = $::form->{language_id};
186   my $greeting_saved      = $::form->{greeting};
187   my $cp_id_saved         = $::form->{cp_id};
188
189   $::form->{customer_id} = $self->letter->vc_id;
190   IS->customer_details(\%::myconfig, $::form);
191
192   if (!$cp_id_saved) {
193     # No contact was selected. Delete all contact variables because
194     # IS->customer_details() and IR->vendor_details() get the default
195     # contact anyway.
196     map({ delete($::form->{$_}); } grep(/^cp_/, keys(%{ $::form })));
197   }
198
199   $::form->{greeting} = $greeting_saved;
200   $::form->{language_id} = $language_saved;
201
202   if ($::form->{cp_id}) {
203     CT->get_contact(\%::myconfig, $::form);
204   }
205
206   $::form->{cp_contact_formal} = ($::form->{cp_greeting} ? "$::form->{cp_greeting} " : '') . ($::form->{cp_givenname} ? "$::form->{cp_givenname} " : '') . $::form->{cp_name};
207
208   $::form->get_employee_data('prefix' => 'employee', 'id' => $letter->{employee_id});
209   $::form->get_employee_data('prefix' => 'salesman', 'id' => $letter->{salesman_id});
210
211   my ($template_file, @template_files) = SL::Helper::CreatePDF->find_template(
212     name        => 'letter',
213     printer_id  => $::form->{printer_id},
214     language_id => $::form->{language_id},
215     formname    => 'letter',
216     format      => 'pdf',
217   );
218
219   if (!defined $template_file) {
220     $::form->error($::locale->text('Cannot find matching template for this print request. Please contact your template maintainer. I tried these: #1.', join ', ', map { "'$_'"} @template_files));
221   }
222
223   my %create_params = (
224     template  => $template_file,
225     variables => $::form,
226     return    => 'file_name',
227     variable_content_types => {
228       body                 => 'html',
229     },
230   );
231
232   my $pdf_file_name;
233   eval {
234     $pdf_file_name = SL::Helper::CreatePDF->create_pdf(%create_params);
235
236     # set some form defaults for printing webdav copy variables
237     if ( $::form->{media} eq 'email') {
238       my $mail             = Mailer->new;
239       my $signature        = $::myconfig{signature};
240       $mail->{$_}          = $::form->{$_}               for qw(cc subject message bcc to);
241       $mail->{from}        = qq|"$::myconfig{name}" <$::myconfig{email}>|;
242       $mail->{fileid}      = time() . '.' . $$ . '.';
243       $mail->{attachments} =  [{ "filename" => $pdf_file_name,
244                                  "name"     => $::form->{attachment_name} }];
245       $mail->{message}    .=  "\n-- \n$signature";
246       $mail->{message}     =~ s/\r//g;
247
248       # copy_file_to_webdav was already done via io.pl -> edit_e_mail
249       my $err = $mail->send;
250       return !$err;
251     }
252
253     $::form->{letternumber} = $self->letter->letternumber;
254     my $attachment_name     = $::form->generate_attachment_filename;
255     my $webdav_file         = SL::Webdav::File->new(
256       filename => $attachment_name,
257       webdav   => SL::Webdav->new(
258         type   => 'letter',
259         number => $self->letter->letternumber,
260       ),
261     );
262
263     if (!$::form->{printer_id} || $::form->{media} eq 'screen') {
264       $self->send_file($pdf_file_name, name => $attachment_name);
265       $webdav_file->store(file => $pdf_file_name) if $::instance_conf->get_webdav_documents;
266       unlink $pdf_file_name;
267
268       return 1;
269     }
270
271     my $printer = SL::DB::Printer->new(id => $::form->{printer_id})->load;
272     $printer->print_document(
273       copies    => $::form->{copies},
274       file_name => $pdf_file_name,
275     );
276
277     $webdav_file->store(file => $pdf_file_name) if $::instance_conf->get_webdav_documents;
278     unlink $pdf_file_name;
279
280     flash_later('info', t8('The documents have been sent to the printer \'#1\'.', $printer->printer_description));
281     $self->redirect_to(action => 'edit', 'letter.id' => $self->letter->id, media => 'printer', printer_id => $::form->{printer_id});
282     1;
283   } or do {
284     unlink $pdf_file_name;
285     $::form->error(t8("Creating the PDF failed:") . " " . $@);
286   };
287
288 }
289
290 sub action_update {
291   my ($self, $name_selected) = @_;
292
293   $self->_display(
294     letter => $self->_update,
295   );
296 }
297
298 sub action_skip_draft {
299   my ($self) = @_;
300   $self->action_add(skip_drafts => 1);
301 }
302
303 sub action_delete_drafts {
304   my ($self) = @_;
305   delete_letter_drafts();
306   $self->action_add(skip_drafts => 1);
307 }
308
309 sub _display {
310   my ($self, %params) = @_;
311
312   $::request->{layout}->use_javascript("${_}.js") for qw(ckeditor/ckeditor ckeditor/adapters/jquery);
313
314   my $letter = $self->letter;
315
316  $params{title} ||= t8('Edit Letter');
317
318   $::form->{type}             = 'letter';   # needed for print_options
319   $::form->{vc}               = 'customer'; # needs to be for _get_contacts...
320
321   $::request->layout->add_javascripts('customer_or_vendor_selection.js');
322   $::request->layout->add_javascripts('edit_part_window.js');
323
324   $::form->{language_id} ||= $params{language_id};
325   $::form->{printers}      = SL::DB::Manager::Printer->get_all_sorted;
326
327   $self->render('letter/edit',
328     %params,
329     TCF           => [ map { key => $_, value => t8(ucfirst $_) }, TEXT_CREATED_FOR_VALUES() ],
330     PCF           => [ map { key => $_, value => t8(ucfirst $_) }, PAGE_CREATED_FOR_VALUES() ],
331     letter        => $letter,
332     employees     => $self->all_employees,
333     print_options => SL::Helper::PrintOptions->get_print_options (
334       options => { no_postscript   => 1,
335                    no_opendocument => 1,
336                    no_html         => 1,
337                    no_queue        => 1 }),
338
339   );
340 }
341
342 sub _update {
343   my ($self, %params) = @_;
344
345   my $letter = $self->letter;
346
347   $self->check_date;
348   $self->set_greetings;
349
350   return $letter;
351 }
352
353 sub prepare_report {
354   my ($self) = @_;
355
356   my $report      = SL::ReportGenerator->new(\%::myconfig, $::form);
357   $self->{report} = $report;
358
359   my @columns  = qw(date subject letternumber vc_id contact date);
360   my @sortable = qw(date subject letternumber vc_id contact date);
361
362   my %column_defs = (
363     date                  => { text => t8('Date'),         sub => sub { $_[0]->date_as_date } },
364     subject               => { text => t8('Subject'),      sub => sub { $_[0]->subject },
365                                obj_link => sub { $self->url_for(action => 'edit', 'letter.id' => $_[0]->id, callback => $self->models->get_callback) }  },
366     letternumber          => { text => t8('Letternumber'), sub => sub { $_[0]->letternumber },
367                                obj_link => sub { $self->url_for(action => 'edit', 'letter.id' => $_[0]->id, callback => $self->models->get_callback) }  },
368     vc_id                 => { text => t8('Customer'),      sub => sub { SL::DB::Manager::Customer->find_by_or_create(id => $_[0]->vc_id)->displayable_name } },
369     contact               => { text => t8('Contact'),       sub => sub { $_[0]->contact ? $_[0]->contact->full_name : '' } },
370   );
371
372   $column_defs{$_}{text} = $sort_columns{$_} for keys %column_defs;
373
374   $report->set_options(
375     std_column_visibility => 1,
376     controller_class      => 'Letter',
377     output_format         => 'HTML',
378     top_info_text         => t8('Letters'),
379     title                 => t8('Letters'),
380     allow_pdf_export      => 1,
381     allow_csv_export      => 1,
382   );
383
384   $report->set_columns(%column_defs);
385   $report->set_column_order(@columns);
386   $report->set_export_options(qw(list filter));
387   $report->set_options_from_form;
388
389   $self->models->disable_plugin('paginated') if $report->{options}{output_format} =~ /^(pdf|csv)$/i;
390   $self->models->finalize;
391   $self->models->set_report_generator_sort_options(report => $report, sortable_columns => \@sortable);
392
393   $report->set_options(
394     raw_top_info_text    => $self->render('letter/report_top',    { output => 0 }),
395     raw_bottom_info_text => $self->render('letter/report_bottom', { output => 0 }, models => $self->models),
396     attachment_basename  => t8('letters_list') . strftime('_%Y%m%d', localtime time),
397   );
398 }
399
400 sub make_filter_summary {
401   my ($self) = @_;
402
403   my $filter = $::form->{filter} || {};
404   my @filter_strings;
405
406   my $employee = $filter->{employee_id} ? SL::DB::Employee->new(id => $filter->{employee_id})->load->name : '';
407   my $salesman = $filter->{salesman_id} ? SL::DB::Employee->new(id => $filter->{salesman_id})->load->name : '';
408
409   my @filters = (
410     [ $filter->{"letternumber:substr::ilike"},  t8('Number')     ],
411     [ $filter->{"subject:substr::ilike"},       t8('Subject')    ],
412     [ $filter->{"body:substr::ilike"},          t8('Body')       ],
413     [ $filter->{"date:date::ge"},               t8('From Date')  ],
414     [ $filter->{"date:date::le"},               t8('To Date')    ],
415     [ $employee,                                t8('Employee')   ],
416     [ $salesman,                                t8('Salesman')   ],
417   );
418
419   my %flags = (
420   );
421   my @flags = map { $flags{$_} } @{ $filter->{part}{type} || [] };
422
423   for (@flags) {
424     push @filter_strings, $_ if $_;
425   }
426   for (@filters) {
427     push @filter_strings, "$_->[1]: $_->[0]" if $_->[0];
428   }
429
430   $self->{filter_summary} = join ', ', @filter_strings;
431 }
432
433 sub e_mail {
434   my $letter = _update();
435
436   $letter->check_number;
437   $letter->save;
438
439   $::form->{formname} = "letter";
440   $letter->export_to($::form);
441
442   $::form->{id} = $letter->{id};
443   edit_e_mail();
444 }
445
446 sub load_letter_draft {
447   my ($self, %params) = @_;
448
449   return 0 if $params{skip_drafts};
450
451   my $letter_drafts = SL::DB::Manager::LetterDraft->get_all;
452
453   return unless @$letter_drafts;
454
455   $self->render('letter/load_drafts',
456     title         => t8('Letter Draft'),
457     LETTER_DRAFTS => $letter_drafts,
458   );
459
460   return 1;
461 }
462
463 sub check_date {
464   my ($self) = @_;
465   my $letter = $self->letter;
466
467   return unless $letter;
468   return if $letter->date;
469
470   $letter->date(DateTime->today)
471 }
472
473 sub check_letter {
474   my ($self, $letter) = @_;
475
476   $letter ||= $self->letter;
477
478   my $error;
479
480   if (!$letter->subject) {
481     flash('error', t8('The subject is missing.'));
482     $error = 1;
483   }
484   if (!$letter->body) {
485     flash('error', t8('The body is missing.'));
486     $error = 1;
487   }
488   if (!$letter->employee_id) {
489     flash('error', t8('The employee is missing.'));
490     $error = 1;
491   }
492
493   return !$error;
494 }
495
496 sub check_number {
497   my ($self, $letter) = @_;
498
499   $letter ||= $self->letter;
500
501   return if $letter->letternumber;
502
503   $letter->letternumber(SL::TransNumber->new(type => 'letter', id => $self->{id}, number => $self->{letternumber})->create_unique);
504 }
505
506 sub set_greetings {
507   my ($self) = @_;
508   my $letter = $self->letter;
509
510   return unless $letter;
511   return if $letter->greeting;
512
513   $letter->greeting(t8('Dear Sir or Madam,'));
514 }
515
516 sub export_letter_to_form {
517   my ($self, $letter) = @_;
518   # nope, not pretty.
519
520   $letter ||= $self->letter;
521
522   for ($letter->meta->columns) {
523     if ((ref $_) =~ /Date/i) {
524       $::form->{$_->name} = $letter->$_->to_kivitendo;
525     } else {
526       $::form->{$_->name} = $letter->$_;
527     }
528   }
529 }
530
531 sub init_letter {
532   my ($self) = @_;
533
534   my $letter      = SL::DB::Manager::Letter->find_by_or_create(id => $::form->{letter}{id} || 0)
535                                            ->assign_attributes(%{ $::form->{letter} });
536
537   if ($letter->cp_id) {
538 #     $letter->vc_id($letter->contact->cp_cv_id);
539       # contacts don't have language_id yet
540 #     $letter->greeting(GenericTranslations->get(
541 #       translation_type => 'greetings::' . ($letter->contact->cp_gender eq 'f' ? 'female' : 'male'),
542 #       language_id      => $letter->contact->language_id,
543 #       allow_fallback   => 1
544 #     ));
545   }
546
547   $letter;
548 }
549
550 sub init_models {
551   my ($self) = @_;
552
553   SL::Controller::Helper::GetModels->new(
554     controller   => $self,
555     model        => 'Letter',
556     sorted       => \%sort_columns,
557     with_objects => [ 'contact', 'salesman', 'employee' ],
558   );
559 }
560
561 sub init_all_employees {
562   SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
563 }
564
565 sub check_auth_edit {
566   $::auth->assert('sales_letter_edit');
567 }
568
569 sub check_auth_report {
570   $::auth->assert('sales_letter_report');
571 }
572
573 1;
574
575 __END__
576
577 =encoding utf-8
578
579 =head1 NAME
580
581 SL::Controller::Letter - Letters CRUD and printing
582
583 =head1 DESCRIPTION
584
585 Simple letter CRUD controller with drafting capabilities.
586
587 =head1 TODO
588
589   Customer/Vendor switch for dealing with vendor letters
590
591 copy to webdav is crap
592
593 customer/vendor stuff
594
595 =head1 AUTHOR
596
597 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
598
599 =cut