Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision a9b2cbe2

Von Jan Büren vor etwa 9 Jahren hinzugefügt

  • ID a9b2cbe24d8c27e71b0683548cf9b358cd5f724b
  • Vorgänger 30c9fd20
  • Nachfolger bb197f4f

Brieffunktion erste Version

Verkaufsbriefe können jetzt auch mit LaTeX erstellt werden.
Ferner gibt es eine Briefentwurfs-Funktion, die aussieht wie drafts, aber
zumindestens auf einer eigenen Tabelle beruht. Zusätzlich wurden zwei neue Rechte
gesetzt

Unterschiede anzeigen:

SL/Auth.pm
957 957
    ["sales_delivery_order_edit",      $locale->text("Create and edit sales delivery orders")],
958 958
    ["invoice_edit",                   $locale->text("Create and edit invoices and credit notes")],
959 959
    ["dunning_edit",                   $locale->text("Create and edit dunnings")],
960
    ["sales_letter_edit",              $locale->text("Edit sales letters")],
960 961
    ["sales_all_edit",                 $locale->text("View/edit all employees sales documents")],
961 962
    ["edit_prices",                    $locale->text("Edit prices and discount (if not used, textfield is ONLY set readonly)")],
962 963
    ["show_ar_transactions",           $locale->text("Show AR transactions as part of AR invoice report")],
963 964
    ["delivery_plan",                  $locale->text("Show delivery plan")],
964 965
    ["delivery_value_report",          $locale->text("Show delivery value report")],
966
    ["sales_letter_report",            $locale->text("Show sales letters report")],
965 967
    ["--ap",                           $locale->text("AP")],
966 968
    ["request_quotation_edit",         $locale->text("Create and edit RFQs")],
967 969
    ["purchase_order_edit",            $locale->text("Create and edit purchase orders")],
SL/Form.pm
1235 1235
    sales_delivery_order    => $main::locale->text('Delivery Order'),
1236 1236
    purchase_delivery_order => $main::locale->text('Delivery Order'),
1237 1237
    dunning                 => $main::locale->text('Dunning'),
1238
    letter                  => $main::locale->text('Letter')
1238 1239
  );
1239 1240

  
1240 1241
  $main::lxdebug->leave_sub();
......
1249 1250
      (first { $self->{type} eq $_ } qw(invoice credit_note)) ? 'inv'
1250 1251
    : ($self->{type} =~ /_quotation$/)                        ? 'quo'
1251 1252
    : ($self->{type} =~ /_delivery_order$/)                   ? 'do'
1253
    : ($self->{type} =~ /letter/)                             ? 'letter'
1252 1254
    :                                                           'ord';
1253 1255

  
1256
  # better default like this?
1257
  # : ($self->{type} =~ /(sales|purcharse)_order/           :  'ord';
1258
  # :                                                           'prefix_undefined';
1259

  
1254 1260
  $main::lxdebug->leave_sub();
1255 1261
  return $prefix;
1256 1262
}
......
2387 2393
  if ($params{contacts} || $params{shipto}) {
2388 2394
    $vc = 'customer' if $self->{"vc"} eq "customer";
2389 2395
    $vc = 'vendor'   if $self->{"vc"} eq "vendor";
2390
    die "invalid use of get_lists, need 'vc'";
2396
    die "invalid use of get_lists, need 'vc'" unless $vc;
2391 2397
    $vc_id = $self->{"${vc}_id"};
2392 2398
  }
2393 2399

  
SL/Letter.pm
1
#=====================================================================
2
# LX-Office ERP
3
# Copyright (C) 2008
4
# Based on SQL-Ledger Version 2.1.9
5
# Web http://www.lx-office.org
6
#
7
#=====================================================================
8
#
9
# Letter module
10
#
11
#=====================================================================
12

  
13
package SL::Letter;
14

  
15
use strict;
16
use List::Util qw(max);
17

  
18
use SL::Common;
19
use SL::CT;
20
use SL::DBUtils;
21
use SL::MoreCommon;
22
use SL::TransNumber;
23
use SL::DB::Manager::Customer;
24

  
25
my $DEFINITION = <<SQL;
26
                                      Table "public.letter"
27
        Column       |            Type             |                Modifiers
28
  -------------------+-----------------------------+------------------------------------------
29
   id                | integer                     | not null default nextval('id'::regclass)
30
   vc_id             | integer                     | not null
31
   letternumber      | text                        |
32
   jobnumber         | text                        |
33
   text_created_for  | text                        |
34
   date              | date                        |
35
   subject           | text                        |
36
   greeting          | text                        |
37
   body              | text                        |
38
   close             | text                        |
39
   company_name      | text                        |
40
   employee_id       | integer                     |
41
   employee_position | text                        |
42
   salesman_id       | integer                     |
43
   salesman_position | text                        |
44
   itime             | timestamp without time zone | default now()
45
   mtime             | timestamp without time zone |
46
   page_created_for  | text                        |
47
   intnotes          | text                        |
48
   cp_id             | integer                     |
49
   reference         | text                        |
50
  Indexes:
51
      "letter_pkey" PRIMARY KEY, btree (id)
52
  Foreign-key constraints:
53
      "letter_cp_id_fkey" FOREIGN KEY (cp_id) REFERENCES contacts(cp_id)
54
      "letter_employee_id_fkey" FOREIGN KEY (employee_id) REFERENCES employee(id)
55
      "letter_salesman_id_fkey" FOREIGN KEY (salesman_id) REFERENCES employee(id)
