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