Letter Controller rewrite
authorSven Schöling <s.schoeling@linet-services.de>
Mon, 21 Dec 2015 15:16:39 +0000 (16:16 +0100)
committerSven Schöling <s.schoeling@linet-services.de>
Thu, 14 Jan 2016 10:33:16 +0000 (11:33 +0100)
Jetzt auf einigermassen aktuellem technischen Stand.

12 files changed:
SL/Controller/Letter.pm [new file with mode: 0644]
SL/DB/Letter.pm
SL/DB/LetterDraft.pm
SL/DB/Manager/Letter.pm [new file with mode: 0644]
SL/Letter.pm [deleted file]
bin/mozilla/letter.pl [deleted file]
locale/de/all
menus/user/00-erp.yaml
templates/webpages/letter/edit.html
templates/webpages/letter/load_drafts.html
templates/webpages/letter/report_top.html
templates/webpages/letter/search.html

diff --git a/SL/Controller/Letter.pm b/SL/Controller/Letter.pm
new file mode 100644 (file)
index 0000000..f1109d7
--- /dev/null
@@ -0,0 +1,583 @@
+package SL::Controller::Letter;
+
+use strict;
+use parent qw(SL::Controller::Base);
+
+use POSIX qw(strftime);
+use SL::Controller::Helper::GetModels;
+use SL::Controller::Helper::ReportGenerator;
+use SL::DB::Letter;
+use SL::DB::LetterDraft;
+use SL::DB::Employee;
+use SL::Helper::Flash qw(flash);
+use SL::Helper::CreatePDF;
+use SL::Locale::String qw(t8);
+use SL::IS;
+use SL::ReportGenerator;
+
+use Rose::Object::MakeMethods::Generic (
+  'scalar --get_set_init' => [ qw(letter all_employees models) ],
+);
+
+__PACKAGE__->run_before('check_auth_edit');
+__PACKAGE__->run_before('check_auth_report', only => [ qw(list) ]);
+
+use constant TEXT_CREATED_FOR_VALUES => (qw(presskit fax letter));
+use constant PAGE_CREATED_FOR_VALUES => (qw(sketch 1 2));
+
+my %sort_columns = (
+  date                  => t8('Date'),
+  subject               => t8('Subject'),
+  letternumber          => t8('Letternumber'),
+  vc_id                 => t8('Customer'),
+  contact               => t8('Contact'),
+);
+
+sub action_add {
+  my ($self, %params) = @_;
+
+  return if $self->load_letter_draft(%params);
+
+  $self->letter->employee_id(SL::DB::Manager::Employee->current->id);
+  $self->letter->salesman_id(SL::DB::Manager::Employee->current->id);
+
+  $self->_display(
+    title       => t8('Add Letter'),
+    language_id => $params{language_id},
+  );
+}
+
+sub action_edit {
+  my ($self, %params) = @_;
+
+  return $self->action_add
+    unless $::form->{letter} || $::form->{draft};
+
+  $self->letter(SL::DB::Letter->new_from_draft($::form->{draft}{id}))
+    if $::form->{draft};
+
+  $self->_display(
+    title  => t8('Edit Letter'),
+  );
+}
+
+sub action_save {
+  my ($self, %params) = @_;
+
+  my $letter = $self->_update;
+
+  if (!$self->check_letter($letter)) {
+    return $self->_display;
+  }
+
+  $self->check_number;
+
+  if (!$letter->save) {
+    flash('error', t8('There was an error saving the letter'));
+    return $self->_display;
+  }
+
+  flash('info', t8('Letter saved!'));
+
+  $self->_display;
+}
+
+sub action_update_contacts {
+  my ($self) = @_;
+
+  my $letter = $self->letter;
+
+  if (!$self->letter->vc_id || !$self->letter->customer) {
+    return $self->js
+      ->replaceWith(
+        '#letter_cp_id',
+        SL::Presenter->get->select_tag('letter.cp_id', [], value_key => 'cp_id', title_key => 'full_name')
+      )
+      ->render;
+  }
+
+  my $contacts = $letter->customer->contacts;
+
+  my $default;
+  if (   $letter->contact
+      && $letter->contact->cp_cv_id
+      && $letter->contact->cp_cv_id == $letter->vc_id) {
+    $default = $letter->contact->cp_id;
+  } else {
+    $default = '';
+  }
+
+  $self->js
+    ->replaceWith(
+      '#letter_cp_id',
+      SL::Presenter->get->select_tag('letter.cp_id', $contacts, default => $default, value_key => 'cp_id', title_key => 'full_name')
+    )
+    ->render;
+}
+
+sub action_save_letter_draft {
+  my ($self, %params) = @_;
+
+  $self->check_letter;
+
+  my $letter_draft = SL::DB::LetterDraft->new_from_letter($self->_update);
+
+  if (!$letter_draft->save) {
+    flash('error', t8('There was an error saving the letter draft'));
+    return $self->_display;
+  }
+
+  flash('info', t8('Draft for this Letter saved!'));
+
+  $self->_display;
+}
+
+sub action_delete {
+  my ($self, %params) = @_;
+
+  if (!$self->letter->delete) {
+    flash('error', t8('An error occured. Letter could not be deleted.'));
+    return $self->action_update;
+  }
+
+  flash_later('info', t8('Letter deleted'));
+  $self->redirect_to(action => 'list');
+}
+
+sub action_delete_letter_drafts {
+  my ($self, %params) = @_;
+
+  my @ids =  grep { /^checked_(.*)/ && $::form->{$_} } keys %$::form;
+
+  SL::DB::Manager::LetterDraft->delete_all(query => [ ids => \@ids ]) if @ids;
+
+  $self->redirect_to(action => 'add');
+}
+
+sub action_list {
+  my ($self, %params) = @_;
+
+  $self->make_filter_summary;
+  $self->prepare_report;
+
+  my $letters = $self->models->get;
+  $self->report_generator_list_objects(report => $self->{report}, objects => $letters);
+
+}
+
+sub action_print_letter {
+  my ($self, $old_form) = @_;
+
+  my $display_form = $::form->{display_form} || "display_form";
+  my $letter       = $self->_update;
+
+  $self->export_letter_to_form($letter);
+  $::form->{formname} = "letter";
+  $::form->{format} = "pdf";
+
+  my $language_saved      = $::form->{language_id};
+  my $greeting_saved      = $::form->{greeting};
+  my $cp_id_saved         = $::form->{cp_id};
+
+  IS->customer_details(\%::myconfig, $::form);
+
+  if (!$cp_id_saved) {
+    # No contact was selected. Delete all contact variables because
+    # IS->customer_details() and IR->vendor_details() get the default
+    # contact anyway.
+    map({ delete($::form->{$_}); } grep(/^cp_/, keys(%{ $::form })));
+  }
+
+  $::form->{greeting} = $greeting_saved;
+  $::form->{language_id} = $language_saved;
+
+  if ($::form->{cp_id}) {
+    CT->get_contact(\%::myconfig, $::form);
+  }
+
+  $::form->{cp_contact_formal} = ($::form->{cp_greeting} ? "$::form->{cp_greeting} " : '') . ($::form->{cp_givenname} ? "$::form->{cp_givenname} " : '') . $::form->{cp_name};
+
+  $::form->get_employee_data('prefix' => 'employee', 'id' => $letter->{employee_id});
+  $::form->get_employee_data('prefix' => 'salesman', 'id' => $letter->{salesman_id});
+
+  my %create_params = (
+    template  => scalar(SL::Helper::CreatePDF->find_template(
+      name        => 'letter',
+      printer_id  => $::form->{printer_id},
+      language_id => $::form->{language_id},
+      formname    => 'letter',
+      format      => 'pdf',
+    )),
+    variables => $::form,
+    return    => 'file_name',
+  );
+  my $pdf_file_name;
+  eval {
+    $pdf_file_name = SL::Helper::CreatePDF->create_pdf(%create_params);
+
+    # set some form defaults for printing webdav copy variables
+    if ( $::form->{media} eq 'email') {
+      my $mail             = Mailer->new;
+      my $signature        = $::myconfig{signature};
+      $mail->{$_}          = $::form->{$_}               for qw(cc subject message bcc to);
+      $mail->{from}        = qq|"$::myconfig{name}" <$::myconfig{email}>|;
+      $mail->{fileid}      = time() . '.' . $$ . '.';
+      $mail->{attachments} =  [{ "filename" => $pdf_file_name,
+                                 "name"     => $::form->{attachment_name} }];
+      $mail->{message}    .=  "\n-- \n$signature";
+      $mail->{message}     =~ s/\r//g;
+
+      # copy_file_to_webdav was already done via io.pl -> edit_e_mail
+      my $err = $mail->send;
+      return !$err;
+    }
+
+    if (!$::form->{printer_id} || $::form->{media} eq 'screen') {
+
+      my $file = IO::File->new($pdf_file_name, 'r') || croak("Cannot open file '$pdf_file_name'");
+      my $size = -s $pdf_file_name;
+      my $content_type    =  'application/pdf';
+      my $attachment_name =  $::form->generate_attachment_filename;
+      $attachment_name    =~ s:.*//::g;
+
+      print $::form->create_http_response(content_type        => $content_type,
+                                          content_disposition => 'attachment; filename="' . $attachment_name . '"',
+                                          content_length      => $size);
+
+      $::locale->with_raw_io(\*STDOUT, sub { print while <$file> });
+      $file->close;
+
+      Common::copy_file_to_webdav_folder($::form) if $::instance_conf->get_webdav_documents;
+      unlink $pdf_file_name;
+      return 1;
+    }
+
+    my $printer = SL::DB::Printer->new(id => $::form->{printer_id})->load;
+    my $command = SL::Template::create(type => 'ShellCommand', form => Form->new(''))->parse($printer->printer_command);
+
+    open my $out, '|-', $command or die $!;
+    binmode $out;
+    print $out scalar(read_file($pdf_file_name));
+    close $out;
+    Common::copy_file_to_webdav_folder($::form) if $::instance_conf->get_webdav_documents;
+
+    flash_later('info', t8('The documents have been sent to the printer \'#1\'.', $printer->printer_description));
+    $self->redirect_to(action => 'edit', id => $letter->{id}, 'printer_id' => $::form->{printer_id});
+    1;
+  } or do {
+    unlink $pdf_file_name;
+    $::form->error(t8("Creating the PDF failed:") . " " . $@);
+  };
+
+}
+
+sub action_update {
+  my ($self, $name_selected) = @_;
+
+  $self->_display(
+    letter => $self->_update,
+  );
+}
+
+sub action_skip_draft {
+  my ($self) = @_;
+  $self->action_add(skip_drafts => 1);
+}
+
+sub action_delete_drafts {
+  my ($self) = @_;
+  delete_letter_drafts();
+  $self->action_add(skip_drafts => 1);
+}
+
+sub _display {
+  my ($self, %params) = @_;
+
+  my $letter = $self->letter;
+
+  require 'bin/mozilla/io.pl';
+
+ $params{title} ||= t8('Edit Letter');
+
+  $::form->{type}             = 'letter';   # needed for print_options
+  $::form->{vc}               = 'customer'; # needs to be for _get_contacts...
+
+  $::request->layout->add_javascripts('customer_or_vendor_selection.js');
+  $::request->layout->add_javascripts('edit_part_window.js');
+
+  $::form->{language_id} ||= $params{language_id};
+
+  $self->render('letter/edit',
+    %params,
+    TCF           => [ map { key => $_, value => t8(ucfirst $_) }, TEXT_CREATED_FOR_VALUES() ],
+    PCF           => [ map { key => $_, value => t8(ucfirst $_) }, PAGE_CREATED_FOR_VALUES() ],
+    letter        => $letter,
+    employees     => $self->all_employees,
+    print_options => print_options(inline => 1),
+  );
+}
+
+sub _update {
+  my ($self, %params) = @_;
+
+  my $letter = $self->letter;
+
+  $self->check_date;
+  $self->set_greetings;
+
+  return $letter;
+}
+
+sub prepare_report {
+  my ($self) = @_;
+
+  my $report      = SL::ReportGenerator->new(\%::myconfig, $::form);
+  $self->{report} = $report;
+
+  my @columns  = qw(date subject letternumber vc_id contact date);
+  my @sortable = qw(date subject letternumber vc_id contact date);
+
+  my %column_defs = (
+    date                  => { text => t8('Date'),         sub => sub { $_[0]->date_as_date } },
+    subject               => { text => t8('Subject'),      sub => sub { $_[0]->subject },
+                               obj_link => sub { $self->url_for(action => 'edit', 'letter.id' => $_[0]->id, callback => $self->models->get_callback) }  },
+    letternumber          => { text => t8('Letternumber'), sub => sub { $_[0]->letternumber },
+                               obj_link => sub { $self->url_for(action => 'edit', 'letter.id' => $_[0]->id, callback => $self->models->get_callback) }  },
+    vc_id                 => { text => t8('Customer'),      sub => sub { $_[0]->customer->displayable_name } },
+    contact               => { text => t8('Contact'),       sub => sub { $_[0]->contact ? $_[0]->contact->full_name : '' } },
+  );
+
+  $column_defs{$_}{text} = $sort_columns{$_} for keys %column_defs;
+
+  $report->set_options(
+    std_column_visibility => 1,
+    controller_class      => 'Letter',
+    output_format         => 'HTML',
+    top_info_text         => t8('Letters'),
+    title                 => t8('Letters'),
+    allow_pdf_export      => 1,
+    allow_csv_export      => 1,
+  );
+
+  $report->set_columns(%column_defs);
+  $report->set_column_order(@columns);
+  $report->set_export_options(qw(list filter));
+  $report->set_options_from_form;
+
+  $self->models->disable_plugin('paginated') if $report->{options}{output_format} =~ /^(pdf|csv)$/i;
+  $self->models->finalize;
+  $self->models->set_report_generator_sort_options(report => $report, sortable_columns => \@sortable);
+
+  $report->set_options(
+    raw_top_info_text    => $self->render('letter/report_top',    { output => 0 }),
+    raw_bottom_info_text => $self->render('letter/report_bottom', { output => 0 }, models => $self->models),
+    attachment_basename  => t8('letters_list') . strftime('_%Y%m%d', localtime time),
+  );
+}
+
+sub make_filter_summary {
+  my ($self) = @_;
+
+  my $filter = $::form->{filter} || {};
+  my @filter_strings;
+
+  my $employee = $filter->{employee_id} ? SL::DB::Employee->new(id => $filter->{employee_id})->load->name : '';
+  my $salesman = $filter->{salesman_id} ? SL::DB::Employee->new(id => $filter->{salesman_id})->load->name : '';
+
+  my @filters = (
+    [ $filter->{"letternumber:substr::ilike"},  t8('Number')     ],
+    [ $filter->{"subject:substr::ilike"},       t8('Subject')    ],
+    [ $filter->{"body:substr::ilike"},          t8('Body')       ],
+    [ $filter->{"date:date::ge"},               t8('From Date')  ],
+    [ $filter->{"date:date::le"},               t8('To Date')    ],
+    [ $employee,                                t8('Employee')   ],
+    [ $salesman,                                t8('Salesman')   ],
+  );
+
+  my %flags = (
+  );
+  my @flags = map { $flags{$_} } @{ $filter->{part}{type} || [] };
+
+  for (@flags) {
+    push @filter_strings, $_ if $_;
+  }
+  for (@filters) {
+    push @filter_strings, "$_->[1]: $_->[0]" if $_->[0];
+  }
+
+  $self->{filter_summary} = join ', ', @filter_strings;
+}
+
+sub e_mail {
+  my $letter = _update();
+
+  $letter->check_number;
+  $letter->save;
+
+  $::form->{formname} = "letter";
+  $letter->export_to($::form);
+
+  $::form->{id} = $letter->{id};
+  edit_e_mail();
+}
+
+sub load_letter_draft {
+  my ($self, %params) = @_;
+
+  return 0 if $params{skip_drafts};
+
+  my $letter_drafts = SL::DB::Manager::LetterDraft->get_all;
+
+  return unless @$letter_drafts;
+
+  $self->render('letter/load_drafts',
+    title         => t8('Letter Draft'),
+    LETTER_DRAFTS => $letter_drafts,
+  );
+
+  return 1;
+}
+
+sub check_date {
+  my ($self) = @_;
+  my $letter = $self->letter;
+
+  return unless $letter;
+  return if $letter->date;
+
+  $letter->date(DateTime->today)
+}
+
+sub check_letter {
+  my ($self, $letter) = @_;
+
+  $letter ||= $self->letter;
+
+  my $error;
+
+  if (!$letter->subject) {
+    flash('error', t8('The subject is missing.'));
+    $error = 1;
+  }
+  if (!$letter->body) {
+    flash('error', t8('The body is missing.'));
+    $error = 1;
+  }
+  if (!$letter->employee_id) {
+    flash('error', t8('The employee is missing.'));
+    $error = 1;
+  }
+
+  return !$error;
+}
+
+sub check_number {
+  my ($self, $letter) = @_;
+
+  $letter ||= $self->letter;
+
+  return if $letter->letternumber;
+
+  $letter->letternumber(SL::TransNumber->new(type => 'letter', id => $self->{id}, number => $self->{letternumber})->create_unique);
+}
+
+sub set_greetings {
+  my ($self) = @_;
+  my $letter = $self->letter;
+
+  return unless $letter;
+  return if $letter->greeting;
+
+  $letter->greeting(t8('Dear Sir or Madam,'));
+}
+
+sub export_letter_to_form {
+  my ($self, $letter) = @_;
+  # nope, not pretty.
+
+  $letter ||= $self->letter;
+
+  for ($letter->meta->columns) {
+    if ((ref $_) =~ /Date/i) {
+      $::form->{$_->name} = $letter->$_->to_kivitendo;
+    } else {
+      $::form->{$_->name} = $letter->$_;
+    }
+  }
+}
+
+sub init_letter {
+  my ($self) = @_;
+
+  my $letter      = SL::DB::Manager::Letter->find_by_or_create(id => $::form->{letter}{id} || 0)
+                                           ->assign_attributes(%{ $::form->{letter} });
+
+  if ($letter->cp_id) {
+#     $letter->vc_id($letter->contact->cp_cv_id);
+      # contacts don't have language_id yet
+#     $letter->greeting(GenericTranslations->get(
+#       translation_type => 'greetings::' . ($letter->contact->cp_gender eq 'f' ? 'female' : 'male'),
+#       language_id      => $letter->contact->language_id,
+#       allow_fallback   => 1
+#     ));
+  }
+
+  $letter;
+}
+
+sub init_models {
+  my ($self) = @_;
+
+  SL::Controller::Helper::GetModels->new(
+    controller   => $self,
+    model        => 'Letter',
+    sorted       => {
+      _default     => {
+        by           => 'letternumber',
+        dir          => 1,
+      },
+      %sort_columns,
+    },
+    with_objects => [ 'contact', 'salesman', 'employee' ],
+  );
+}
+
+sub init_all_employees {
+  SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
+}
+
+sub check_auth_edit {
+  $::auth->assert('sales_letter_edit');
+}
+
+sub check_auth_report {
+  $::auth->assert('sales_letter_report');
+}
+
+1;
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Controller::Letter - Letters CRUD and printing
+
+=head1 DESCRIPTION
+
+Simple letter CRUD controller with drafting capabilities.
+
+=head1 TODO
+
+  Customer/Vendor switch for dealing with vendor letters
+
+copy to webdav is crap
+
+customer/vendor stuff
+
+=head1 AUTHOR
+
+Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
+
+=cut
index 08657bd..a7deeae 100644 (file)
@@ -6,10 +6,34 @@ package SL::DB::Letter;
 use strict;
 
 use SL::DB::MetaSetup::Letter;