56
SQL
57

  
58
# XXX not working yet
59
#sub customer {
60
#  my $self = shift;
61
#
62
#  die 'not a setter' if @_;
63
#
64
#  return unless $self->{customer_id};
65
#
66
#  # resolve customer_obj
67
#}
68

  
69
sub new {
70
  my $class  = ref $_[0] || $_[0]; shift;
71
  my %params = @_;
72
  my $ref    = $_[0];
73

  
74
  $ref = ref $_[0] eq 'HASH' ? $ref : \%params; # don't like it either...
75

  
76
  my $self = bless $ref, $class;
77

  
78
  $self->_lastname_used;
79
  $self->_resolve_customer;
80
  $self->set_greetings;
81

  
82
  return $self;
83
}
84

  
85
sub _create {
86
  my $self = shift;
87
  my $dbh  = $::form->get_standard_dbh;
88
  ($self->{id}) = selectfirst_array_query($::form, $dbh, "select nextval('id')");
89

  
90
  do_query($::form, $dbh, <<SQL, $self->{id}, $self->{customer_id});
91
    INSERT INTO letter (id, vc_id) VALUES (?, ?);
92
SQL
93
}
94

  
95
sub _create_draft {
96
  my $self = shift;
97
  my $dbh  = $::form->get_standard_dbh;
98
  ($self->{draft_id}) = selectfirst_array_query($::form, $dbh, "select nextval('id')");
99

  
100
  do_query($::form, $dbh, <<SQL, $self->{draft_id}, $self->{customer_id});
101
    INSERT INTO letter_draft (id, vc_id) VALUES (?, ?);
102
SQL
103
}
104

  
105

  
106
sub save {
107
  $::lxdebug->enter_sub;
108

  
109
  my $self     = shift;
110
  my %params   = @_;
111
  my $dbh      = $::form->get_standard_dbh;
112
  my ($table, $update_value);
113

  
114
  if ($params{draft}) {
115
    $self->_create_draft unless $self->{draft_id};
116
    $table = 'letter_draft';
117
    $update_value = 'draft_id';
118
  } else {
119
    $self->_create unless $self->{id};
120
    $table = 'letter';
121
    $update_value = 'id';
122
  }
123

  
124
  my %fields         = __PACKAGE__->_get_fields;
125
  my %field_mappings = __PACKAGE__->_get_field_mappings;
126

  
127
  delete $fields{id};
128

  
129
  my @update_fields = keys %fields;
130
  my $set_clause    = join ', ', map { "$_ = ?" } @update_fields;
131
  my @values        = map { _escaper($_)->( $self->{ $field_mappings{$_} || $_ } ) } @update_fields, $update_value;
132

  
133
  my $query = "UPDATE $table SET $set_clause WHERE id = ?";
134

  
135
  do_query($::form, $dbh, $query, @values);
136

  
137
  $dbh->commit;
138

  
139
  $::lxdebug->leave_sub;
140
}
141

  
142
sub find {
143
  $::lxdebug->enter_sub;
144

  
145
  my $class    = ref $_[0] || $_[0]; shift;
146
  my $myconfig = \%main::myconfig;
147
  my $form     = $main::form;
148
  my $dbh      = $form->get_standard_dbh($myconfig);
149
  my %params   = @_;
150
  my $letter_table = 'letter';
151

  
152
  $letter_table = 'letter_draft' if $params{draft};
153
  %params = %$form if  !scalar keys %params;
154

  
155
  my (@wheres, @values);
156
  my $add_token = sub { add_token(\@wheres, \@values, @_) };
157

  
158
  $add_token->(col => 'letter.id',           val => $params{id},           esc => 'id'    ) if $params{id};
159
  $add_token->(col => 'letter.letternumber', val => $params{letternumber}, esc => 'substr') if $params{letternumber};
160
  $add_token->(col => 'vc.name',             val => $params{customer},     esc => 'substr') if $params{customer};
161
  $add_token->(col => 'vc.id',               val => $params{customer_id},  esc => 'id'    ) if $params{customer_id};
162
  $add_token->(col => 'letter.cp_id',        val => $params{cp_id},        esc => 'id'    ) if $params{cp_id};
163
  $add_token->(col => 'ct.cp_name',          val => $params{contact},      esc => 'substr') if $params{contact};
164
  $add_token->(col => 'letter.subject',      val => $params{subject},      esc => 'substr') if $params{subject};
165
  $add_token->(col => 'letter.body',         val => $params{body},         esc => 'substr') if $params{body};
166
  $add_token->(col => 'letter.date',         val => $params{date_from}, method => '>='    ) if $params{date_from};
167
  $add_token->(col => 'letter.date',         val => $params{date_to},   method => '<='    ) if $params{date_to};
168

  
169
  my $query = qq|
170
    SELECT $letter_table.*, vc.name AS customer, vc.id AS customer_id, ct.cp_name AS contact FROM $letter_table
171
      LEFT JOIN customer vc ON vc.id = $letter_table.vc_id
172
      LEFT JOIN contacts ct ON $letter_table.cp_id = ct.cp_id
173
  |;
174

  
175
  if (@wheres) {
176
    $query .= ' WHERE ' . join ' AND ', @wheres;
177
  }
178

  
179
  my @results = selectall_hashref_query($form, $dbh, $query, @values);
180
  my @objects = map { $class->new($_) } @results;
181

  
182
  $::lxdebug->leave_sub;
183

  
184
  return @objects;
185
}
186

  
187
sub delete {
188
  $::lxdebug->enter_sub;
189

  
190
  my $self     = shift;
191

  
192
  do_query($::form, $::form->get_standard_dbh, <<SQL, $self->{id});
193
    DELETE FROM letter WHERE id = ?
194
SQL
195

  
196
  $::form->get_standard_dbh->commit;
197

  
198
  $::lxdebug->leave_sub;
199
}
200

  
201
sub delete_drafts {
202
  $::lxdebug->enter_sub;
203

  
204
  my $self        = shift;
205
  my @draft_ids   = @_;
206

  
207
  my $form        = $main::form;
208
  my $myconfig = \%main::myconfig;
209
  my $dbh         = $form->get_standard_dbh($myconfig);
210

  
211

  
212
  return $main::lxdebug->leave_sub() unless (@draft_ids);
213

  
214
  my  $query = qq|DELETE FROM letter_draft WHERE id IN (| . join(", ", map { "?" } @draft_ids) . qq|)|;
215
  do_query($form, $dbh, $query, @draft_ids);
216

  
217
  $dbh->commit;
218

  
219
  $::lxdebug->leave_sub;
220
}
221

  
222

  
223
sub check_number {
224
  my $self = shift;
225

  
226
  return if $self->{letternumber}
227
         && $self->{id}
228
         && 1 == scalar __PACKAGE__->find(letternumber => $self->{letternumber});
229

  
230
  $self->{letternumber} = SL::TransNumber->new(type => 'letter', id => $self->{id}, number => $self->{letternumber})->create_unique;
231
}
232

  
233
sub check_name {
234
  my $self   = shift;
235
  my %params = @_;
236

  
237
  unless ($params{_name_selected}) {
238
    $::form->{$_} = $self->{$_} for qw(oldcustomer customer selectcustomer customer_id);
239

  
240
    if (::check_name('customer')) {
241
      $self->_set_customer_from($::form);
242
    }
243
  } else {
244
    $self->_set_customer_from($::form);
245
  }
246
}
247

  
248
sub _set_customer_from {
249
  my $self = shift;
250
  my $from = shift;
251

  
252
  $self->{$_} = $from->{$_} for qw(oldcustomer customer_id customer selectcustomer);
253

  
254
  $self;
255
}
256

  
257
sub check_date {
258
  my $self = shift;
259
  $self->{date} ||= $::form->current_date(\%::myconfig);
260
}
261

  
262
sub load {
263
  my $self   = shift;
264
  my $table  = 'letter';
265
  my $draft = $self->{draft};
266
  $table     = 'letter_draft' if $draft;
267

  
268

  
269
  return $self unless $self && $self->{id}; # no id? dont load.
270

  
271
  my %mappings      = _get_field_mappings();
272
  my $mapped_select = join ', ', '*', map { "$_ AS $mappings{$_}" } keys %mappings;
273

  
274
  my ($db_letter) = selectfirst_hashref_query($::form, $::form->get_standard_dbh, <<SQL, $self->{id});
275
    SELECT $mapped_select FROM $table WHERE id = ?
276
SQL
277

  
278
  $self->update_from($db_letter);
279
  $self->_resolve_customer;
280
  $self->set_greetings;
281
  $self->{draft_id} = delete $self->{id} if $draft;  # set draft if we have one
282

  
283
  return $self;
284
}
285

  
286
sub update_from {
287
  my $self   = shift;
288
  my $src    = shift;
289
  my %fields = $self->_get_fields;
290

  
291
  $fields{$_} = $src->{$_} for qw{customer_id customer selectcustomer oldcustomer}; # customer stuff
292

  
293
  $self->{$_} = $src->{$_} for keys %fields;
294

  
295
  return $self;
296
}
297

  
298
sub export_to {
299
  my $self = shift;
300
  my $form = shift;
301

  
302
  my %fields         = $self->_get_fields;
303
  my %field_mappings = $self->_get_field_mappings;
304

  
305
  for (keys %fields) {
306
    $form->{$_} =  _escaper($_)->( $self->{ $field_mappings{$_} || $_ } );
307
  }
308
}
309

  
310
sub language {
311
  my $self = shift;
312
  die 'not a setter' if @_;
313

  
314
  return unless $self->{cp_id};
315

  
316
  # umetec/cetaq only!
317
  # contacts have a custom variable called "mailing"
318
  # it contains either a language code or the string "No"
319

  
320
  my $custom_variables = CVar->get_custom_variables(
321
    module      => 'Contacts',
322
    name_prefix => 'cp',
323
    trans_id    => $self->{cp_id},
324
  );
325

  
326
  my ($mailing) = grep { $_->{name} eq 'Mailing' } @$custom_variables;
327

  
328
  return $mailing->{value} eq 'No' ? undef : $mailing->{value};
329
}
330

  
331
sub set_greetings {
332
  $::lxdebug->enter_sub;
333

  
334
  my $self = shift;
335
  return $::lxdebug->leave_sub if $self->{greeting};
336

  
337
  # automatically set greetings
338
  # greetings depend mainly on contact person
339
#   my $contact = $self->_get_contact;
340

  
341
  $self->{greeting} = $::locale->text('Dear Sir or Madam,');
342

  
343
  $::lxdebug->leave_sub;
344
}
345

  
346
sub _lastname_used {
347
  # wrapper for form lastname_used
348
  # sets customer to last used customer,
349
  # also used to initalize customer for new objects
350
  my $self = shift;
351

  
352
  return if $self->{customer_id};
353

  
354
  my $saved_form = save_form($::form);
355

  
356
  $::form->lastname_used($::form->get_standard_dbh, \%::myconfig, 'customer');
357

  
358
  $self->{customer_id} = $::form->{customer_id};
359
  $self->{customer}    = $::form->{customer};
360

  
361
  restore_form($saved_form);
362

  
363
  return $self;
364
}
365

  
366
sub _resolve_customer {
367
  # used if an object is created with only id.
368
  my $self = shift;
369

  
370
  return unless $self->{customer_id} && !$self->{customer};
371

  
372
#  my ($customer) = CT->find_by_id(cv => 'customer', id => $self->{customer_id});
373
#  my ($customer) = CT->find_by_id(cv => 'customer', id => $self->{customer_id});
374
  # SL/CVar.pm:        : $cfg->{type} eq 'customer'  ? (SL::DB::Manager::Customer->find_by(id => 1*$ref->{number_value}) || SL::DB::Customer->new)->name
375
  $self->{customer} = SL::DB::Manager::Customer->find_by(id => $self->{customer_id})->name; # || SL::DB::Customer->new)->name
376

  
377

  
378
}
379

  
380
sub _get_definition {
381
  $DEFINITION;
382
}
383

  
384
sub _get_field_mappings {
385
  return (
386
    vc_id => 'customer_id',
387
  );
388
}
389

  
390
sub _get_fields {
391
  my %fields = _get_definition() =~ /(\w+) \s+ \| \s+ (integer|text|timestamp|numeric|date)/xg;
392
}
393

  
394
sub _escaper {
395
  my $field_name = shift;
396
  my %fields     = __PACKAGE__->_get_fields;
397

  
398
  for ($fields{$field_name}) {
399
    return sub { conv_i(shift) } if /integer/;
400
    return sub { shift };
401
  }
402
}
403

  
404
1;
SL/TransNumber.pm
14 14
 scalar => [ qw(type id number save dbh dbh_provided business_id) ],
