Consolidation and extended test runs
[kivitendo-erp.git] / 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;