+use SL::DB::Manager::Letter;
+
+__PACKAGE__->meta->add_relationships(
+  customer  => {
+    type                   => 'many to one',
+    class                  => 'SL::DB::Customer',
+    column_map             => { vc_id => 'id' },
+  },
+
+);
 
 __PACKAGE__->meta->initialize;
 
-# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
-__PACKAGE__->meta->make_manager_class;
+sub new_from_draft {
+  my ($class, $draft) = @_;
+
+  my $self = $class->new;
+
+  if (!ref $draft) {
+    require SL::DB::LetterDraft;
+    $draft = SL::DB::LetterDraft->new(id => $draft)->load;
+  }
+
+  $self->assign_attributes(map { $_ => $draft->$_ } $draft->meta->columns);
+
+  $self->id(undef);
+
+  $self;
+}
 
 1;
index eb4a4b6..109010e 100644 (file)
@@ -12,4 +12,21 @@ __PACKAGE__->meta->initialize;
 # Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
 __PACKAGE__->meta->make_manager_class;
 
+sub new_from_letter {
+  my ($class, $letter) = @_;
+
+  my $self = $class->new;
+
+  if (!ref $letter) {
+    require SL::DB::Draft;
+    $letter = SL::DB::Draft->new(id => $letter)->load;
+  }
+
+  $self->assign_attributes(map { $_ => $letter->$_ } $letter->meta->columns);
+
+  $self->id(undef);
+
+  $self;
+}
+
 1;