15 15
);
16 16

  
17
my @SUPPORTED_TYPES = qw(invoice credit_note customer vendor sales_delivery_order purchase_delivery_order sales_order purchase_order sales_quotation request_quotation part service assembly);
17
my @SUPPORTED_TYPES = qw(invoice credit_note customer vendor sales_delivery_order purchase_delivery_order sales_order purchase_order sales_quotation request_quotation part service assembly letter);
18 18

  
19 19
sub new {
20 20
  my $class = shift;
......
71 71
    $filters{numberfield}   = $type eq 'service' ? 'servicenumber' : 'articlenumber';
72 72
    $filters{numberfield}   = $type eq 'assembly' ? 'assemblynumber' : $filters{numberfield};
73 73
    $filters{table}         = "parts";
74
  } elsif ($type =~ /letter/) {
75
    $filters{trans_number}  = "letternumber";
76
    $filters{numberfield}   = "letternumber";
77
    $filters{table}         = "letter";
74 78
  }
75 79

  
76 80
  return %filters;
bin/mozilla/letter.pl
1
#=====================================================================
2
# LX-Office ERP
3
# Copyright (C) 2008
4
# Based on SQL-Ledger Version 2.1.9
5
# Web http://www.lx-office.org
6
#
7
#=====================================================================
8
#
9
# Letter module
10
#
11
#======================================================================
12

  
13
use strict;
14
use POSIX qw(strftime);
15

  
16
use SL::GenericTranslations;
17
use SL::ReportGenerator;
18
use SL::Letter;
19
use SL::CT;
20
use SL::DB::Contact;
21
use SL::DB::Default;
22
use SL::Helper::CreatePDF;
23
use SL::Helper::Flash;
24
require "bin/mozilla/reportgenerator.pl";
25
require "bin/mozilla/io.pl";
26
require "bin/mozilla/arap.pl";
27

  
28
use constant TEXT_CREATED_FOR_VALUES => (qw(presskit fax letter));
29
use constant PAGE_CREATED_FOR_VALUES => (qw(sketch 1 2));
30

  
31
our ($form, %myconfig, $locale, $lxdebug);
32

  
33
# parserhappy(R)
34
# $locale->text('Presskit')
35
# $locale->text('Sketch')
36
# $locale->text('Fax')
37
# $locale->text('Letter')
38

  
39
sub add {
40
  $::lxdebug->enter_sub;
41

  
42
  $main::auth->assert('sales_letter_edit');
43
  my %params = @_;
44

  
45
  return $main::lxdebug->leave_sub if load_letter_draft();
46

  
47
  my $letter = SL::Letter->new(%params);
48

  
49
  if (my $cp_id = delete $::form->{contact_id}) {
50
    my $contact = SL::DB::Manager::Contact->find_by(cp_id => $cp_id);
51
    $letter->{cp_id}     = $contact->cp_id;
52
    $letter->{vc_id}     = $contact->cp_cv_id;
53
    $letter->{greeting}  = GenericTranslations->get(
54
      translation_type => 'greetings::' . ($contact->{cp_gender} eq 'f' ? 'female' : 'male'),
55
      language_id      => $contact->language_id,
56
      allow_fallback   => 1
57
    );
58
    $params{language_id} = $contact->language_id;
59
  }
60

  
61
  $letter->check_date;
62

  
63
  _display(
64
    letter      => $letter,
65
    title       => $locale->text('Add Letter'),
66
    language_id => $params{language_id},
67
  );
68

  
69
  $::lxdebug->leave_sub;
70
}
71

  
72
sub edit {
73
  $::lxdebug->enter_sub;
74

  
75
  $main::auth->assert('sales_letter_edit');
76
  add() unless ($form->{id});
77

  
78
  my $letter = SL::Letter->new( id => $form->{id}, draft => $form->{draft} )->load;
79

  
80
  add() unless $letter && ($letter->{id} || $letter->{draft_id});
81

  
82
  _display(
83
    letter => $letter,
84
    title  => $locale->text('Edit Letter'),
85
  );
86

  
87
  $::lxdebug->leave_sub;
88
}
89

  
90
sub save {
91
  $::lxdebug->enter_sub;
92

  
93
  $main::auth->assert('sales_letter_edit');
94
  my %params = @_;
95

  
96

  
97
  $::form->error(t8('The subject is missing.')) unless $form->{letter}->{subject};
98
  $::form->error(t8('The body is missing.')) unless $form->{letter}->{body};
99
  $::form->error(t8('The employee is missing.')) unless $form->{letter}->{employee_id};
100

  
101
  my $letter = _update();
102

  
103
  $letter->check_number;
104
  $letter->save;
105

  
106
  $form->{SAVED_MESSAGE} = $locale->text('Letter saved!');
107

  
108
  _display(
109
    letter => $letter,
110
  );
111

  
112
  $::lxdebug->leave_sub;
113
}
114

  
115
sub save_letter_draft {
116
  $::lxdebug->enter_sub;
117

  
118
  $main::auth->assert('sales_letter_edit');
119

  
120
  $::form->error(t8('The subject is missing.')) unless $form->{letter}->{subject};
121
  $::form->error(t8('The body is missing.')) unless $form->{letter}->{body};
122
  $::form->error(t8('The employee is missing.')) unless $form->{letter}->{employee_id};
123
  $::form->error(t8('Already as letter saved.')) if $form->{letter}->{letternumber};
124

  
125
  my $letter_draft = _update();
126
  $letter_draft->{draft_id} = delete $letter_draft->{id}; # if we have one
127
  $letter_draft->save(draft => '1');
128
  $letter_draft->{vergiss_mich_nicht} = 'nicht vergessen';
129
  $form->{SAVED_MESSAGE} = $locale->text('Draft for this Letter saved!');
130

  
131
  _display(
132
    letter => $letter_draft,
133
  );
134

  
135
  $::lxdebug->leave_sub;
136
}
137

  
138
sub delete {
139
  $main::lxdebug->enter_sub();
140

  
141
  $main::auth->assert('sales_letter_edit');
142
  # NYI
143
  $form->{SAVED_MESSAGE} = $locale->text('Not yet implemented!');
144
  _display();
145

  
146
  $main::lxdebug->leave_sub();
147
}
148

  
149
sub delete_letter_drafts {
150
  $main::lxdebug->enter_sub();
151

  
152
  $main::auth->assert('sales_letter_edit');
153

  
154
  my @ids;
155
  foreach (keys %{$form}) {
156
    push @ids, $1 if (/^checked_(.*)/ && $form->{$_});
157
  }
158

  
159
  SL::Letter->delete_drafts(@ids) if (@ids); #->{id});
160

  
161
  add();
162

  
163
  $main::lxdebug->leave_sub();
164
}
165

  
166
sub _display {
167
  $main::lxdebug->enter_sub();
168

  
169
  $main::auth->assert('sales_letter_edit');
170
  my %params = @_;
171

  
172
  my $letter = $params{letter};
173

  
174
  my %TMPL_VAR;
175

  
176
  $form->{type}             = 'letter';   # needed for print_options
177
  $form->{vc}               = 'customer'; # needs to be for _get_contacts...
178
  $form->{"$form->{vc}_id"} ||= $letter->{customer_id};
179
  $form->{jsscript}         = 1;
180
  $form->{javascript}       =
181
     qq|<script type="text/javascript" src="js/customer_or_vendor_selection.js"></script>
182
        <script type="text/javascript" src="js/edit_part_window.js"></script>|;
183

  
184
  $form->get_lists("contacts"      => "ALL_CONTACTS",
185
  "employees"     => "ALL_EMPLOYEES",
186
                   "salesmen"      => "ALL_SALESMEN",
187
                   "departments"   => "ALL_DEPARTMENTS",
188
                   "languages"     => "languages",
189
                   "customers"     => { key   => "ALL_CUSTOMERS",
190
                                        limit => $myconfig{vclimit} + 1 },
191
                   "vc"            => 'customer',
192
                   );
193

  
194
  $TMPL_VAR{vc_keys}       = sub { "$_[0]->{name}--$_[0]->{id}" };
195
  $TMPL_VAR{vc_select}     = "customer_or_vendor_selection_window('letter.customer', '', 0, 0)";
196
  $TMPL_VAR{ct_labels}     = sub { ($_[0]->{cp_greeting} ? "$_[0]->{cp_greeting} " : '') .  $_[0]->{cp_name} .  ($_[0]->{cp_givenname} ? ", $_[0]->{cp_givenname}" : '') };
197
  $TMPL_VAR{TCF}           = [ map { key => $_, value => $locale->text(ucfirst $_) }, TEXT_CREATED_FOR_VALUES() ];
198
  $TMPL_VAR{PCF}           = [ map { key => $_, value => $locale->text(ucfirst $_) }, PAGE_CREATED_FOR_VALUES() ];
199

  
200
  $form->header();
201

  
202
  $form->{language_id} ||= $params{language_id};
203

  
204
  print $form->parse_html_template('letter/edit', {
205
    %params,
206
    %TMPL_VAR,
207
    letter        => $letter,
208
    print_options => print_options(inline => 1),
209
  });
210

  
211
  $main::lxdebug->leave_sub();
212
}
213

  
214
sub search {
215
  $lxdebug->enter_sub();
216

  
217
  $main::auth->assert('sales_letter_report');
218

  
219
  $form->get_lists("employees" => "EMPLOYEES",
220
                   "salesmen"  => "SALESMEN",
221
                   "customers" => "ALL_CUSTOMERS");
222

  
223
  $form->{jsscript} = 1;
224
  $form->{title}    = $locale->text('Letters');
225

  
226
  $form->header();
227
  print $form->parse_html_template('letter/search');
228

  
229
  $lxdebug->leave_sub();
230
}
231

  
232
sub report {
233
  $lxdebug->enter_sub();
234

  
235
  $main::auth->assert('sales_letter_report');
236

  
237
  my %params = @_;
238

  
239
  my @report_params = qw(letternumber subject body contact date_from date_to cp_id);
240

  
241
  if ($form->{selectcustomer}) {
242
    push @report_params, 'customer_id';
243
    $form->{customer_id} = $form->{customer};
244
  } else {
245
    push @report_params, 'customer';
246
  }
247

  
248
  report_generator_set_default_sort('date', 1);
249

  
250
  %params = (%params, map { $_ => $form->{$_} } @report_params);
251

  
252
  my @letters       = SL::Letter->find(%params);
253

  
254
  $form->{rowcount} = @letters;
255
  $form->{title}    = $locale->text('Letters');
256

  
257
  my %column_defs = (
258
    'date'                  => { 'text' => $locale->text('Date'), },
259
    'subject'               => { 'text' => $locale->text('Subject'), },
260
    'letternumber'          => { 'text' => $locale->text('Letternumber'), },
261
    'customer'              => { 'text' => $locale->text('Customer') },
262
    'contact'               => { 'text' => $locale->text('Contact') },
263
    'date'                  => { 'text' => $locale->text('Date') },
264
  );
265

  
266
  my @columns = qw(date subject letternumber customer contact date);
267
  my $href    = build_std_url('action=report', grep { $form->{$_} } @report_params);
268

  
269
  my @sortable_columns = qw(date subject letternumber customer contact date);
270

  
271
  foreach my $name (@sortable_columns) {
272
    my $sortdir                 = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
273
    $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
274
  }
275

  
276
  my @options;
277

  
278
  # option line
279

  
280
  push @options, $locale->text('Subject')                  . " : $form->{subject}"   if ($form->{subject});
281
  push @options, $locale->text('Body')                     . " : $form->{body}"      if ($form->{body});
282

  
283
  my @hidden_report_params = map { +{ 'key' => $_, 'value' => $form->{$_} } } @report_params;
284

  
285
  my $report = SL::ReportGenerator->new(\%myconfig, $form, 'std_column_visibility' => 1);
286

  
287
  $report->set_columns(%column_defs);
288
  $report->set_column_order(@columns);
289

  
290
  $report->set_export_options('report', @report_params);
291

  
292
  $report->set_sort_indicator($form->{sort}, $form->{sortdir});
293

  
294
  $report->set_options('raw_top_info_text'    => $form->parse_html_template('letter/report_top',    { 'OPTIONS' => \@options }),
295
                       'raw_bottom_info_text' => $form->parse_html_template('letter/report_bottom', { 'HIDDEN'  => \@hidden_report_params }),
296
                       'output_format'        => 'HTML',
297
                       'title'                => $form->{title},
298
                       'attachment_basename'  => $locale->text('letters_list') . strftime('_%Y%m%d', localtime time),
299
    );
300
  $report->set_options_from_form();
301

  
302
  my $idx      = 0;
303
  my $callback = build_std_url('action=report', grep { $form->{$_} } @report_params);
304
  my $edit_url = build_std_url('action=edit', 'callback=' . E($callback));
305

  
306
  foreach my $l (@letters) {
307
    $idx++;
308

  
309
    my $row = { map { $_ => { 'data' => $l->{$_} } } keys %{ $l } };
310

  
311
    $row->{subject}->{link}      = $edit_url . '&id=' . Q($l->{id});
312
    $row->{letternumber}->{link} = $edit_url . '&id=' . Q($l->{id});
313

  
314
    $report->add_data($row);
315
  }
316

  
317
  $report->generate_with_headers();
318

  
319
  $lxdebug->leave_sub();
320
}
321

  
322
sub print_letter {
323
  $lxdebug->enter_sub();
324

  
325
  $main::auth->assert('sales_letter_edit');
326

  
327
  my ($old_form) = @_;
328

  
329
  my $display_form = $form->{display_form} || "display_form";
330
  my $letter       = _update();
331

  
332
  $letter->export_to($form);
333
  $form->{formname} = "letter";
334
  $form->{format} = "pdf";
335

  
336
  my $language_saved      = $form->{language_id};
337
  my $greeting_saved      = $form->{greeting};
338
  my $cp_id_saved         = $form->{cp_id};
339

  
340
  call_sub("customer_details");
341

  
342
  if (!$cp_id_saved) {
343
    # No contact was selected. Delete all contact variables because
344
    # IS->customer_details() and IR->vendor_details() get the default
345
    # contact anyway.
346
    map({ delete($form->{$_}); } grep(/^cp_/, keys(%{ $form })));
347
  }
348

  
349
  $form->{greeting} = $greeting_saved;
350
  $form->{language_id} = $language_saved;
351

  
352
  if ($form->{cp_id}) {
353
    CT->get_contact(\%myconfig, $form);
354
  }
355

  
356
  $form->{cp_contact_formal} = ($form->{cp_greeting} ? "$form->{cp_greeting} " : '') . ($form->{cp_givenname} ? "$form->{cp_givenname} " : '') . $form->{cp_name};
357

  
358
  $form->get_employee_data('prefix' => 'employee', 'id' => $letter->{employee_id});
359
  $form->get_employee_data('prefix' => 'salesman', 'id' => $letter->{salesman_id});
360

  
361
  my %create_params = (
362
    template  => scalar(SL::Helper::CreatePDF->find_template(
363
      name        => 'letter',
364
      printer_id  => $::form->{printer_id},
365
      language_id => $::form->{language_id},
366
      formname    => 'letter',
367
      format      => 'pdf',
368
    )),
369
    variables => $::form,
370
    return    => 'file_name',
371
  );
372
  my $pdf_file_name;
373
  eval {
374
    $pdf_file_name = SL::Helper::CreatePDF->create_pdf(%create_params);
375

  
376
    if ( $::form->{media} eq 'email') {
377
      my $mail             = Mailer->new;
378
      my $signature        = $::myconfig{signature};
379
      $mail->{$_}          = $::form->{$_}               for qw(cc subject message bcc to);
380
      $mail->{from}        = qq|"$::myconfig{name}" <$::myconfig{email}>|;
381
      $mail->{fileid}      = time() . '.' . $$ . '.';
382
      $mail->{attachments} =  [{ "filename" => $pdf_file_name,
383
                                 "name"     => $::form->{attachment_name} }];
384
      $mail->{message}    .=  "\n-- \n$signature";
385
      $mail->{message}     =~ s/\r//g;
386

  
387
      my $err = $mail->send;
388
# TODO
389
#       $self
390
#           ->js
391
#           ->flash($err?'error':'info',
392
#                   $err?t8('A mail error occurred: #1', $err):
393
#                        t8('The document have been sent to \'#1\'.', $mail->{to}))
394
#           ->render($self);
395
      return $err?0:1;
396
    }
397

  
398
    if (!$::form->{printer_id}) {
399
      my $file = IO::File->new($pdf_file_name, 'r') || croak("Cannot open file '$pdf_file_name'");
400
      my $size = -s $pdf_file_name;
401
      my $content_type    =  'application/pdf';
402
      my $attachment_name =  $::form->generate_attachment_filename;
403
      $attachment_name    =~ s:.*//::g;
404

  
405
      print $::form->create_http_response(content_type        => $content_type,
406
                                          content_disposition => 'attachment; filename="' . $attachment_name . '"',
407
                                          content_length      => $size);
408

  
409
      $::locale->with_raw_io(\*STDOUT, sub { print while <$file> });
410
      $file->close;
411
      unlink $pdf_file_name;
412
      return 1;
413
    }
414

  
415
    my $printer = SL::DB::Printer->new(id => $::form->{printer_id})->load;
416
    my $command = SL::Template::create(type => 'ShellCommand', form => Form->new(''))->parse($printer->printer_command);
417

  
418
    open my $out, '|-', $command or die $!;
419
    binmode $out;
420
    print $out scalar(read_file($pdf_file_name));
421
    close $out;
422

  
423
    flash_later('info', t8('The documents have been sent to the printer \'#1\'.', $printer->printer_description));
424
    my $callback = build_std_url('letter.pl', 'action=edit', 'id=' . $letter->{id}, 'printer_id');
425
    $::form->redirect;
426
    1;
427
  } or do {
428
    unlink $pdf_file_name;
429
    $::form->error(t8("Creating the PDF failed:") . " " . $@);
430
  };
431

  
432
  $lxdebug->leave_sub();
433
}
434

  
435
sub update {
436
  $::lxdebug->enter_sub;
437

  
438
  $main::auth->assert('sales_letter_edit');
439

  
440
  my $name_selected = shift;
441

  
442
  _display(
443
    letter => _update(
444
      _name_selected => $name_selected,
445
    ),
446
  );
447

  
448
  $::lxdebug->leave_sub;
449
}
450

  
451
sub _update {
452
  $::lxdebug->enter_sub;
453

  
454
  $main::auth->assert('sales_letter_edit');
455

  
456
  my %params = @_;
457

  
458
  my $from_letter = $::form->{letter};
459

  
460
  my $letter      = SL::Letter->new( id => $from_letter->{id} )
461
                              ->load
462
                              ->update_from($from_letter);
463

  
464
  $letter->check_name(%params);
465
  $letter->check_date;
466
  $letter->set_greetings;
467

  
468
  $::lxdebug->leave_sub;
469

  
470
  return $letter;
471
}
472

  
473
sub letter_tab {
474
  $::lxdebug->enter_sub;
475

  
476
  $main::auth->assert('sales_letter_edit');
477

  
478
  my @report_params = qw(letternumber subject contact date);
479

  
480
  my @letters       = SL::Letter->find(map { $_ => $form->{$_} } @report_params);
481

  
482
  $::lxdebug->leave_sub;
483
}
484

  
485
sub e_mail {
486
  $::lxdebug->enter_sub;
487

  
488
  $main::auth->assert('sales_letter_edit');
489
  my $letter = _update();
490

  
491
  $letter->check_number;
492
  $letter->save;
493

  
494
  $letter->export_to($::form);
495

  
496
  $::form->{id} = $letter->{id};
497
  edit_e_mail();
498

  
499
  $::lxdebug->leave_sub;
500
}
501

  
502
sub dispatcher {
503
  $main::lxdebug->enter_sub();
504
  # dispatch drafts
505
  my $locale   = $main::locale;
506

  
507

  
508
  if ($form->{letter_draft_action} eq $locale->text("Skip")) {
509
    $form->{DONT_LOAD_DRAFT} = 1;
510
    add();
511
    return 1;
512
  } elsif ($form->{letter_draft_action} eq $locale->text("Delete drafts")) {
513
    delete_letter_drafts();
514
    return 1;
515
  }
516

  
517
  foreach my $action (qw(e_mail print save update save_letter_draft)) {
518
    if ($::form->{"action_${action}"}) {
519
      $::form->{dispatched_action} = $action;
520
      call_sub($action);
521
      return;
522
    }
523
  }
524

  
525
  $::form->error($::locale->text('No action defined.'));
526
  $::lxdebug->leave_sub;
527
}
528

  
529
sub continue {
530
  call_sub($form->{nextsub});
531
}
532

  
533

  
534
sub load_letter_draft {
535
  $lxdebug->enter_sub();
536

  
537
  $main::auth->assert('sales_letter_edit');
538
 $main::lxdebug->leave_sub() and return 0 if ($form->{DONT_LOAD_DRAFT});
539
 $form->{title}    = $locale->text('Letter Draft');
540
 $form->{script}   = 'letter.pl';
541

  
542
  my @letter_drafts = SL::Letter->find(draft => 1);
543

  
544
  return unless @letter_drafts;
545
  $form->header();
546
  print $form->parse_html_template('letter/load_drafts', { LETTER_DRAFTS => \@letter_drafts });
547

  
548
  return 1;
549
  $lxdebug->leave_sub();
550
}
551

  
552
1;
locale/de/all
53 53
  'A directory with the name for the new print templates exists already.' => 'Ein Verzeichnis mit dem selben Namen wie die neuen Druckvorlagen existiert bereits.',
54 54
  'A lot of the usability of kivitendo has been enhanced with javascript. Although it is currently possible to use every aspect of kivitendo without javascript, we strongly recommend it. In a future version this may change and javascript may be necessary to access advanced features.' => 'Die Bedienung von kivitendo wurde an vielen Stellen mit Javascript verbessert. Obwohl es derzeit möglich ist, jeden Aspekt von kivitendo auch ohne Javascript zu benutzen, empfehlen wir es. In einer zukünftigen Version wird Javascript eventuell notwendig sein um weitergehende Features zu benutzen.',
55 55
  'A lower-case character is required.' => 'Ein Kleinbuchstabe ist vorgeschrieben.',
56
  'A mail error occurred: #1'   => 'Ein ',
56 57
  'A special character is required (valid characters: #1).' => 'Ein Sonderzeichen ist vorgeschrieben (gültige Zeichen: #1).',
57 58
  'A transaction description is required.' => 'Die Vorgangsbezeichnung muss eingegeben werden.',
58 59
  'A unit with this name does already exist.' => 'Eine Einheit mit diesem Namen existiert bereits.',
......
146 147
  'Add Group'                   => 'Warengruppe erfassen',
147 148
  'Add Language'                => 'Sprache hinzufügen',
148 149
  'Add Lead'                    => 'Kundenquelle erfassen',