diff --git a/SL/DB/Manager/Letter.pm b/SL/DB/Manager/Letter.pm
new file mode 100644 (file)
index 0000000..8aa2a34
--- /dev/null
@@ -0,0 +1,27 @@
+package SL::DB::Manager::Letter;
+
+use strict;
+
+use SL::DB::Helper::Manager;
+use base qw(SL::DB::Helper::Manager);
+
+use SL::DB::Helper::Filtered;
+use SL::DB::Helper::Paginated;
+use SL::DB::Helper::Sorted;
+
+sub object_class { 'SL::DB::Letter' }
+
+__PACKAGE__->make_manager_methods;
+
+sub _sort_spec {
+  return ( columns => { SIMPLE    => 'ALL',
+                        customer  => [ 'lower(customer.name)', ],
+                      },
+           default => [ 'date', 1 ],
+           nulls   => { }
+         );
+}
+
+sub default_objects_per_page { 15 }
+
+1;
diff --git a/SL/Letter.pm b/SL/Letter.pm
deleted file mode 100644 (file)
index 1d1b974..0000000
+++ /dev/null
@@ -1,404 +0,0 @@
-#=====================================================================
-# LX-Office ERP
-# Copyright (C) 2008
-# Based on SQL-Ledger Version 2.1.9
-# Web http://www.lx-office.org
-#
-#=====================================================================
-#
-# Letter module
-#
-#=====================================================================
-
-package SL::Letter;
-
-use strict;
-use List::Util qw(max);
-
-use SL::Common;
-use SL::CT;
-use SL::DBUtils;
-use SL::MoreCommon;
-use SL::TransNumber;
-use SL::DB::Manager::Customer;
-
-my $DEFINITION = <<SQL;
-                                      Table "public.letter"
-        Column       |            Type             |                Modifiers
-  -------------------+-----------------------------+------------------------------------------
-   id                | integer                     | not null default nextval('id'::regclass)
-   vc_id             | integer                     | not null
-   letternumber      | text                        |
-   jobnumber         | text                        |
-   text_created_for  | text                        |
-   date              | date                        |
-   subject           | text                        |
-   greeting          | text                        |
-   body              | text                        |
-   close             | text                        |
-   company_name      | text                        |
-   employee_id       | integer                     |
-   employee_position | text                        |
-   salesman_id       | integer                     |
-   salesman_position | text                        |
-   itime             | timestamp without time zone | default now()
-   mtime             | timestamp without time zone |
-   page_created_for  | text                        |
-   intnotes          | text                        |
-   cp_id             | integer                     |
-   reference         | text                        |
-  Indexes:
-      "letter_pkey" PRIMARY KEY, btree (id)
-  Foreign-key constraints:
-      "letter_cp_id_fkey" FOREIGN KEY (cp_id) REFERENCES contacts(cp_id)
-      "letter_employee_id_fkey" FOREIGN KEY (employee_id) REFERENCES employee(id)
-      "letter_salesman_id_fkey" FOREIGN KEY (salesman_id) REFERENCES employee(id)
-SQL
-
-# XXX not working yet
-#sub customer {
-#  my $self = shift;
-#
-#  die 'not a setter' if @_;
-#
-#  return unless $self->{customer_id};
-#
-#  # resolve customer_obj
-#}
-
-sub new {
-  my $class  = ref $_[0] || $_[0]; shift;
-  my %params = @_;
-  my $ref    = $_[0];
-
-  $ref = ref $_[0] eq 'HASH' ? $ref : \%params; # don't like it either...
-
-  my $self = bless $ref, $class;
-
-  $self->_lastname_used;
-  $self->_resolve_customer;
-  $self->set_greetings;
-
-  return $self;
-}
-
-sub _create {
-  my $self = shift;
-  my $dbh  = $::form->get_standard_dbh;
-  ($self->{id}) = selectfirst_array_query($::form, $dbh, "select nextval('id')");
-
-  do_query($::form, $dbh, <<SQL, $self->{id}, $self->{customer_id});
-    INSERT INTO letter (id, vc_id) VALUES (?, ?);
-SQL
-}
-
-sub _create_draft {
-  my $self = shift;
-  my $dbh  = $::form->get_standard_dbh;
-  ($self->{draft_id}) = selectfirst_array_query($::form, $dbh, "select nextval('id')");
-
-  do_query($::form, $dbh, <<SQL, $self->{draft_id}, $self->{customer_id});
-    INSERT INTO letter_draft (id, vc_id) VALUES (?, ?);
-SQL
-}
-
-
-sub save {
-  $::lxdebug->enter_sub;
-
-  my $self     = shift;
-  my %params   = @_;
-  my $dbh      = $::form->get_standard_dbh;
-  my ($table, $update_value);
-
-  if ($params{draft}) {
-    $self->_create_draft unless $self->{draft_id};
-    $table = 'letter_draft';
-    $update_value = 'draft_id';
-  } else {
-    $self->_create unless $self->{id};
-    $table = 'letter';
-    $update_value = 'id';
-  }
-
-  my %fields         = __PACKAGE__->_get_fields;
-  my %field_mappings = __PACKAGE__->_get_field_mappings;
-
-  delete $fields{id};
-
-  my @update_fields = keys %fields;
-  my $set_clause    = join ', ', map { "$_ = ?" } @update_fields;
-  my @values        = map { _escaper($_)->( $self->{ $field_mappings{$_} || $_ } ) } @update_fields, $update_value;
-
-  my $query = "UPDATE $table SET $set_clause WHERE id = ?";
-
-  do_query($::form, $dbh, $query, @values);
-
-  $dbh->commit;
-
-  $::lxdebug->leave_sub;
-}
-
-sub find {
-  $::lxdebug->enter_sub;
-
-  my $class    = ref $_[0] || $_[0]; shift;
-  my $myconfig = \%main::myconfig;
-  my $form     = $main::form;
-  my $dbh      = $form->get_standard_dbh($myconfig);
-  my %params   = @_;
-  my $letter_table = 'letter';
-
-  $letter_table = 'letter_draft' if $params{draft};
-  %params = %$form if  !scalar keys %params;
-
-  my (@wheres, @values);
-  my $add_token = sub { add_token(\@wheres, \@values, @_) };
-
-  $add_token->(col => 'letter.id',           val => $params{id},           esc => 'id'    ) if $params{id};
-  $add_token->(col => 'letter.letternumber', val => $params{letternumber}, esc => 'substr') if $params{letternumber};
-  $add_token->(col => 'vc.name',             val => $params{customer},     esc => 'substr') if $params{customer};
-  $add_token->(col => 'vc.id',               val => $params{customer_id},  esc => 'id'    ) if $params{customer_id};
-  $add_token->(col => 'letter.cp_id',        val => $params{cp_id},        esc => 'id'    ) if $params{cp_id};
-  $add_token->(col => 'ct.cp_name',          val => $params{contact},      esc => 'substr') if $params{contact};
-  $add_token->(col => 'letter.subject',      val => $params{subject},      esc => 'substr') if $params{subject};
-  $add_token->(col => 'letter.body',         val => $params{body},         esc => 'substr') if $params{body};
-  $add_token->(col => 'letter.date',         val => $params{date_from}, method => '>='    ) if $params{date_from};
-  $add_token->(col => 'letter.date',         val => $params{date_to},   method => '<='    ) if $params{date_to};
-
-  my $query = qq|
-    SELECT $letter_table.*, vc.name AS customer, vc.id AS customer_id, ct.cp_name AS contact FROM $letter_table
-      LEFT JOIN customer vc ON vc.id = $letter_table.vc_id
-      LEFT JOIN contacts ct ON $letter_table.cp_id = ct.cp_id
-  |;
-
-  if (@wheres) {
-    $query .= ' WHERE ' . join ' AND ', @wheres;
-  }
-
-  my @results = selectall_hashref_query($form, $dbh, $query, @values);
-  my @objects = map { $class->new($_) } @results;
-
-  $::lxdebug->leave_sub;
-
-  return @objects;
-}
-
-sub delete {
-  $::lxdebug->enter_sub;
-
-  my $self     = shift;
-
-  do_query($::form, $::form->get_standard_dbh, <<SQL, $self->{id});
-    DELETE FROM letter WHERE id = ?
-SQL
-
-  $::form->get_standard_dbh->commit;
-
-  $::lxdebug->leave_sub;
-}
-
-sub delete_drafts {
-  $::lxdebug->enter_sub;
-
-  my $self        = shift;
-  my @draft_ids   = @_;
-
-  my $form        = $main::form;
-  my $myconfig = \%main::myconfig;
-  my $dbh         = $form->get_standard_dbh($myconfig);
-
-
-  return $main::lxdebug->leave_sub() unless (@draft_ids);
-
-  my  $query = qq|DELETE FROM letter_draft WHERE id IN (| . join(", ", map { "?" } @draft_ids) . qq|)|;
-  do_query($form, $dbh, $query, @draft_ids);
-
-  $dbh->commit;
-
-  $::lxdebug->leave_sub;
-}
-
-
-sub check_number {
-  my $self = shift;
-
-  return if $self->{letternumber}
-         && $self->{id}
-         && 1 == scalar __PACKAGE__->find(letternumber => $self->{letternumber});
-
-  $self->{letternumber} = SL::TransNumber->new(type => 'letter', id => $self->{id}, number => $self->{letternumber})->create_unique;
-}
-
-sub check_name {
-  my $self   = shift;
-  my %params = @_;
-
-  unless ($params{_name_selected}) {
-    $::form->{$_} = $self->{$_} for qw(oldcustomer customer selectcustomer customer_id);
-
-    if (::check_name('customer')) {
-      $self->_set_customer_from($::form);
-    }
-  } else {
-    $self->_set_customer_from($::form);
-  }
-}
-
-sub _set_customer_from {
-  my $self = shift;
-  my $from = shift;
-
-  $self->{$_} = $from->{$_} for qw(oldcustomer customer_id customer selectcustomer);
-
-  $self;
-}
-
-sub check_date {
-  my $self = shift;
-  $self->{date} ||= $::form->current_date(\%::myconfig);
-}
-
-sub load {
-  my $self   = shift;
-  my $table  = 'letter';
-  my $draft = $self->{draft};
-  $table     = 'letter_draft' if $draft;
-
-
-  return $self unless $self && $self->{id}; # no id? dont load.
-
-  my %mappings      = _get_field_mappings();
-  my $mapped_select = join ', ', '*', map { "$_ AS $mappings{$_}" } keys %mappings;
-
-  my ($db_letter) = selectfirst_hashref_query($::form, $::form->get_standard_dbh, <<SQL, $self->{id});
-    SELECT $mapped_select FROM $table WHERE id = ?
-SQL
-
-  $self->update_from($db_letter);
-  $self->_resolve_customer;
-  $self->set_greetings;
-  $self->{draft_id} = delete $self->{id} if $draft;  # set draft if we have one
-
-  return $self;
-}
-
-sub update_from {
-  my $self   = shift;
-  my $src    = shift;
-  my %fields = $self->_get_fields;
-
-  $fields{$_} = $src->{$_} for qw{customer_id customer selectcustomer oldcustomer}; # customer stuff
-
-  $self->{$_} = $src->{$_} for keys %fields;
-
-  return $self;
-}
-
-sub export_to {
-  my $self = shift;
-  my $form = shift;
-
-  my %fields         = $self->_get_fields;
-  my %field_mappings = $self->_get_field_mappings;
-
-  for (keys %fields) {
-    $form->{$_} =  _escaper($_)->( $self->{ $field_mappings{$_} || $_ } );
-  }
-}
-
-sub language {
-  my $self = shift;
-  die 'not a setter' if @_;
-
-  return unless $self->{cp_id};
-
-  # umetec/cetaq only!
-  # contacts have a custom variable called "mailing"
-  # it contains either a language code or the string "No"
-
-  my $custom_variables = CVar->get_custom_variables(
-    module      => 'Contacts',
-    name_prefix => 'cp',
-    trans_id    => $self->{cp_id},
-  );
-
-  my ($mailing) = grep { $_->{name} eq 'Mailing' } @$custom_variables;
-
-  return $mailing->{value} eq 'No' ? undef : $mailing->{value};
-}
-
-sub set_greetings {
-  $::lxdebug->enter_sub;
-
-  my $self = shift;
-  return $::lxdebug->leave_sub if $self->{greeting};
-
-  # automatically set greetings
-  # greetings depend mainly on contact person
-#   my $contact = $self->_get_contact;
-
-  $self->{greeting} = $::locale->text('Dear Sir or Madam,');
-
-  $::lxdebug->leave_sub;
-}
-
-sub _lastname_used {
-  # wrapper for form lastname_used
-  # sets customer to last used customer,
-  # also used to initalize customer for new objects
-  my $self = shift;
-
-  return if $self->{customer_id};
-
-  my $saved_form = save_form($::form);
-
-  $::form->lastname_used($::form->get_standard_dbh, \%::myconfig, 'customer');
-
-  $self->{customer_id} = $::form->{customer_id};
-  $self->{customer}    = $::form->{customer};
-
-  restore_form($saved_form);
-
-  return $self;
-}
-
-sub _resolve_customer {
-  # used if an object is created with only id.
-  my $self = shift;
-
-  return unless $self->{customer_id} && !$self->{customer};
-
-#  my ($customer) = CT->find_by_id(cv => 'customer', id => $self->{customer_id});
-#  my ($customer) = CT->find_by_id(cv => 'customer', id => $self->{customer_id});
-  # SL/CVar.pm:        : $cfg->{type} eq 'customer'  ? (SL::DB::Manager::Customer->find_by(id => 1*$ref->{number_value}) || SL::DB::Customer->new)->name
-  $self->{customer} = SL::DB::Manager::Customer->find_by(id => $self->{customer_id})->name; # || SL::DB::Customer->new)->name
-
-
-}
-
-sub _get_definition {
-  $DEFINITION;
-}
-
-sub _get_field_mappings {
-  return (
-    vc_id => 'customer_id',
-  );
-}
-
-sub _get_fields {
-  my %fields = _get_definition() =~ /(\w+) \s+ \| \s+ (integer|text|timestamp|numeric|date)/xg;
-}
-
-sub _escaper {
-  my $field_name = shift;
-  my %fields     = __PACKAGE__->_get_fields;
-
-  for ($fields{$field_name}) {
-    return sub { conv_i(shift) } if /integer/;
-    return sub { shift };
-  }
-}
-
-1;
diff --git a/bin/mozilla/letter.pl b/bin/mozilla/letter.pl
deleted file mode 100755 (executable)
index d9a0742..0000000
+++ /dev/null
@@ -1,568 +0,0 @@
-#=====================================================================
-# LX-Office ERP
-# Copyright (C) 2008
-# Based on SQL-Ledger Version 2.1.9
-# Web http://www.lx-office.org
-#
-#=====================================================================
-#
-# Letter module
-#
-#======================================================================
-
-use strict;
-use POSIX qw(strftime);
-
-use SL::GenericTranslations;
-use SL::ReportGenerator;
-use SL::Letter;
-use SL::CT;
-use SL::DB::Contact;
-use SL::DB::Default;
-use SL::Helper::CreatePDF;
-use SL::Helper::Flash;
-use SL::Common;
-use Cwd;
-require "bin/mozilla/reportgenerator.pl";
-require "bin/mozilla/io.pl";
-require "bin/mozilla/arap.pl";
-
-use constant TEXT_CREATED_FOR_VALUES => (qw(presskit fax letter));
-use constant PAGE_CREATED_FOR_VALUES => (qw(sketch 1 2));
-
-our ($form, %myconfig, $locale, $lxdebug);
-
-# parserhappy(R)
-# $locale->text('Presskit')
-# $locale->text('Sketch')
-# $locale->text('Fax')
-# $locale->text('Letter')
-
-sub add {
-  $::lxdebug->enter_sub;
-
-  $main::auth->assert('sales_letter_edit');
-  my %params = @_;
-
-  return $main::lxdebug->leave_sub if load_letter_draft();
-
-  my $letter = SL::Letter->new(%params);
-
-  if (my $cp_id = delete $::form->{contact_id}) {
-    my $contact = SL::DB::Manager::Contact->find_by(cp_id => $cp_id);
-    $letter->{cp_id}     = $contact->cp_id;
-    $letter->{vc_id}     = $contact->cp_cv_id;
-    $letter->{greeting}  = GenericTranslations->get(
-      translation_type => 'greetings::' . ($contact->{cp_gender} eq 'f' ? 'female' : 'male'),
-      language_id      => $contact->language_id,
-      allow_fallback   => 1
-    );
-    $params{language_id} = $contact->language_id;
-  }
-
-  $letter->check_date;
-
-  _display(
-    letter      => $letter,
-    title       => $locale->text('Add Letter'),
-    language_id => $params{language_id},
-  );
-
-  $::lxdebug->leave_sub;
-}
-
-sub edit {
-  $::lxdebug->enter_sub;
-
-  $main::auth->assert('sales_letter_edit');
-  add() unless ($form->{id});
-
-  my $letter = SL::Letter->new( id => $form->{id}, draft => $form->{draft} )->load;
-
-  add() unless $letter && ($letter->{id} || $letter->{draft_id});
-
-  _display(
-    letter => $letter,
-    title  => $locale->text('Edit Letter'),
-  );
-
-  $::lxdebug->leave_sub;
-}
-
-sub save {
-  $::lxdebug->enter_sub;
-
-  $main::auth->assert('sales_letter_edit');
-  my %params = @_;
-
-
-  $::form->error(t8('The subject is missing.')) unless $form->{letter}->{subject};
-  $::form->error(t8('The body is missing.')) unless $form->{letter}->{body};
-  $::form->error(t8('The employee is missing.')) unless $form->{letter}->{employee_id};
-
-  my $letter = _update();
-
-  $letter->check_number;
-  $letter->save;
-
-  $form->{SAVED_MESSAGE} = $locale->text('Letter saved!');
-
-  _display(
-    letter => $letter,
-  );
-
-  $::lxdebug->leave_sub;
-}
-
-sub save_letter_draft {
-  $::lxdebug->enter_sub;
-
-  $main::auth->assert('sales_letter_edit');
-
-  $::form->error(t8('The subject is missing.')) unless $form->{letter}->{subject};
-  $::form->error(t8('The body is missing.')) unless $form->{letter}->{body};
-  $::form->error(t8('The employee is missing.')) unless $form->{letter}->{employee_id};
-  $::form->error(t8('Already as letter saved.')) if $form->{letter}->{letternumber};
-
-  my $letter_draft = _update();
-  $letter_draft->{draft_id} = delete $letter_draft->{id}; # if we have one
-  $letter_draft->save(draft => '1');
-  $letter_draft->{vergiss_mich_nicht} = 'nicht vergessen';
-  $form->{SAVED_MESSAGE} = $locale->text('Draft for this Letter saved!');
-
-  _display(
-    letter => $letter_draft,
-  );
-
-  $::lxdebug->leave_sub;
-}
-
-sub delete {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('sales_letter_edit');
-  # NYI
-  $form->{SAVED_MESSAGE} = $locale->text('Not yet implemented!');
-  _display();
-
-  $main::lxdebug->leave_sub();
-}
-
-sub delete_letter_drafts {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('sales_letter_edit');
-
-  my @ids;
-  foreach (keys %{$form}) {
-    push @ids, $1 if (/^checked_(.*)/ && $form->{$_});
-  }
-
-  SL::Letter->delete_drafts(@ids) if (@ids); #->{id});
-
-  add();
-
-  $main::lxdebug->leave_sub();
-}
-
-sub _display {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('sales_letter_edit');
-  my %params = @_;
-
-  my $letter = $params{letter};
-
-  my %TMPL_VAR;
-
-  $form->{type}             = 'letter';   # needed for print_options
-  $form->{vc}               = 'customer'; # needs to be for _get_contacts...
-  $form->{"$form->{vc}_id"} ||= $letter->{customer_id};
-  $form->{jsscript}         = 1;
-  $form->{javascript}       =
-     qq|<script type="text/javascript" src="js/customer_or_vendor_selection.js"></script>
-        <script type="text/javascript" src="js/edit_part_window.js"></script>|;
-
-  $form->get_lists("contacts"      => "ALL_CONTACTS",
-  "employees"     => "ALL_EMPLOYEES",
-                   "salesmen"      => "ALL_SALESMEN",
-                   "departments"   => "ALL_DEPARTMENTS",
-                   "languages"     => "languages",
-                   "customers"     => { key   => "ALL_CUSTOMERS",
-                                        limit => $myconfig{vclimit} + 1 },
-                   "vc"            => 'customer',
-                   );
-
-  $TMPL_VAR{vc_keys}       = sub { "$_[0]->{name}--$_[0]->{id}" };
-  $TMPL_VAR{vc_select}     = "customer_or_vendor_selection_window('letter.customer', '', 0, 0)";
-  $TMPL_VAR{ct_labels}     = sub { ($_[0]->{cp_greeting} ? "$_[0]->{cp_greeting} " : '') .  $_[0]->{cp_name} .  ($_[0]->{cp_givenname} ? ", $_[0]->{cp_givenname}" : '') };
-  $TMPL_VAR{TCF}           = [ map { key => $_, value => $locale->text(ucfirst $_) }, TEXT_CREATED_FOR_VALUES() ];
-  $TMPL_VAR{PCF}           = [ map { key => $_, value => $locale->text(ucfirst $_) }, PAGE_CREATED_FOR_VALUES() ];
-
-  $form->header();
-
-  $form->{language_id} ||= $params{language_id};
-
-  print $form->parse_html_template('letter/edit', {
-    %params,
-    %TMPL_VAR,
-    letter        => $letter,
-    print_options => print_options(inline => 1),
-  });
-
-  $main::lxdebug->leave_sub();
-}
-
-sub search {
-  $lxdebug->enter_sub();
-
-  $main::auth->assert('sales_letter_report');
-
-  $form->get_lists("employees" => "EMPLOYEES",
-                   "salesmen"  => "SALESMEN",
-                   "customers" => "ALL_CUSTOMERS");
-
-  $form->{jsscript} = 1;
-  $form->{title}    = $locale->text('Letters');
-
-  $form->header();
-  print $form->parse_html_template('letter/search');
-
-  $lxdebug->leave_sub();
-}
-
-sub report {
-  $lxdebug->enter_sub();
-
-  $main::auth->assert('sales_letter_report');
-
-  my %params = @_;
-
-  my @report_params = qw(letternumber subject body contact date_from date_to cp_id);
-
-  if ($form->{selectcustomer}) {
-    push @report_params, 'customer_id';
-    $form->{customer_id} = $form->{customer};
-  } else {
-    push @report_params, 'customer';
-  }
-
-  report_generator_set_default_sort('date', 1);
-
-  %params = (%params, map { $_ => $form->{$_} } @report_params);
-
-  my @letters       = SL::Letter->find(%params);
-
-  $form->{rowcount} = @letters;
-  $form->{title}    = $locale->text('Letters');
-
-  my %column_defs = (
-    'date'                  => { 'text' => $locale->text('Date'), },
-    'subject'               => { 'text' => $locale->text('Subject'), },
-    'letternumber'          => { 'text' => $locale->text('Letternumber'), },
-    'customer'              => { 'text' => $locale->text('Customer') },
-    'contact'               => { 'text' => $locale->text('Contact') },
-    'date'                  => { 'text' => $locale->text('Date') },
-  );
-
-  my @columns = qw(date subject letternumber customer contact date);
-  my $href    = build_std_url('action=report', grep { $form->{$_} } @report_params);
-
-  my @sortable_columns = qw(date subject letternumber customer contact date);
-
-  foreach my $name (@sortable_columns) {
-    my $sortdir                 = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
-    $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
-  }
-
-  my @options;
-
-  # option line
-
-  push @options, $locale->text('Subject')                  . " : $form->{subject}"   if ($form->{subject});
-  push @options, $locale->text('Body')                     . " : $form->{body}"      if ($form->{body});
-
-  my @hidden_report_params = map { +{ 'key' => $_, 'value' => $form->{$_} } } @report_params;
-
-  my $report = SL::ReportGenerator->new(\%myconfig, $form, 'std_column_visibility' => 1);
-
-  $report->set_columns(%column_defs);
-  $report->set_column_order(@columns);
-
-  $report->set_export_options('report', @report_params);
-
-  $report->set_sort_indicator($form->{sort}, $form->{sortdir});
-
-  $report->set_options('raw_top_info_text'    => $form->parse_html_template('letter/report_top',    { 'OPTIONS' => \@options }),
-                       'raw_bottom_info_text' => $form->parse_html_template('letter/report_bottom', { 'HIDDEN'  => \@hidden_report_params }),
-                       'output_format'        => 'HTML',
-                       'title'                => $form->{title},
-                       'attachment_basename'  => $locale->text('letters_list') . strftime('_%Y%m%d', localtime time),
-    );
-  $report->set_options_from_form();
-
-  my $idx      = 0;
-  my $callback = build_std_url('action=report', grep { $form->{$_} } @report_params);
-  my $edit_url = build_std_url('action=edit', 'callback=' . E($callback));
-
-  foreach my $l (@letters) {
-    $idx++;
-
-    my $row = { map { $_ => { 'data' => $l->{$_} } } keys %{ $l } };
-
-    $row->{subject}->{link}      = $edit_url . '&id=' . Q($l->{id});
-    $row->{letternumber}->{link} = $edit_url . '&id=' . Q($l->{id});
-
-    $report->add_data($row);
-  }
-
-  $report->generate_with_headers();
-
-  $lxdebug->leave_sub();
-}
-
-sub print_letter {
-  $lxdebug->enter_sub();
-
-  $main::auth->assert('sales_letter_edit');
-
-  my ($old_form) = @_;
-
-  my $display_form = $form->{display_form} || "display_form";
-  my $letter       = _update();
-
-  $letter->export_to($form);
-  $form->{formname} = "letter";
-  $form->{format} = "pdf";
-
-  my $language_saved      = $form->{language_id};
-  my $greeting_saved      = $form->{greeting};
-  my $cp_id_saved         = $form->{cp_id};
-
-  call_sub("customer_details");
-
-  if (!$cp_id_saved) {
-    # No contact was selected. Delete all contact variables because
-    # IS->customer_details() and IR->vendor_details() get the default
-    # contact anyway.
-    map({ delete($form->{$_}); } grep(/^cp_/, keys(%{ $form })));
-  }
-
-  $form->{greeting} = $greeting_saved;
-  $form->{language_id} = $language_saved;
-
-  if ($form->{cp_id}) {
-    CT->get_contact(\%myconfig, $form);
-  }
-
-  $form->{cp_contact_formal} = ($form->{cp_greeting} ? "$form->{cp_greeting} " : '') . ($form->{cp_givenname} ? "$form->{cp_givenname} " : '') . $form->{cp_name};
-
-  $form->get_employee_data('prefix' => 'employee', 'id' => $letter->{employee_id});
-  $form->get_employee_data('prefix' => 'salesman', 'id' => $letter->{salesman_id});
-
-  my %create_params = (
-    template  => scalar(SL::Helper::CreatePDF->find_template(
-      name        => 'letter',
-      printer_id  => $::form->{printer_id},
-      language_id => $::form->{language_id},
-      formname    => 'letter',
-      format      => 'pdf',
-    )),
-    variables => $::form,
-    return    => 'file_name',
-  );
-  my $pdf_file_name;
-  eval {
-    # catch LaTeX template not found error
-    my $tex_templates  = $::instance_conf->get_templates . '/letter.tex';
-    die( t8('Please create/copy a template named letter.tex in your client template dir') ) unless (-e $tex_templates);
-
-    $pdf_file_name = SL::Helper::CreatePDF->create_pdf(%create_params);
-
-    # set some form defaults for printing webdav copy variables
-    $form->{tmpfile} = $pdf_file_name;
-    $form->{tmpdir} = 'users';
-    $form->{type} = 'letter';
-    $form->{cwd}        = getcwd();
-    if ( $::form->{media} eq 'email') {
-      my $mail             = Mailer->new;
-      my $signature        = $::myconfig{signature};
-      $mail->{$_}          = $::form->{$_}               for qw(cc subject message bcc to);
-      $mail->{from}        = qq|"$::myconfig{name}" <$::myconfig{email}>|;
-      $mail->{fileid}      = time() . '.' . $$ . '.';
-      $mail->{attachments} =  [{ "filename" => $pdf_file_name,
-                                 "name"     => $::form->{attachment_name} }];
-      $mail->{message}    .=  "\n-- \n$signature";
-      $mail->{message}     =~ s/\r//g;
-
-      # copy_file_to_webdav was already done via io.pl -> edit_e_mail
-      my $err = $mail->send;
-      # TODO
-      #       $self
-      #           ->js
-      #           ->flash($err?'error':'info',
-      #                   $err?t8('A mail error occurred: #1', $err):
-      #                        t8('The document have been sent to \'#1\'.', $mail->{to}))
-      #           ->render($self);
-      return $err?0:1;
-    }
-
-    if (!$::form->{printer_id} || $::form->{media} eq 'screen') {
-
-      my $file = IO::File->new($pdf_file_name, 'r') || croak("Cannot open file '$pdf_file_name'");
-      my $size = -s $pdf_file_name;
-      my $content_type    =  'application/pdf';
-      my $attachment_name =  $::form->generate_attachment_filename;
-      $attachment_name    =~ s:.*//::g;
-
-      print $::form->create_http_response(content_type        => $content_type,
-                                          content_disposition => 'attachment; filename="' . $attachment_name . '"',
-                                          content_length      => $size);
-
-      $::locale->with_raw_io(\*STDOUT, sub { print while <$file> });
-      $file->close;
-      Common::copy_file_to_webdav_folder($form) if $::instance_conf->get_webdav_documents;
-      unlink $pdf_file_name;
-      return 1;
-    }
-
-    my $printer = SL::DB::Printer->new(id => $::form->{printer_id})->load;
-    my $command = SL::Template::create(type => 'ShellCommand', form => Form->new(''))->parse($printer->printer_command);
-
-    open my $out, '|-', $command or die $!;
-    binmode $out;
-    print $out scalar(read_file($pdf_file_name));
-    close $out;
-    Common::copy_file_to_webdav_folder($form) if $::instance_conf->get_webdav_documents;
-
-    flash_later('info', t8('The documents have been sent to the printer \'#1\'.', $printer->printer_description));
-    my $callback = build_std_url('letter.pl', 'action=edit', 'id=' . $letter->{id}, 'printer_id');
-    $::form->redirect;
-    1;
-  } or do {
-    unlink $pdf_file_name;
-    $::form->error(t8("Creating the PDF failed:") . " " . $@);
-  };
-
-  $lxdebug->leave_sub();
-}
-
-sub update {
-  $::lxdebug->enter_sub;
-
-  $main::auth->assert('sales_letter_edit');
-
-  my $name_selected = shift;
-
-  _display(
-    letter => _update(
-      _name_selected => $name_selected,
-    ),
-  );
-
-  $::lxdebug->leave_sub;
-}
-
-sub _update {
-  $::lxdebug->enter_sub;
-
-  $main::auth->assert('sales_letter_edit');
-
-  my %params = @_;
-
-  my $from_letter = $::form->{letter};
-
-  my $letter      = SL::Letter->new( id => $from_letter->{id} )
-                              ->load
-                              ->update_from($from_letter);
-
-  $letter->check_name(%params);
-  $letter->check_date;
-  $letter->set_greetings;
-
-  $::lxdebug->leave_sub;
-
-  return $letter;
-}
-
-sub letter_tab {
-  $::lxdebug->enter_sub;
-
-  $main::auth->assert('sales_letter_edit');
-
-  my @report_params = qw(letternumber subject contact date);
-
-  my @letters       = SL::Letter->find(map { $_ => $form->{$_} } @report_params);
-
-  $::lxdebug->leave_sub;
-}
-
-sub e_mail {
-  $::lxdebug->enter_sub;
-
-  $main::auth->assert('sales_letter_edit');
-  my $letter = _update();
-
-  $letter->check_number;
-  $letter->save;
-
-  $form->{formname} = "letter";
-  $letter->export_to($::form);
-
-  $::form->{id} = $letter->{id};
-  edit_e_mail();
-
-  $::lxdebug->leave_sub;
-}
-
-sub dispatcher {
-  $main::lxdebug->enter_sub();
-  # dispatch drafts
-  my $locale   = $main::locale;
-
-
-  if ($form->{letter_draft_action} eq $locale->text("Skip")) {
-    $form->{DONT_LOAD_DRAFT} = 1;
-    add();
-    return 1;
-  } elsif ($form->{letter_draft_action} eq $locale->text("Delete drafts")) {
-    delete_letter_drafts();
-    return 1;
-  }
-
-  foreach my $action (qw(e_mail print save update save_letter_draft)) {
-    if ($::form->{"action_${action}"}) {
-      $::form->{dispatched_action} = $action;
-      call_sub($action);
-      return;
-    }
-  }
-
-  $::form->error($::locale->text('No action defined.'));
-  $::lxdebug->leave_sub;
-}
-
-sub continue {
-  call_sub($form->{nextsub});
-}
-
-
-sub load_letter_draft {
-  $lxdebug->enter_sub();
-
-  $main::auth->assert('sales_letter_edit');
- $main::lxdebug->leave_sub() and return 0 if ($form->{DONT_LOAD_DRAFT});
- $form->{title}    = $locale->text('Letter Draft');
- $form->{script}   = 'letter.pl';
-
-  my @letter_drafts = SL::Letter->find(draft => 1);
-
-  return unless @letter_drafts;
-  $form->header();
-  print $form->parse_html_template('letter/load_drafts', { LETTER_DRAFTS => \@letter_drafts });
-
-  return 1;
-  $lxdebug->leave_sub();
-}
-
-1;
index 4c1a5e4..7b5b175 100755 (executable)
@@ -246,6 +246,7 @@ $self->{texts} = {
   'Amount less skonto'          => 'Betrag abzgl. Skonto',
   'Amount payable'              => 'Noch zu bezahlender Betrag',
   'Amount payable less discount' => 'Noch zu bezahlender Betrag abzüglich Skonto',
+  'An error occured. Letter could not be deleted.' => 'Es ist ein Fehler aufgetreten. Der Brief konnte nicht gelöscht werden.',
   'An exception occurred during execution.' => 'Während der Ausführung trat eine Ausnahme auf.',
   'An invalid character was used (invalid characters: #1).' => 'Ein ungültiges Zeichen wurde benutzt (ungültige Zeichen: #1).',
   'An invalid character was used (valid characters: #1).' => 'Ein ungültiges Zeichen wurde benutzt (gültige Zeichen: #1).',
@@ -272,6 +273,7 @@ $self->{texts} = {
   'Are you sure you want to delete this business?' => 'Sind Sie sicher, dass Sie diesen Kunden-/Lieferantentyp löschen wollen?',
   'Are you sure you want to delete this delivery term?' => 'Wollen Sie diese Lieferbedingungen wirklich löschen?',
   'Are you sure you want to delete this department?' => 'Sind Sie sicher, dass Sie diese Abteilung löschen wollen?',
+  'Are you sure you want to delete this letter?' => 'Sind Sie sicher, dass Sie diesen Brief löschen wollen?',
   'Are you sure you want to delete this payment term?' => 'Wollen Sie diese Zahlungsbedingungen wirklich löschen?',
   'Are you sure you want to remove the marked entries from the queue?' => 'Sind Sie sicher, dass die markierten Einträge von der Warteschlange gelöscht werden sollen?',
   'Are you sure you want to update the prices' => 'Sind Sie sicher, dass Sie die Preise aktualisieren wollen?',
@@ -1533,6 +1535,7 @@ $self->{texts} = {
   'Left'                        => 'Links',
   'Letter'                      => 'Brief',
   'Letter Draft'                => 'Briefentwurf',
+  'Letter deleted'              => 'Brief gelöscht',
   'Letter saved!'               => 'Brief gespeichert!',
   'Letternumber'                => 'Briefnummer',
   'Letters'                     => 'Briefe',
@@ -2954,6 +2957,8 @@ $self->{texts} = {
   'There is one or more sections for which no part has been assigned yet; therefore creating the new record is not possible yet.' => 'Es gibt einen oder mehrere Abschnitte ohne Artikelzuweisung; daher kann der neue Beleg noch nicht erstellt werden.',
   'There was an error executing the background job.' => 'Bei der Ausführung des Hintergrund-Jobs trat ein Fehler auf.',
   'There was an error parsing the csv file: #1 in line #2: #3' => 'Es gab einen Fehler beim Parsen der CSV Datei: "#1" in der Zeile "#2": "#3"',
+  'There was an error saving the letter' => 'Ein Fehler ist aufgetreten. Der Brief konnte nicht gespeichert werden.',
+  'There was an error saving the letter draft' => 'Ein Fehler ist aufgetreten. Der Briefentwurf konnte nicht gespeichert werden.',
   'There you can let kivitendo create the basic tables for you, even in an already existing database.' => 'Dort können Sie kivitendo diese grundlegenden Tabellen erstellen lassen, selbst in einer bereits existierenden Datenbank.',
   'Therefore several settings that had to be made for each user in the past have been consolidated into the client configuration.' => 'Dazu wurden gewisse Einstellungen, die vorher bei jedem Benutzer vorgenommen werden mussten, in die Konfiguration eines Mandanten verschoben.',
   'Therefore the definition of "kg" with the base unit "g" and a factor of 1000 is valid while defining "g" with a base unit of "kg" and a factor of "0.001" is not.' => 'So ist die Definition von "kg" mit der Basiseinheit "g" und dem Faktor 1000 zulässig, die Definition von "g" mit der Basiseinheit "kg" und dem Faktor "0,001" hingegen nicht.',
index f693a0f..052853a 100644 (file)
   name: Add Letter
   order: 800
   access: sales_letter_edit
-  module: letter.pl
   params:
-    action: add
+    action: Letter/add
 - parent: ar
   id: ar_invoices
   name: Invoices
   name: Letters
   order: 1100
   access: sales_letter_report
-  module: letter.pl
   params:
-    action: search
+    action: Letter/list
 - id: ap
   name: AP
   icon: ap
index 5d6aebf..1e84de8 100644 (file)
 [%- USE HTML %]
 [%- USE T8 %]
 [%- USE L %]
- <h1>[% title %]</h1>
-
- <form action='letter.pl' method='POST'>
+[%- USE P %]
+[%- USE LxERP %]
+<h1>[% title | html %]</h1>
 
+<form action='controller.pl' method='POST'>
   <input type="hidden" name="letter.id" value="[% letter.id | html %]">
-  <input type="hidden" name="letter.draft_id" value="[% letter.draft_id | html %]">
-  <input type="hidden" name="title" value="[% title | html %]">
+  <input type="hidden" name="draft.id" value="[% draft.id | html %]">
   <input type="hidden" name="type" value="[% type | html %]">
-  <input type="hidden" name="print_nextsub" value="print_letter">
 
+  [%- PROCESS 'common/flash.html' %]
 
-<p>
- <table width=100%>
-  [%- IF SAVED_MESSAGE %]
-  <tr>
-    <td colspan=2>[% SAVED_MESSAGE %]</td>
-  </tr>
-  <tr height=10px><td></td></tr>
-  [%- END %]
-  <tr>
-   <td width=50%>
-    <!-- upper left block -->
-     <table width=90%>
-      <tr>
-       <th align='right'>[% 'Customer' | $T8 %]:</th>
-       <td>
-        [%- INCLUDE 'generic/multibox.html'
-             name          = 'letter.customer',
-             style         = 'width:60%',
-             DATA          = ALL_CUSTOMERS,
-             id_sub        = 'vc_keys',
-             label_key     = 'name',
-             select        = vc_select,
-             limit         = MYCONFIG.vclimit,
-             allow_textbox = 1,
-             force_textbox = limit_exceeded_all_customer
-             onChange      = "document.getElementById('update_button').click();" -%]
+<table width=100%>
+<tr>
+ <td width=50%>
+  <!-- upper left block -->
+   <table width=90%>
+    <tr>
+     <th align='right'>[% 'Customer' | $T8 %]:</th>
+     <td>[% P.customer_vendor_picker('letter.vc_id', letter.vc_id, type='customer') %]
 [%- IF letter.customer_id %]
-        <input type="button" value="[% 'Details (one letter abbreviation)' | $T8 %]" onclick="show_vc_details('customer')">
-[%- END %]
-        <input type='hidden' name='letter.oldcustomer' value='[% letter.oldcustomer | html %]'>
-        <input type='hidden' name='letter.customer_id' value='[% letter.customer_id | html %]'>
-        <input type='hidden' name='customer_id' value='[% customer_id | html %]'>
-[%- UNLESS MYCONFIG.vclimit < ALL_CUSTOMERS.size %]
-        <input type="hidden" name="letter.select[% vc %]" value="1">
+      <input type="button" value="[% 'Details (one letter abbreviation)' | $T8 %]" onclick="show_vc_details('customer')">
 [%- END %]
-      </tr>
-      <tr>
-       <th align='right'>[% 'Contact Person' | $T8 %]</th>
-       <td>
-        [%- INCLUDE 'generic/multibox.html'
-             name       = 'letter.cp_id',
-             style      = 'width:100%',
-             DATA       = ALL_CONTACTS,
-             id_key     = 'cp_id',
-             label_sub  = 'ct_labels',
-             show_empty = 1 -%]
-       </td>
-      </tr>
-      <tr>
-       <th align='right'>[% 'Your Reference' | $T8 %]:</th>
-       <td><input name='letter.reference' style='width:70%' value='[% letter.reference %]'></td>
-      </tr>
-     </table>
-    <!-- /upper left block -->
-   </td>
-   <td width=50%>
-    <!-- upper right block -->
-     <table align=center width=90%>
-      <tr>
-       <th align='right'>[% 'Letternumber' | $T8 %]:</th>
-       <td><input name='letter.letternumber' style='width:70%' value='[% letter.letternumber %]'></td>
-      </tr>
-      <tr>
-       <th align='right'>[% 'Date' | $T8 %]:</th>
-        <td>[% L.date_tag('letter.date', letter.date, readonly=readonly) %]</td>
-       </td>
-      </tr>
-     </table>
-    <!-- /upper right block -->
-   </td>
-  </tr>
+     </td>
+    </tr>
+    <tr>
+     <th align='right'>[% 'Contact Person' | $T8 %]</th>
+     <td>[% L.select_tag('letter.cp_id', letter.cp_id ? letter.customer.contacts : [], value_key='cp_id', title_key='full_name', default=letter.cp_id) %]</td>
+    </tr>
+    <tr>
+     <th align='right'>[% 'Your Reference' | $T8 %]:</th>
+     <td><input name='letter.reference' style='width:70%' value='[% letter.reference | html %]'></td>
+    </tr>
+   </table>
+  <!-- /upper left block -->
+ </td>
+ <td width=50%>
+  <!-- upper right block -->
+   <table align=center width=90%>
+    <tr>
+     <th align='right'>[% 'Letternumber' | $T8 %]:</th>
+     <td><input name='letter.letternumber' style='width:70%' value='[% letter.letternumber | html %]'></td>
+    </tr>
+    <tr>
+     <th align='right'>[% 'Date' | $T8 %]:</th>
+      <td>[% L.date_tag('letter.date_as_date', letter.date_as_date, readonly=readonly) %]</td>
+     </td>
+    </tr>
+   </table>
+  <!-- /upper right block -->
+ </td>
+</tr>
 
-  <tr height=20></tr>
+<tr height=20></tr>
 
-  <tr>
  <td colspan=2 width=100%>
-    <!-- central block -->
-     <table width=80%>
-      <tr>
-       <th align=right>[% 'Subject' | $T8 %]</th>
-       <td><textarea name='letter.subject' style='width:100%;font-weight:bold' rows=1>[% letter.subject %]</textarea></td>
-      </tr>
-       <th align=right>[% 'Greeting' | $T8 %]</th>
-       <td><input name='letter.greeting' style='width:100%;font-weight:bold' value="[% letter.greeting | html %]"></td>
-      </tr>
-      <tr>
-      <tr>
-       <th align=right>[% 'Body' | $T8 %]</th>
-       <td><textarea name='letter.body' style='width:100%' rows=20>[% letter.body | html %]</textarea></td>
-      </tr>
-      <tr height=10></tr>
-       <th align=right>[% 'Internal Notes' | $T8 %]</th>
-       <td><textarea name='letter.intnotes' style='width:100%' rows=4>[% letter.intnotes | html %]</textarea></td>
-      </tr>
+<tr>
+ <td colspan=2 width=100%>
+  <!-- central block -->
+   <table width=80%>
+    <tr>
+     <th align=right>[% 'Subject' | $T8 %]</th>
+     <td><textarea name='letter.subject' style='width:100%;font-weight:bold' rows=1>[% letter.subject | html %]</textarea></td>
+    </tr>
+     <th align=right>[% 'Greeting' | $T8 %]</th>
+     <td><input name='letter.greeting' style='width:100%;font-weight:bold' value="[% letter.greeting | html %]"></td>
+    </tr>
+    <tr>
+    <tr>
+     <th align=right>[% 'Body' | $T8 %]</th>
+     <td><textarea name='letter.body' style='width:100%' rows=20>[% letter.body | html %]</textarea></td>
+    </tr>
+    <tr height=10></tr>
+     <th align=right>[% 'Internal Notes' | $T8 %]</th>
+     <td><textarea name='letter.intnotes' style='width:100%' rows=4>[% letter.intnotes | html %]</textarea></td>
+    </tr>
 
-      <tr>
-       <th></th>
-       <td>
-        <table width=90%>
-         <tr>
-          <td>
-           <table width=100%>
-            <tr>
-             <td>[% 'Employee' | $T8 %]</td>
-            </tr>
-            <tr>
-             <td>
-              [%- INCLUDE 'generic/multibox.html'
-                   name          = 'letter.employee_id',
-                   default       = letter.employee_id,
-                   style         = 'width:70%;font-weight:bold',
-                   DATA          = ALL_EMPLOYEES,
-                   id_key        = 'id',
-                   label_key     = 'name',
-                   limit         = vclimit,
-                   show_empty    = 1,
-                   allow_textbox = 0,
-                   force_textbox = limit_exceeded_ALL_EMPLOYEES
-                   onChange      = "document.getElementById('update_button').click();" -%]
-             </td>
-            </tr>
-            <!-- tr><td><input name="employee_position" style=width:70% value='[% employee_position %]'></td></tr -->
-           </table>
-          </td>
-          <td>
-           <table width=100%>
-            <tr>
-             <td>[% 'Salesman' | $T8 %]</td>
-            </tr>
-            <tr>
-             <td>
-              [%- INCLUDE 'generic/multibox.html'
-                   name          = 'letter.salesman_id',
-                   default       = letter.salesman_id,
-                   style         = 'width:70%;font-weight:bold',
-                   DATA          = ALL_SALESMEN,
-                   id_key        = 'id',
-                   label_key     = 'name',
-                   limit         = vclimit,
-                   show_empty    = 1,
-                   allow_textbox = 0,
-                   force_textbox = limit_exceeded_ALL_SALESMAN
-                   onChange      = "document.getElementById('update_button').click();" -%]
-             </td>
-            </tr>
-           </table>
-          </td>
-          <td>
-          </td>
-         </tr>
-        </table>
-       </td>
-      </tr>
-     </table>
-    <!-- /central block -->
-   </td>
-  </tr>
-  <tr>
-    <td colspan=3><hr size="3" noshade></td>
-  </tr>
-  <tr>
-   <td>
-     [% print_options %]
-   </td>
-  </tr>
- </table>
-</p>
+    <tr>
+     <th></th>
+     <td>
+      <table width=90% align='center'>
+       <tr>
+        <td>
+         <table width=100%>
+          <tr>
+           <td>[% 'Employee' | $T8 %]</td>
+          </tr>
+          <tr>
+           <td>
+            [%- L.select_tag('letter.employee_id', employees, default=letter.employee_id, title_key='safe_name', class='bold', allow_empty=1, style='width:70%') %]
+           </td>
+          </tr>
+         </table>
+        </td>
+        <td>
+         <table width=100%>
+          <tr>
+           <td>[% 'Salesman' | $T8 %]</td>
+          </tr>
+          <tr>
+           <td>
+            [%- L.select_tag('letter.salesman_id', employees, default=letter.salesman_id, title_key='safe_name', class='bold', allow_empty=1, style='width:70%') %]
+           </td>
+          </tr>
+         </table>
+        </td>
+        <td>
+        </td>
+       </tr>
+      </table>
+     </td>
+    </tr>
+   </table>
+  <!-- /central block -->
+ </td>
+</tr>
+<tr>
+  <td colspan=3><hr size="3" noshade></td>
+</tr>
+<tr>
+ <td>
+   [% print_options %]
+ </td>
+</tr>
+</table>
 
-<input type="hidden" name="action" value="dispatcher">
+<input type="hidden" name="action" value="Letter/dispatch">
 <input class="submit" type="submit" name="action_update" id="update_button" value="[% 'Update' | $T8 %]">
 
 [%- IF letter.letternumber %]
-  <input class="submit" type="submit" name="action_print" value="[% 'Print' | $T8 %]">
+  <input class="submit" type="submit" name="action_print_letter" value="[% 'Print' | $T8 %]">
 [% END %]
 
 <input class="submit" type="submit" name="action_save" value="[% 'Save' | $T8 %]">
+[% L.submit_tag('action_delete', LxERP.t8('Delete'), confirm=LxERP.t8('Are you sure you want to delete this letter?')) %]
 <input class="submit" type="submit" name="action_save_letter_draft" value="[% 'Save Draft' | $T8 %]">
 
- </form>
+</form>
+
+
+<script type='text/javascript'>
+  $(function(){
+    $('#letter_vc_id').change(function(){
+      var data = $('form').serializeArray();
+      data.push({ name: 'action_update_contacts', value: 1 });
+      $.post('controller.pl', data, kivi.eval_json_result);
+    })
+  })
+</script>
index 78c04cd..ceb1d54 100644 (file)
@@ -2,11 +2,9 @@
 [%- USE HTML %]
 <h1>[% 'Load letter draft' | $T8 %]</h1>
 
- <form method="post" name="Form" action="letter.pl">
+ <form method="post" name="Form" action="controller.pl">
+  <p>[% 'The following drafts have been saved and can be loaded.' | $T8 %]</p>
   <table width="100%">
-   <tr>
-    <td>
-     [% 'The following drafts have been saved and can be loaded.' | $T8 %]
     </td>
    </tr>
    <tr>
       [% FOREACH row = LETTER_DRAFTS %]
        <tr class="listrow[% loop.count % 2 %]">
         <td><input type="checkbox" name="checked_[% row.id %]" value="1"></td>
-        <td>[% HTML.escape(row.date) %]</td>
-        <td><a href="letter.pl?action=edit&callback=letter.pl&draft=1&id=[% HTML.url(row.id) %]">[% HTML.escape(row.subject) %]</a></td>
-        <td>[% HTML.escape(row.customer) %]</td>
+        <td>[% row.date | html %]</td>
+        <td><a href="[% SELF.url_for(action='edit', 'draft.id'=row.id) %]">[% row.subject | html %]</a></td>
+        <td>[% row.customer | html %]</td>
        </tr>
       [% END %]
      </table>
     </td>
    </tr>
    <tr>
-     <input type="hidden" name="action" value="dispatcher">
-     <input type="hidden" name="draft" value="1"><!-- maybe not needed -->
-     <input type="submit" class="submit" name="letter_draft_action" value="[% 'Skip' | $T8 %]">
-     <input type="submit" class="submit" name="letter_draft_action" value="[% 'Delete drafts' | $T8 %]">
+    <td>
+     <input type="hidden" name="action" value="Letter/dispatch">
+     <input type="submit" class="submit" name="action_skip_draft" value="[% 'Skip' | $T8 %]">
+     <input type="submit" class="submit" name="action_delete_draft" value="[% 'Delete drafts' | $T8 %]">
    </td>
    </tr>
   </table>
index 548c4ed..b5ad9a5 100644 (file)
@@ -1,10 +1,4 @@
-[%- IF OPTIONS.size %]
-<p>
- [%- FOREACH option = OPTIONS %]
- [%- option %][% UNLESS loop.last %]<br>[% END %]
- [%- END %]
-</p>
-[%- END %]
+[%- PROCESS 'letter/search.html' filter=SELF.models.filtered.laundered %]
+<hr>
 
-<form action="letter.pl" method="post" name="Form">
 
index 365e7e7..4d12934 100644 (file)
@@ -1,65 +1,57 @@
 [% USE HTML %]
 [% USE T8 %]
 [% USE L %]
- <h1>[% title %]</h1>
+[% USE LxERP %]
+<form action="controller.pl" method="post" name="Form">
 
- <form action="letter.pl" method="post" name="Form">
-  <input type="hidden" name="nextsub" value="report">
+<div class='filter_toggle'>
+<a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Show Filter' | $T8 %]</a>
+  [% SELF.filter_summary | html %]
+</div>
+<div class='filter_toggle' style='display:none'>
+<a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Hide Filter' | $T8 %]</a>
 
-  <p>
-   <table>
-    <tr>
-     <th align='right'>[% 'Letternumber' | $T8 %]</th>
-     <td><input name='letternumber' style='width:250px' value='[% letternumber %]'></th>
-    </tr>
-    <tr>
-     <td align="right">[% 'Customer' | $T8 %]</td>
-     <td>
-        [%- INCLUDE 'generic/multibox.html'
-             name          = 'customer',
-             style         = 'width:250px',
-             DATA          = ALL_CUSTOMERS,
-             id_key        = 'id',
-             label_key     = 'name',
-             select        = vc_select,
-             limit         = MYCONFIG.vclimit,
-             allow_textbox = 1,
-             show_empty    = 1,
-             force_textbox = limit_exceeded_all_customer
-             onChange      = "document.getElementById('update_button').click();" -%]
-[%- IF MYCONFIG.vclimit > ALL_CUSTOMERS.size %]
-       <input type="hidden" name='selectcustomer' value="1">
-[%- END %]
-     </td>
-    </tr>
 
-    <tr>
-     <td align="right">[% 'Contact' | $T8 %]</td>
-     <td><input name="contact" style='width:250px'></td>
-    </tr>
+ <table id='filter_table'>
+  <tr>
+   <th align='right'>[% 'Letternumber' | $T8 %]</th>
+   <td>[% L.input_tag('filter.letternumber:substr::ilike', filter.letternumber_substr__ilike, style='width:250px') %]</th>
+  </tr>
+  <tr>
+   <td align="right">[% 'Customer' | $T8 %]</td>
+   <td>[% L.customer_vendor_picker('filter.vc_id', filter.vc_id, type='customer', style='width:250px') %]</td>
+  </tr>
 
-    <tr>
-     <td align="right">[% 'Subject' | $T8 %]</td>
-     <td><input name="subject" style='width:250px'></td>
-    </tr>
+  <tr>
+   <td align="right">[% 'Contact' | $T8 %]</td>
+   <td>[% L.input_tag('filter.contact.cp_name:substr::ilike', filter.contact.cp_name_substr__ilike, style='width:250px') %]</th>
+  </tr>
 
-    <tr>
-     <td align="right">[% 'Body' | $T8 %]</td>
-     <td><input name="body" style='width:250px'></td>
-    </tr>
+  <tr>
+   <td align="right">[% 'Subject' | $T8 %]</td>
+   <td>[% L.input_tag('filter.subject:substr::ilike', filter.subject_substr__ilike, style='width:250px') %]</th>
+  </tr>
 
-    <tr>
-     <td align='right'>[% 'From' | $T8 %]</td>
-     <td>
-      [% L.date_tag('date_from') %]
-      [% 'To (time)' | $T8 %]
-      [% L.date_tag('date_to') %]
-     </td>
-    </tr>
-   </table>
-  </p>
+  <tr>
+   <td align="right">[% 'Body' | $T8 %]</td>
+   <td>[% L.input_tag('filter.body:substr::ilike', filter.body_substr__ilike, style='width:250px') %]</th>
+  </tr>
 
-  <p>
-   <input type="submit" name="action" value="[% 'Continue' | $T8 %]">
-  </p>
- </form>
+  <tr>
+   <td align='right'>[% 'From' | $T8 %]</td>
+   <td> [% L.date_tag('filter.date:date::ge', filter.date_date__ge) %]
+        [% 'To (time)' | $T8 %]
+        [% L.date_tag('filter.date:date::le', filter.date_date__le) %]</td>
+  </tr>
+ </table>
+
+ [% L.hidden_tag('sort_by', FORM.sort_by) %]
+ [% L.hidden_tag('sort_dir', FORM.sort_dir) %]
+ [% L.hidden_tag('page', FORM.page) %]
+ [% L.hidden_tag('action', 'Letter/dispatch') %]
+ [% L.submit_tag('action_list', LxERP.t8('Continue')) %]
+
+<a href='#' onClick='javascript:$("#filter_table input").val("");$("#filter_table input[type=checkbox]").prop("checked", 0);$("#filter_table select").prop("selectedIndex", 0);'>[% 'Reset' | $T8 %]</a>
+</div>
+
+</form>