150
  'Add Letter'                  => 'Brief hinzufügen',
149 151
  'Add Machine'                 => 'Maschine erfassen',
150 152
  'Add Part'                    => 'Ware erfassen',
151 153
  'Add Price Factor'            => 'Preisfaktor erfassen',
......
170 172
  'Add User Group'              => 'Neue Benutzergruppe',
171 173
  'Add Vendor'                  => 'Lieferant erfassen',
172 174
  'Add Vendor Invoice'          => 'Einkaufsrechnung erfassen',
175
  'Add Vendor Letter'           => '',
173 176
  'Add Warehouse'               => 'Lager erfassen',
174 177
  'Add and edit units'          => 'Einheiten erfassen und bearbeiten',
175 178
  'Add bank account'            => 'Bankkonto erfassen',
......
223 226
  'Allow direct creation of new purchase delivery orders' => 'Direktes Anlegen neuer Einkaufslieferscheine zulassen',
224 227
  'Allow direct creation of new purchase invoices' => 'Direktes Anlegen neuer Einkaufsrechnungen zulassen',
225 228
  'Allow the following users access to my follow-ups:' => 'Erlaube den folgenden Benutzern Zugriff auf meine Wiedervorlagen:',
229
  'Already as letter saved.'    => 'Wurde schon als Brief gespeichert.',
226 230
  'Alternatively you can create a new part which will then be selected.' => 'Sie k&ouml;nnen auch einen neuen Artikel anlegen, der dann automatisch ausgew&auml;hlt wird.',
227 231
  'Amended Advance Turnover Tax Return' => 'Berichtigte Anmeldung',
228 232
  'Amended Advance Turnover Tax Return (Nr. 10)' => 'Ist dies eine berichtigte Anmeldung? (Nr. 10/Zeile 15 Steuererklärung)',
......
659 663
  'Created by'                  => 'Erstellt von',
660 664
  'Created for'                 => 'Erstellt f&uuml;r',
661 665
  'Created on'                  => 'Erstellt am',
666
  'Creating the PDF failed:'    => 'PDF-Erzeugung fehlgeschlagen:',
662 667
  'Creation Date'               => 'Erstelldatum',
663 668
  'Credit'                      => 'Haben',
664 669
  'Credit (one letter abbreviation)' => 'H',
......
757 762
  'Datevautomatik'              => 'Datev-Automatik',
758 763
  'Datum von'                   => 'Datum von',
759 764
  'Deactivate by default'       => 'Deaktiviert als Voreinstellung',
765
  'Dear Sir or Madam,'          => 'Sehr geehrte Damen und Herren,',
760 766
  'Debit'                       => 'Soll',
761 767
  'Debit (one letter abbreviation)' => 'S',
762 768
  'Debit Account'               => 'Sollkonto',
......
771 777
  'Default (no language selected)' => 'Standard (keine Sprache ausgewählt)',
772 778
  'Default Accounts'            => 'Standardkonten',
773 779
  'Default Bin'                 => 'Standard-Lagerplatz',
774
  'Default Bin with ignoring onhand' => 'Standard-Lagerplatz für Lagerbewegungen ohne Überprüfung auf verfügbare Menge ',
780
  'Default Bin with ignoring onhand' => 'Standard-Lagerplatz für Auslagern ohne Prüfung auf Bestand',
775 781
  'Default Client (unconfigured)' => 'Standardmandant (unkonfiguriert)',
776 782
  'Default Customer/Vendor Language' => 'Standard-Kunden-/Lieferantensprache',
777 783
  'Default Transfer'            => 'Ein- / Auslagern über Standardlagerplätze',
......
780 786
  'Default Transfer with Master Bin' => 'Standardlagerplatz für Lagerbewegungen verwenden, falls keiner in Stammdaten definiert',
781 787
  'Default Transfer with services' => 'Ein- /Auslagern von Dienstleistungen über Standard-Lagerplatz',
782 788
  'Default Warehouse'           => 'Standard-Lager',
783
  'Default Warehouse with ignoring on hand' => 'Standardlager für Auslagern ohne Prüfung auf Bestand',
789
  'Default Warehouse with ignoring onhand' => 'Standard-Lager für Auslagern ohne Prüfung auf Bestand',
784 790
  'Default article for converting into quotations and orders' => 'Standardartikel für Konvertierung von Pflichtenheften in Angebote und Aufträge',
785 791
  'Default buchungsgruppe'      => 'Standardbuchungsgruppe',
786 792
  'Default client'              => 'Standardmandant',
......
791 797
  'Default printer'             => 'Standarddrucker',
792 798
  'Default taxzone'             => 'Standardsteuerzone',
793 799
  'Default template format'     => 'Standardvorlagenformat',
800
  'Default transfer delivery order' => 'Standard-Auslagern über Lieferschein',
801
  'Default transfer invoice'    => 'Standard-Auslagern über Rechnung',
794 802
  'Default transport article number' => 'Standard Versand / Transport-Erinnerungs-Artikel',
795 803
  'Default unit'                => 'Standardeinheit',
796 804
  'Default value'               => 'Standardwert',
......
907 915
  'Download SEPA XML export file' => 'SEPA-XML-Exportdatei herunterladen',
908 916
  'Download picture'            => 'Bild herunterladen',
909 917
  'Download sample file'        => 'Beispieldatei herunterladen',
918
  'Draft for this Letter saved!' => 'Briefentwurf gespeichert!',
910 919
  'Draft saved.'                => 'Entwurf gespeichert.',
911 920
  'Drawing'                     => 'Zeichnung',
912 921
  'Dropdown Limit'              => 'Auswahllistenbegrenzung',
......
965 974
  'Edit Group'                  => 'Warengruppe editieren',
966 975
  'Edit Language'               => 'Sprache bearbeiten',
967 976
  'Edit Lead'                   => 'Kundenquelle bearbeiten',
977
  'Edit Letter'                 => 'Brief bearbeiten',
968 978
  'Edit Part'                   => 'Ware bearbeiten',
969 979
  'Edit Preferences for #1'     => 'Einstellungen von #1 bearbeiten',
970 980
  'Edit Price Factor'           => 'Preisfaktor bearbeiten',
......
1454 1464
  'Lead'                        => 'Kundenquelle',
1455 1465
  'Leads'                       => 'Kundenquellen',
1456 1466
  'Left'                        => 'Links',
1467
  'Letter'                      => 'Brief',
1468
  'Letter Draft'                => 'Briefentwurf',
1469
  'Letter saved!'               => 'Brief gespeichert!',
1470
  'Letternumber'                => 'Briefnummer',
1471
  'Letters'                     => 'Briefe',
1457 1472
  'Liability'                   => 'Passiva/Mittelherkunft',
1458 1473
  'Limit part selection'        => 'Artikelauswahl eingrenzen',
1459 1474
  'Line Total'                  => 'Zeilensumme',
......
1480 1495
  'List of tax zones'           => 'Liste der Steuerzonen',
1481 1496
  'List open SEPA exports'      => 'Noch nicht ausgeführte SEPA-Exporte anzeigen',
1482 1497
  'Load draft'                  => 'Entwurf laden',
1498
  'Load letter draft'           => 'Briefentwurf laden',
1483 1499
  'Load profile'                => 'Profil laden',
1484 1500
  'Loading...'                  => 'Wird geladen...',
1485 1501
  'Local Tax Office Preferences' => 'Angaben zum Finanzamt',
......
1674 1690
  'Not delivered'               => 'Nicht geliefert',
1675 1691
  'Not done yet'                => 'Noch nicht fertig',
1676 1692
  'Not obsolete'                => 'Gültig',
1693
  'Not yet implemented!'        => 'Noch nicht implementiert!',
1677 1694
  'Note'                        => 'Hinweis',
1678 1695
  'Note: Taxkeys must have a "valid from" date, and will not behave correctly without.' => 'Hinweis: Steuerschlüssel sind fehlerhaft ohne "Gültig ab" Datum',
1679 1696
  'Notes'                       => 'Bemerkungen',
......
1902 1919
  'Prepare bank collection via SEPA XML' => 'Einzug via SEPA XML vorbereiten',
1903 1920
  'Prepare bank transfer via SEPA XML' => 'Überweisung via SEPA XML vorbereiten',
1904 1921
  'Prepayment'                  => 'Vorauszahlung',
1922
  'Presskit'                    => 'Pressemappe',
1905 1923
  'Preview'                     => 'Druckvorschau',
1906 1924
  'Preview Mode'                => 'Vorschaumodus',
1907 1925
  'Previous transdate text'     => 'wurde gespeichert am',
......
2322 2340
  'Since bin is not enforced in the parts data, please specify a bin where goods without a specified bin will be put.' => 'Da Lagerpl&auml;tze kein Pflichtfeld sind, geben Sie bitte einen Lagerplatz an, in dem Waren ohne spezifizierten Lagerplatz eingelagert werden sollen.',
2323 2341
  'Single quotes'               => 'Einfache Anführungszeichen',
2324 2342
  'Single values in item mode, cumulated values in invoice mode' => 'Einzelwerte im Artikelmodus, kumulierte Werte im Rechnungsmodus',
2343
  'Sketch'                      => 'Skizze',
2325 2344
  'Skip'                        => 'Überspringen',
2326 2345
  'Skip entry'                  => 'Eintrag überspringen',
2327 2346
  'Skipping due to existing entry in database' => 'Wegen existierendem Eintrag mit selber Nummer übersprungen',
......
2500 2519
  'The base unit does not exist.' => 'Die Basiseinheit existiert nicht.',
2501 2520
  'The base unit relations must not contain loops (e.g. by saying that unit A\'s base unit is B, B\'s base unit is C and C\'s base unit is A) in row %d.' => 'Die Beziehungen der Einheiten d&uuml;rfen keine Schleifen beinhalten (z.B. wenn gesagt wird, dass Einheit As Basiseinheit B, Bs Basiseinheit C und Cs Basiseinheit A ist) in Zeile %d.',
2502 2521
  'The basic client tables have not been created for this client\'s database yet.' => 'Die grundlegenden Mandantentabellen wurden in der für diesen Mandanten konfigurierten Datenbank noch nicht angelegt.',
2522
  'The body is missing.'        => 'Der Text fehlt',
2503 2523
  'The buchungsgruppe has been deleted.' => 'Die Buchungsgruppe wurde gelöscht.',
2504 2524
  'The buchungsgruppe is in use and cannot be deleted.' => 'Die Buchungsgruppe wird benutzt und kann daher nicht gelöscht werden.',
2505 2525
  'The business has been created.' => 'Der Kunden-/Lieferantentyp wurde erfasst.',
......
2542 2562
  'The database user is missing.' => 'Der Datenbankbenutzer fehlt.',
2543 2563
  'The dataset #1 has been created.' => 'Die Datenbank #1 wurde angelegt.',
2544 2564
  'The dataset #1 has been deleted.' => 'Die Datenbank #1 wurde gelöscht.',
2565
  'The date is missing.'        => 'Das Datum fehlt',
2545 2566
  'The deductible amount'       => 'Der abziehbare Skontobetrag',
2546 2567
  'The default delivery plan only checks if all delivery orders have been created not if the goods are transferred. This feature will check if all the goods are transferred. Caveat: Only the state of the delivery orders are checked not partial transferred delivery orders (in technical terms: the table inventory is not checked' => 'Standardmässig wird beim Lieferplan überprüft, ob es eine vollständige Liefermenge über alle Lieferscheine gibt. Dies ist dann die Statusbedingung für geliefert oder nicht geliefert. Mit dieser Erweiterung wird geprüft ob die Lieferbelege auch wirklich ausgelagert sind oder nicht. Teilausgelagerte Lieferscheine werden allerdings nicht berücksichtigt (Technischer Hintergrund: Keine Überprüfung der Lagertabelle inventory).',
2547 2568
  'The default value depends on the variable type:' => 'Die Bedeutung des Standardwertes h&auml;ngt vom Variablentypen ab:',
......
2562 2583
  'The discount must be less than 100%.' => 'Der Rabatt muss kleiner als 100% sein.',
2563 2584
  'The discount must not be negative.' => 'Der Rabatt darf nicht negativ sein.',
2564 2585
  'The discounted amount will be shown in documents.' => 'Der Rabattbetrag wird in Belegen ausgewiesen.',
2586
  'The document have been sent to \'#1\'.' => 'Das Dokument wurde an \'#1\' geschickt.',
2587
  'The documents have been sent to the printer \'#1\'.' => 'Die Dokumente wurden an den Drucker \'#1\' geschickt.',
2565 2588
  'The dunning process started' => 'Der Mahnprozess ist gestartet.',
2566 2589
  'The dunnings have been printed.' => 'Die Mahnung(en) wurden gedruckt.',
2590
  'The employee is missing.'    => 'Der Bearbeiter fehlt.',
2567 2591
  'The end date is the last day for which invoices will possibly be created.' => 'Das Enddatum ist das letztmögliche Datum, an dem eine Rechnung erzeugt wird.',
2568 2592
  'The execution schedule is invalid.' => 'Der Ausführungszeitplan ist ungültig.',
2569 2593
  'The execution type is invalid.' => 'Der Ausführungstyp ist ungültig.',
......
2576 2600
  'The following currencies have been used, but they are not defined:' => 'Die folgenden Währungen wurden benutzt, sind aber nicht ordnungsgemäß in der Datenbank eingetragen:',
2577 2601
  'The following drafts have been saved and can be loaded.' => 'Die folgenden Entw&uuml;rfe wurden gespeichert und k&ouml;nnen geladen werden.',
2578 2602
  'The following groups are valid for this client' => 'Die folgenden Gruppen sind für diesen Mandanten gültig',
2603
  'The following letter drafts have been saved and can be loaded.' => 'Die folgenden Briefentwürfe sind vorhanden und können geladen werden.',
2579 2604
  'The following list has been generated automatically from existing users collapsing users with identical settings into a single entry.' => 'Die folgende Liste wurde automatisch aus den im System vorhandenen Benutzern zusammengestellt, wobei identische Einstellungen zu einem Eintrag zusammengefasst wurden.',
2580 2605
  'The following old files whose settings have to be merged manually into the new configuration file "config/kivitendo.conf" still exist:' => 'Es existieren noch die folgenden alten Dateien, deren Einstellungen manuell in die neue Konfiguratsdatei "config/kivitendo.conf" migriert werden müssen:',
2581 2606
  'The following transaction contains wrong taxes:' => 'Die folgende Buchung enthält falsche Steuern:',
......
2776 2801
  'This has been changed in this version.' => 'Ab dieser Version ist dies nicht mehr so.',
2777 2802
  'This is a very critical problem.' => 'Dieses Problem ist sehr schwerwiegend.',
2778 2803
  'This is the client to be selected by default on the login screen.' => 'Dies ist derjenige Mandant, der im Loginbildschirm standardmäßig ausgewählt sein wird.',
2779
  'This is the default bin for ignoring onhand' => 'Standardlagerplatz für Auslagern ohne Bestandsprüfung',
2780 2804
  'This is the default bin for parts' => 'Standard-Lagerplatz für Stammdaten/Waren',
2805
  'This is the default warehouse for ignoring onhand' => 'Standardlager für Auslagern ohne Prüfung auf Bestand.',
2781 2806
  'This list is capped at 15 items to keep it fast. If you need a full list, please use reports.' => 'Diese Liste ist auf 15 Zeilen begrenzt. Wenn Sie eine vollständige Liste benötigen, erstellen Sie bitte einen Bericht.',
2782 2807
  'This means that the user has created an AP transaction and chosen a taxkey for sales taxes, or that he has created an AR transaction and chosen a taxkey for input taxes.' => 'Das bedeutet, dass ein Benutzer eine Kreditorenbuchung angelegt und in ihr einen Umsatzsteuer-Steuerschlüssel verwendet oder eine Debitorenbuchung mit Vorsteuer-Steuerschlüssel angelegt hat.',
2783 2808
  'This module can help you identify and correct such entries by analyzing the general ledger and presenting you likely solutions but also allowing you to fix problems yourself.' => 'Dieses Modul kann Ihnen helfen, problematische Einträge im Hauptbuch zu identifizieren und teilweise zu beheben. Dabei werden je nach Problem mögliche Lösungen aufgezeigt, wobei Sie die entscheiden können, welche Probleme automatisch gelöst werden sollen.',
......
2962 2987
  'Vendor Discount'             => 'Lieferantenrabatt',
2963 2988
  'Vendor Invoice'              => 'Einkaufsrechnung',
2964 2989
  'Vendor Invoices & AP Transactions' => 'Einkaufsrechnungen & Kreditorenbuchungen',
2990
  'Vendor Letters'              => '',
2965 2991
  'Vendor Name'                 => 'Lieferantenname',
2966 2992
  'Vendor Number'               => 'Lieferantennummer',
2967 2993
  'Vendor Order Number'         => 'Bestellnummer beim Lieferanten',
......
3064 3090
  'You have to grant users access to one or more clients.' => 'Benutzern muss dann Zugriff auf einzelne Mandanten gewährt werden.',
3065 3091
  'You have to specify a department.' => 'Sie müssen eine Abteilung wählen.',
3066 3092
  'You have to specify an execution date for each antry.' => 'Sie müssen für jeden zu buchenden Eintrag ein Ausführungsdatum angeben.',
3093
  'You must choose a user.'     => '',
3067 3094
  'You must chose a user.'      => 'Sie m&uuml;ssen einen Benutzer ausw&auml;hlen.',
3068 3095
  'You must enter a name for your new print templates.' => 'Sie müssen einen Namen für die neuen Druckvorlagen angeben.',
3069 3096
  'You must select existing print templates or create a new set.' => 'Sie müssen vorhandene Druckvorlagen auswählen oder einen neuen Satz anlegen.',
......
3071 3098
  'You\'re not editing a file.' => 'Sie bearbeiten momentan keine Datei.',
3072 3099
  'You\'ve already chosen the following limitations:' => 'Sie haben bereits die folgenden Einschr&auml;nkungen vorgenommen:',
3073 3100
  'Your PostgreSQL installationen does not use Unicode as its encoding. This is not supported anymore.' => 'Ihre PostgreSQL-Installation benutzt ein anderes Encoding als Unicode. Dies wird nicht mehr unterstützt.',
3101
  'Your Reference'              => 'Ihr Zeichen',
3074 3102
  'Your TODO list'              => 'Ihre Aufgabenliste',
3075 3103
  'Your account number'         => 'Ihre Kontonummer',
3076 3104
  'Your bank'                   => 'Der Name Ihrer Bank',
......
3110 3138
  'bis'                         => 'bis',
3111 3139
  'building data'               => 'Verarbeite Daten',
3112 3140
  'building report'             => 'Erstelle Bericht',
3141
  'button'                      => '',
3113 3142
  'cash'                        => 'Ist-Versteuerung',
3114 3143
  'chargenumber #1'             => 'Chargennummer #1',
3115 3144
  'chart_of_accounts'           => 'kontenuebersicht',
......
3186 3215
  'kivitendo will then update the database automatically.' => 'kivitendo wird die Datenbank daraufhin automatisch aktualisieren.',
3187 3216
  'lead deleted!'               => 'Kundenquelle gelöscht',
3188 3217
  'lead saved!'                 => 'Kundenquelle geichert',
3218
  'letters_list'                => '',
3189 3219
  'list'                        => 'auflisten',
3190 3220
  'list_of_payments'            => 'zahlungsausgaenge',
3191 3221
  'list_of_receipts'            => 'zahlungseingaenge',
menus/erp.ini
147 147
ACCESS=dunning_edit
148 148
module=dn.pl
149 149
action=add
150
[AR--Add Letter]
151
ACCESS=sales_letter_edit
152
module=letter.pl
153
action=add
150 154

  
151 155
[AR--Reports]
152 156
module=menu.pl
......
213 217
module=controller.pl
214 218
action=FinancialControllingReport/list
215 219

  
220
[AR--Reports--Letters]
221
ACCESS=sales_letter_report
222
module=letter.pl
223
action=search
224

  
216 225
[AP]
217 226

  
218 227
[AP--Add RFQ]
sql/Pg-upgrade2-auth/sales_letter_rights.pl
1
# @tag: sales_letter_rights
2
# @description: Setzt das neue Recht den Lieferplan anzuzeigen
3
# @depends: release_3_2_0
4
package SL::DBUpgrade2::sales_letter_rights;
5

  
6
use strict;
7
use utf8;
8

  
9
use parent qw(SL::DBUpgrade2::Base);
10

  
11
use SL::DBUtils;
12

  
13
sub run {
14
  my ($self) = @_;
15

  
16
  my $groups = $main::auth->read_groups();
17

  
18
  foreach my $group (values %{$groups}) {
19
    $group->{rights}->{sales_letter_edit} = $group->{rights}->{sales_order_edit};
20
    $group->{rights}->{sales_letter_report} = $group->{rights}->{sales_order_edit};
21
    $main::auth->save_group($group);
22
  }
23

  
24
  return 1;
25
} # end run
26

  
27
1;
sql/Pg-upgrade2/letter.sql
1
-- @tag: letter
2
-- @description: Brieffunktion Felder
3
-- @depends: release_3_2_0
4

  
5
CREATE TABLE letter (
6
  id INTEGER NOT NULL DEFAULT nextval('id'),
7
  vc_id INTEGER NOT NULL,
8
  rcv_name TEXT,
9
  rcv_contact TEXT,
10
  rcv_address TEXT,
11
  rcv_countrycode TEXT,
12
  rcv_zipcode TEXT,
13
  rcv_city TEXT,
14

  
15
  letternumber TEXT,
16
  jobnumber TEXT,
17
  text_created_for TEXT,
18
  date TEXT,
19

  
20
  subject TEXT,
21
  greeting TEXT,
22
  body TEXT,
23
  close TEXT,
24
  company_name TEXT,
25

  
26
  employee_id INTEGER NOT NULL,
27
  employee_position TEXT,
28

  
29
  salesman_id INTEGER NOT NULL,
30
  salesman_position TEXT,
31

  
32
  itime TIMESTAMP DEFAULT now(),
33
  mtime TIMESTAMP,
34

  
35
  PRIMARY KEY (id),
36
  FOREIGN KEY (employee_id) REFERENCES employee (id),
37
  FOREIGN KEY (salesman_id) REFERENCES employee (id)
38
);
39

  
40
ALTER TABLE defaults ADD COLUMN letternumber integer;
41

  
sql/Pg-upgrade2/letter_country_page.sql
1
-- @tag: letter_country_page
2
-- @description: Brieffunktion Felder Update
3
-- @depends: letter
4

  
5
ALTER TABLE letter ADD COLUMN rcv_country TEXT;
6
ALTER TABLE letter ADD COLUMN page_created_for TEXT;
7

  
sql/Pg-upgrade2/letter_cp_id.sql
1
-- @tag: letter_cp_id
2
-- @description: Ansprechpartner Link
3
-- @depends: letter_notes_internal
4

  
5
ALTER TABLE letter ADD COLUMN cp_id integer;
6
ALTER TABLE letter ADD FOREIGN KEY (cp_id) REFERENCES contacts(cp_id);
sql/Pg-upgrade2/letter_date_type.sql
1
-- @tag: letter_date_type
2
-- @description: Briefe: Datumsfeld als Datum speichern
3
-- @depends: release_3_2_0 letter
4
-- @encoding: utf-8
5
ALTER TABLE letter ADD column date_date DATE;
6
UPDATE letter SET date_date = date::DATE;
7
ALTER TABLE letter DROP COLUMN date;
8
ALTER TABLE letter RENAME COLUMN date_date TO date;
9

  
sql/Pg-upgrade2/letter_draft.sql
1
-- @tag: letter_draft
2
-- @description: Briefentwürfe Felder
3
-- @depends: release_3_2_0 letter
4

  
5
CREATE TABLE letter_draft (
6
  id INTEGER NOT NULL DEFAULT nextval('id'),
7
  vc_id INTEGER NOT NULL,
8
  cp_id INTEGER,
9
  rcv_name TEXT,
10
  rcv_contact TEXT,
11
  rcv_address TEXT,
12
  rcv_countrycode TEXT,
13
  rcv_zipcode TEXT,
14
  rcv_city TEXT,
15
  rcv_country TEXT,
16
  page_created_for TEXT,
17
  letternumber TEXT,
18
  jobnumber TEXT,
19
  text_created_for TEXT,
20
  date DATE,
21
  intnotes TEXT,
22

  
23
  reference TEXT,
24
  subject TEXT,
25
  greeting TEXT,
26
  body TEXT,
27
  close TEXT,
28
  company_name TEXT,
29

  
30
  employee_id INTEGER,
31
  employee_position TEXT,
32

  
33
  salesman_id INTEGER,
34
  salesman_position TEXT,
35

  
36
  itime TIMESTAMP DEFAULT now(),
37
  mtime TIMESTAMP,
38

  
39
  PRIMARY KEY (id),
40
  FOREIGN KEY (employee_id) REFERENCES employee (id),
41
  FOREIGN KEY (salesman_id) REFERENCES employee (id),
42
  FOREIGN KEY (cp_id) REFERENCES contacts(cp_id)
43
);
44

  
sql/Pg-upgrade2/letter_employee_salesman.sql
1
-- @tag: letter_emplyee_salesman
2
-- @description: Briefe: Fußfelder sind nicht mehr Pflicht.
3
-- @depends: letter_country_page
4

  
5
ALTER TABLE letter ALTER COLUMN employee_id DROP NOT NULL;
6
ALTER TABLE letter ALTER COLUMN salesman_id DROP NOT NULL;
7

  
8

  
sql/Pg-upgrade2/letter_notes_internal.sql
1
-- @tag: letter_notes_internal
2
-- @description: Briefe: interne Bemerkungen.
3
-- @depends: letter_emplyee_salesman
... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet.

Auch abrufbar als: Unified diff