Merge branch 'gewicht'
[kivitendo-erp.git] / SL / CT.pm
1 #=====================================================================
2 # LX-Office ERP
3 # Copyright (C) 2004
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
6 #
7 #=====================================================================
8 # SQL-Ledger Accounting
9 # Copyright (C) 2001
10 #
11 #  Author: Dieter Simader
12 #   Email: dsimader@sql-ledger.org
13 #     Web: http://www.sql-ledger.org
14 #
15 #  Contributors:
16 #
17 # This program is free software; you can redistribute it and/or modify
18 # it under the terms of the GNU General Public License as published by
19 # the Free Software Foundation; either version 2 of the License, or
20 # (at your option) any later version.
21 #
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 # GNU General Public License for more details.
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #======================================================================
30 #
31 # backend code for customers and vendors
32 #
33 # CHANGE LOG:
34 #   DS. 2000-07-04  Created
35 #
36 #======================================================================
37
38 package CT;
39
40 use Data::Dumper;
41
42 use SL::Common;
43 use SL::CVar;
44 use SL::DBUtils;
45 use SL::FU;
46 use SL::Notes;
47 use SL::TransNumber;
48
49 use strict;
50
51 sub get_tuple {
52   $main::lxdebug->enter_sub();
53
54   my ( $self, $myconfig, $form ) = @_;
55
56   my $cv = $form->{db} eq "customer" ? "customer" : "vendor";
57
58   my $dbh   = $form->dbconnect($myconfig);
59   my $query =
60     qq|SELECT ct.*, b.id AS business, cp.* | .
61     qq|FROM $cv ct | .
62     qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
63     qq|LEFT JOIN contacts cp ON (ct.id = cp.cp_cv_id) | .
64     qq|WHERE (ct.id = ?) | .
65     qq|ORDER BY cp.cp_id LIMIT 1|;
66   my $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
67
68   my $ref = $sth->fetchrow_hashref("NAME_lc");
69
70   map { $form->{$_} = $ref->{$_} } keys %$ref;
71   $sth->finish;
72
73   #get name of currency instead of id:
74   $query = qq|SELECT name AS curr FROM currencies WHERE id=?|;
75   ($form->{curr}) = selectrow_query($form, $dbh, $query, conv_i($form->{currency_id}));
76
77   if ( $form->{salesman_id} ) {
78     my $query =
79       qq|SELECT ct.name AS salesman | .
80       qq|FROM $cv ct | .
81       qq|WHERE ct.id = ?|;
82     ($form->{salesman}) =
83       selectrow_query($form, $dbh, $query, $form->{salesman_id});
84   }
85
86   my ($employee_id) = selectrow_query($form, $dbh, qq|SELECT id FROM employee WHERE login = ?|, $form->{login});
87   $query =
88     qq|SELECT n.*, n.itime::DATE AS created_on,
89          e.name AS created_by_name, e.login AS created_by_login
90        FROM notes n
91        LEFT JOIN employee e ON (n.created_by = e.id)
92        WHERE (n.trans_id = ?) AND (n.trans_module = 'ct')|;
93   $form->{NOTES} = selectall_hashref_query($form, $dbh, $query, conv_i($form->{id}));
94
95   $query =
96     qq|SELECT fu.follow_up_date, fu.done AS follow_up_done, e.name AS created_for_name, e.name AS created_for_login
97        FROM follow_ups fu
98        LEFT JOIN employee e ON (fu.created_for_user = e.id)
99        WHERE (fu.note_id = ?)
100          AND NOT COALESCE(fu.done, FALSE)
101          AND (   (fu.created_by = ?)
102               OR (fu.created_by IN (SELECT DISTINCT what FROM follow_up_access WHERE who = ?)))|;
103   $sth = prepare_query($form, $dbh, $query);
104
105   foreach my $note (@{ $form->{NOTES} }) {
106     do_statement($form, $sth, $query, conv_i($note->{id}), conv_i($note->{created_by}), conv_i($employee_id));
107     $ref = $sth->fetchrow_hashref();
108
109     map { $note->{$_} = $ref->{$_} } keys %{ $ref } if ($ref);
110   }
111
112   $sth->finish();
113
114   if ($form->{edit_note_id}) {
115     $query =
116       qq|SELECT n.id AS NOTE_id, n.subject AS NOTE_subject, n.body AS NOTE_body,
117            fu.id AS FU_id, fu.follow_up_date AS FU_date, fu.done AS FU_done, fu.created_for_user AS FU_created_for_user
118          FROM notes n
119          LEFT JOIN follow_ups fu ON ((n.id = fu.note_id) AND NOT COALESCE(fu.done, FALSE))
120          WHERE n.id = ?|;
121     $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{edit_note_id}));
122
123     if ($ref) {
124       foreach my $key (keys %{ $ref }) {
125         my $new_key       =  $key;
126         $new_key          =~ s/^([^_]+)/\U$1\E/;
127         $form->{$new_key} =  $ref->{$key};
128       }
129     }
130   }
131
132   # check if it is orphaned
133   my $arap      = ( $form->{db} eq 'customer' ) ? "ar" : "ap";
134   my $num_args  = 2;
135   my $makemodel = '';
136   if ($form->{db} eq 'vendor') {
137     $makemodel = qq| UNION SELECT 1 FROM makemodel mm WHERE mm.make = ?|;
138     $num_args++;
139   }
140
141   $query =
142     qq|SELECT a.id | .
143     qq|FROM $arap a | .
144     qq|JOIN $cv ct ON (a.${cv}_id = ct.id) | .
145     qq|WHERE ct.id = ? | .
146     qq|UNION | .
147     qq|SELECT a.id | .
148     qq|FROM oe a | .
149     qq|JOIN $cv ct ON (a.${cv}_id = ct.id) | .
150     qq|WHERE ct.id = ?|
151     . $makemodel;
152   my ($dummy) = selectrow_query($form, $dbh, $query, (conv_i($form->{id})) x $num_args);
153
154   $form->{status} = "orphaned" unless ($dummy);
155
156   $dbh->disconnect;
157
158   $main::lxdebug->leave_sub();
159 }
160
161 sub populate_drop_down_boxes {
162   $main::lxdebug->enter_sub();
163
164   my ($self, $myconfig, $form, $provided_dbh) = @_;
165   my $query;
166
167   my $dbh = $provided_dbh ? $provided_dbh : $form->dbconnect($myconfig);
168
169   # get business types
170   $query = qq|SELECT id, description FROM business ORDER BY id|;
171   $form->{all_business} = selectall_hashref_query($form, $dbh, $query);
172
173   # get shipto address
174   $query =
175     qq|SELECT shipto_id, shiptoname, shiptodepartment_1, shiptostreet, shiptocity
176        FROM shipto
177        WHERE (trans_id = ?) AND (module = 'CT')|;
178   $form->{SHIPTO} = selectall_hashref_query($form, $dbh, $query, $form->{id});
179
180   # get contacts
181   $query  = qq|SELECT cp_id, cp_name, cp_givenname FROM contacts WHERE cp_cv_id = ? ORDER BY cp_name|;
182   $form->{CONTACTS} = selectall_hashref_query($form, $dbh, $query, $form->{id});
183
184   # get languages
185   $query = qq|SELECT id, description FROM language ORDER BY id|;
186   $form->{languages} = selectall_hashref_query($form, $dbh, $query);
187
188   # get payment terms
189   $query = qq|SELECT id, description FROM payment_terms ORDER BY sortkey|;
190   $form->{payment_terms} = selectall_hashref_query($form, $dbh, $query);
191
192   $dbh->disconnect() unless ($provided_dbh);
193
194   $main::lxdebug->leave_sub();
195 }
196
197 sub query_titles_and_greetings {
198   $main::lxdebug->enter_sub();
199
200   my ( $self, $myconfig, $form ) = @_;
201   my ( %tmp,  $ref, $query );
202
203   my $dbh = $form->dbconnect($myconfig);
204
205   $query =
206     qq|SELECT DISTINCT(greeting) | .
207     qq|FROM customer | .
208     qq|WHERE greeting ~ '[a-zA-Z]' | .
209     qq|UNION | .
210     qq|SELECT DISTINCT(greeting) | .
211     qq|FROM vendor | .
212     qq|WHERE greeting ~ '[a-zA-Z]' | .
213     qq|ORDER BY greeting|;
214
215   map({ $tmp{$_} = 1; } selectall_array_query($form, $dbh, $query));
216   $form->{COMPANY_GREETINGS} = [ sort(keys(%tmp)) ];
217
218   $query =
219     qq|SELECT DISTINCT(cp_title) | .
220     qq|FROM contacts | .
221     qq|WHERE cp_title ~ '[a-zA-Z]'|;
222   $form->{TITLES} = [ selectall_array_query($form, $dbh, $query) ];
223
224   $query =
225     qq|SELECT DISTINCT(cp_abteilung) | .
226     qq|FROM contacts | .
227     qq|WHERE cp_abteilung ~ '[a-zA-Z]'|;
228   $form->{DEPARTMENT} = [ selectall_array_query($form, $dbh, $query) ];
229
230   $dbh->disconnect();
231   $main::lxdebug->leave_sub();
232 }
233
234 sub save_customer {
235   $main::lxdebug->enter_sub();
236
237   my ( $self, $myconfig, $form ) = @_;
238
239   # set pricegroup to default
240   $form->{klass} = 0 unless ($form->{klass});
241
242   # connect to database
243   my $dbh = $form->get_standard_dbh;
244
245   map( {
246     $form->{"cp_${_}"} = $form->{"selected_cp_${_}"}
247     if ( $form->{"selected_cp_${_}"} );
248        } qw(title greeting abteilung) );
249   $form->{"greeting"} = $form->{"selected_company_greeting"}
250   if ( $form->{"selected_company_greeting"} );
251
252   # assign value discount, terms, creditlimit
253   $form->{discount} = $form->parse_amount( $myconfig, $form->{discount} );
254   $form->{discount} /= 100;
255   $form->{creditlimit} = $form->parse_amount( $myconfig, $form->{creditlimit} );
256
257   my ( $query, $sth, $f_id );
258
259   if ( $form->{id} ) {
260     $query = qq|SELECT id FROM customer WHERE customernumber = ?|;
261     ($f_id) = selectrow_query($form, $dbh, $query, $form->{customernumber});
262
263     if (($f_id ne $form->{id}) && ($f_id ne "")) {
264       $main::lxdebug->leave_sub();
265       return 3;
266     }
267
268   } else {
269     my $customernumber      = SL::TransNumber->new(type        => 'customer',
270                                                    dbh         => $dbh,
271                                                    number      => $form->{customernumber},
272                                                    business_id => $form->{business},
273                                                    save        => 1);
274     $form->{customernumber} = $customernumber->create_unique unless $customernumber->is_unique;
275
276     $query = qq|SELECT nextval('id')|;
277     ($form->{id}) = selectrow_query($form, $dbh, $query);
278
279     $query = qq|INSERT INTO customer (id, name, currency_id) VALUES (?, '', (SELECT currency_id FROM defaults))|;
280     do_query($form, $dbh, $query, $form->{id});
281   }
282
283   $query = qq|UPDATE customer SET | .
284     qq|customernumber = ?, | .
285     qq|name = ?, | .
286     qq|greeting = ?, | .
287     qq|department_1 = ?, | .
288     qq|department_2 = ?, | .
289     qq|street = ?, | .
290     qq|zipcode = ?, | .
291     qq|city = ?, | .
292     qq|country = ?, | .
293     qq|homepage = ?, | .
294     qq|contact = ?, | .
295     qq|phone = ?, | .
296     qq|fax = ?, | .
297     qq|email = ?, | .
298     qq|cc = ?, | .
299     qq|bcc = ?, | .
300     qq|notes = ?, | .
301     qq|discount = ?, | .
302     qq|creditlimit = ?, | .
303     qq|terms = ?, | .
304     qq|business_id = ?, | .
305     qq|taxnumber = ?, | .
306     qq|language = ?, | .
307     qq|account_number = ?, | .
308     qq|bank_code = ?, | .
309     qq|bank = ?, | .
310     qq|iban = ?, | .
311     qq|bic = ?, | .
312     qq|obsolete = ?, | .
313     qq|direct_debit = ?, | .
314     qq|ustid = ?, | .
315     qq|username = ?, | .
316     qq|salesman_id = ?, | .
317     qq|language_id = ?, | .
318     qq|payment_id = ?, | .
319     qq|taxzone_id = ?, | .
320     qq|user_password = ?, | .
321     qq|c_vendor_id = ?, | .
322     qq|klass = ?, | .
323     qq|currency_id = (SELECT id FROM currencies WHERE name = ?), | .
324     qq|taxincluded_checked = ? | .
325     qq|WHERE id = ?|;
326   my @values = (
327     $form->{customernumber},
328     $form->{name},
329     $form->{greeting},
330     $form->{department_1},
331     $form->{department_2},
332     $form->{street},
333     $form->{zipcode},
334     $form->{city},
335     $form->{country},
336     $form->{homepage},
337     $form->{contact},
338     $form->{phone},
339     $form->{fax},
340     $form->{email},
341     $form->{cc},
342     $form->{bcc},
343     $form->{notes},
344     $form->{discount},
345     $form->{creditlimit},
346     conv_i($form->{terms}),
347     conv_i($form->{business}),
348     $form->{taxnumber},
349     $form->{language},
350     $form->{account_number},
351     $form->{bank_code},
352     $form->{bank},
353     $form->{iban},
354     $form->{bic},
355     $form->{obsolete} ? 't' : 'f',
356     $form->{direct_debit} ? 't' : 'f',
357     $form->{ustid},
358     $form->{username},
359     conv_i($form->{salesman_id}),
360     conv_i($form->{language_id}),
361     conv_i($form->{payment_id}),
362     conv_i($form->{taxzone_id}, 0),
363     $form->{user_password},
364     $form->{c_vendor_id},
365     conv_i($form->{klass}),
366     $form->{currency},
367     $form->{taxincluded_checked} ne '' ? $form->{taxincluded_checked} : undef,
368     $form->{id}
369     );
370   do_query( $form, $dbh, $query, @values );
371
372   $form->{cp_id} = $self->_save_contact($form, $dbh);
373
374   # add shipto
375   $form->add_shipto( $dbh, $form->{id}, "CT" );
376
377   $self->_save_note('dbh' => $dbh);
378   $self->_delete_selected_notes('dbh' => $dbh);
379
380   CVar->save_custom_variables('dbh'       => $dbh,
381                               'module'    => 'CT',
382                               'trans_id'  => $form->{id},
383                               'variables' => $form,
384                               'always_valid' => 1);
385   if ($form->{cp_id}) {
386     CVar->save_custom_variables('dbh'       => $dbh,
387                                 'module'    => 'Contacts',
388                                 'trans_id'  => $form->{cp_id},
389                                 'variables' => $form,
390                                 'name_prefix'  => 'cp',
391                                 'always_valid' => 1);
392   }
393
394   my $rc = $dbh->commit();
395
396   $main::lxdebug->leave_sub();
397   return $rc;
398 }
399
400 sub save_vendor {
401   $main::lxdebug->enter_sub();
402
403   my ( $self, $myconfig, $form ) = @_;
404
405   $form->{taxzone_id} *= 1;
406   # connect to database
407   my $dbh = $form->get_standard_dbh;
408
409   map( {
410     $form->{"cp_${_}"} = $form->{"selected_cp_${_}"}
411     if ( $form->{"selected_cp_${_}"} );
412        } qw(title greeting abteilung) );
413   $form->{"greeting"} = $form->{"selected_company_greeting"}
414   if ( $form->{"selected_company_greeting"} );
415
416   $form->{discount} = $form->parse_amount( $myconfig, $form->{discount} );
417   $form->{discount} /= 100;
418   $form->{creditlimit} = $form->parse_amount( $myconfig, $form->{creditlimit} );
419
420   my $query;
421
422   if (!$form->{id}) {
423     $query = qq|SELECT nextval('id')|;
424     ($form->{id}) = selectrow_query($form, $dbh, $query);
425
426     $query = qq|INSERT INTO vendor (id, name, currency_id) VALUES (?, '', (SELECT currency_id FROM defaults))|;
427     do_query($form, $dbh, $query, $form->{id});
428
429     my $vendornumber      = SL::TransNumber->new(type   => 'vendor',
430                                                  dbh    => $dbh,
431                                                  number => $form->{vendornumber},
432                                                  save   => 1);
433     $form->{vendornumber} = $vendornumber->create_unique unless $vendornumber->is_unique;
434   }
435
436   $query =
437     qq|UPDATE vendor SET | .
438     qq|  vendornumber = ?, | .
439     qq|  name = ?, | .
440     qq|  greeting = ?, | .
441     qq|  department_1 = ?, | .
442     qq|  department_2 = ?, | .
443     qq|  street = ?, | .
444     qq|  zipcode = ?, | .
445     qq|  city = ?, | .
446     qq|  country = ?, | .
447     qq|  homepage = ?, | .
448     qq|  contact = ?, | .
449     qq|  phone = ?, | .
450     qq|  fax = ?, | .
451     qq|  email = ?, | .
452     qq|  cc = ?, | .
453     qq|  bcc = ?, | .
454     qq|  notes = ?, | .
455     qq|  terms = ?, | .
456     qq|  discount = ?, | .
457     qq|  creditlimit = ?, | .
458     qq|  business_id = ?, | .
459     qq|  taxnumber = ?, | .
460     qq|  language = ?, | .
461     qq|  account_number = ?, | .
462     qq|  bank_code = ?, | .
463     qq|  bank = ?, | .
464     qq|  iban = ?, | .
465     qq|  bic = ?, | .
466     qq|  obsolete = ?, | .
467     qq|  direct_debit = ?, | .
468     qq|  ustid = ?, | .
469     qq|  payment_id = ?, | .
470     qq|  taxzone_id = ?, | .
471     qq|  language_id = ?, | .
472     qq|  username = ?, | .
473     qq|  user_password = ?, | .
474     qq|  v_customer_id = ?, | .
475     qq|  currency_id = (SELECT id FROM currencies WHERE name = ?) | .
476     qq|WHERE id = ?|;
477   my @values = (
478     $form->{vendornumber},
479     $form->{name},
480     $form->{greeting},
481     $form->{department_1},
482     $form->{department_2},
483     $form->{street},
484     $form->{zipcode},
485     $form->{city},
486     $form->{country},
487     $form->{homepage},
488     $form->{contact},
489     $form->{phone},
490     $form->{fax},
491     $form->{email},
492     $form->{cc},
493     $form->{bcc},
494     $form->{notes},
495     conv_i($form->{terms}),
496     $form->{discount},
497     $form->{creditlimit},
498     conv_i($form->{business}),
499     $form->{taxnumber},
500     $form->{language},
501     $form->{account_number},
502     $form->{bank_code},
503     $form->{bank},
504     $form->{iban},
505     $form->{bic},
506     $form->{obsolete} ? 't' : 'f',
507     $form->{direct_debit} ? 't' : 'f',
508     $form->{ustid},
509     conv_i($form->{payment_id}),
510     conv_i($form->{taxzone_id}, 0),
511     conv_i( $form->{language_id}),
512     $form->{username},
513     $form->{user_password},
514     $form->{v_customer_id},
515     $form->{currency},
516     $form->{id}
517     );
518   do_query($form, $dbh, $query, @values);
519
520   $form->{cp_id} = $self->_save_contact($form, $dbh);
521
522   # add shipto
523   $form->add_shipto( $dbh, $form->{id}, "CT" );
524
525   $self->_save_note('dbh' => $dbh);
526   $self->_delete_selected_notes('dbh' => $dbh);
527
528   CVar->save_custom_variables('dbh'       => $dbh,
529                               'module'    => 'CT',
530                               'trans_id'  => $form->{id},
531                               'variables' => $form,
532                               'always_valid' => 1);
533   if ($form->{cp_id}) {
534     CVar->save_custom_variables('dbh'       => $dbh,
535                                 'module'    => 'Contacts',
536                                 'trans_id'  => $form->{cp_id},
537                                 'variables' => $form,
538                                 'name_prefix'  => 'cp',
539                                 'always_valid' => 1);
540   }
541
542   my $rc = $dbh->commit();
543
544   $main::lxdebug->leave_sub();
545   return $rc;
546 }
547
548 sub _save_contact {
549   my ($self, $form, $dbh) = @_;
550
551   return undef unless $form->{cp_id} || $form->{cp_name} || $form->{cp_givenname};
552
553   my @columns = qw(cp_title cp_givenname cp_name cp_email cp_phone1 cp_phone2 cp_abteilung cp_fax
554                    cp_mobile1 cp_mobile2 cp_satphone cp_satfax cp_project cp_privatphone cp_privatemail cp_birthday cp_gender
555                    cp_street cp_zipcode cp_city cp_position);
556   my @values  = map(
557     {
558       if ( $_ eq 'cp_gender' ) {
559         $form->{$_} eq 'f' ? 'f' : 'm';
560       } elsif ( $_ eq 'cp_birthday' && $form->{cp_birthday} eq '' ) {
561         undef;
562       } else {
563         $form->{$_};
564       }
565     }
566     @columns
567   );
568
569   my ($query, $cp_id);
570   if ($form->{cp_id}) {
571     $query = qq|UPDATE contacts SET | . join(', ', map { "${_} = ?" } @columns) . qq| WHERE cp_id = ?|;
572     push @values, $form->{cp_id};
573     $cp_id = $form->{cp_id};
574
575   } else {
576     ($cp_id) = selectrow_query($form, $dbh, qq|SELECT nextval('id')|);
577
578     $query = qq|INSERT INTO contacts (| . join(', ', @columns, 'cp_cv_id', 'cp_id') . qq|) VALUES (| . join(', ', ('?') x (2 + scalar @columns)) . qq|)|;
579     push @values, $form->{id}, $cp_id;
580   }
581
582   do_query($form, $dbh, $query, @values);
583
584   return $cp_id;
585 }
586
587 sub delete {
588   $main::lxdebug->enter_sub();
589
590   my ( $self, $myconfig, $form ) = @_;
591   # connect to database
592   my $dbh = $form->dbconnect($myconfig);
593
594   # delete vendor
595   my $cv = $form->{db} eq "customer" ? "customer" : "vendor";
596   my $query = qq|DELETE FROM $cv WHERE id = ?|;
597   do_query($form, $dbh, $query, $form->{id});
598
599   $dbh->disconnect;
600
601   $main::lxdebug->leave_sub();
602 }
603
604 sub search {
605   $main::lxdebug->enter_sub();
606
607   my ( $self, $myconfig, $form ) = @_;
608
609   # connect to database
610   my $dbh = $form->dbconnect($myconfig);
611
612   my $cv = $form->{db} eq "customer" ? "customer" : "vendor";
613   my $join_records = $form->{l_invnumber} || $form->{l_ordnumber} || $form->{l_quonumber};
614
615   my $where = "1 = 1";
616   my @values;
617
618   my %allowed_sort_columns = (
619       "id"                 => "ct.id",
620       "customernumber"     => "ct.customernumber",
621       "vendornumber"       => "ct.vendornumber",
622       "name"               => "ct.name",
623       "contact"            => "ct.contact",
624       "phone"              => "ct.phone",
625       "fax"                => "ct.fax",
626       "email"              => "ct.email",
627       "street"             => "ct.street",
628       "taxnumber"          => "ct.taxnumber",
629       "business"           => "ct.business",
630       "invnumber"          => "ct.invnumber",
631       "ordnumber"          => "ct.ordnumber",
632       "quonumber"          => "ct.quonumber",
633       "zipcode"            => "ct.zipcode",
634       "city"               => "ct.city",
635       "country"            => "ct.country",
636       "salesman"           => "e.name"
637     );
638
639   $form->{sort} ||= "name";
640   my $sortorder;
641   if ( $join_records ) {
642     # in UNION case order by hash key, e.g. salesman
643     # the UNION created an implicit select around the result
644     $sortorder = $allowed_sort_columns{$form->{sort}} ? $form->{sort} : "name";
645   } else {
646     # in not UNION case order by hash value, e.g. e.name
647     $sortorder = $allowed_sort_columns{$form->{sort}} ?  $allowed_sort_columns{$form->{sort}} : "ct.name";
648   }
649   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
650
651   if ($sortorder !~ /(business|id)/ && !$join_records) {
652     $sortorder  = "lower($sortorder) ${sortdir}";
653   } else {
654     $sortorder .= " ${sortdir}";
655   }
656
657   if ($form->{"${cv}number"}) {
658     $where .= " AND ct.${cv}number ILIKE ?";
659     push(@values, '%' . $form->{"${cv}number"} . '%');
660   }
661
662   foreach my $key (qw(name contact email)) {
663     if ($form->{$key}) {
664       $where .= " AND ct.$key ILIKE ?";
665       push(@values, '%' . $form->{$key} . '%');
666     }
667   }
668
669   if ($form->{cp_name}) {
670     $where .= " AND ct.id IN (SELECT cp_cv_id FROM contacts WHERE lower(cp_name) LIKE lower(?))";
671     push @values, '%' . $form->{cp_name} . '%';
672   }
673
674   if ($form->{addr_city}) {
675     $where .= " AND ((lower(ct.city) LIKE lower(?))
676                      OR
677                      (ct.id IN (
678                         SELECT sc.trans_id
679                         FROM shipto sc
680                         WHERE (sc.module = 'CT')
681                           AND (lower(sc.shiptocity) LIKE lower(?))
682                       ))
683                      )";
684     push @values, ('%' . $form->{addr_city} . '%') x 2;
685   }
686
687   if ($form->{addr_country}) {
688     $where .= " AND ((lower(ct.country) LIKE lower(?))
689                      OR
690                      (ct.id IN (
691                         SELECT so.trans_id
692                         FROM shipto so
693                         WHERE (so.module = 'CT')
694                           AND (lower(so.shiptocountry) LIKE lower(?))
695                       ))
696                      )";
697     push @values, ('%' . $form->{addr_country} . '%') x 2;
698   }
699
700   if ( $form->{status} eq 'orphaned' ) {
701     $where .=
702       qq| AND ct.id NOT IN | .
703       qq|   (SELECT o.${cv}_id FROM oe o, $cv cv WHERE cv.id = o.${cv}_id)|;
704     if ($cv eq 'customer') {
705       $where .=
706         qq| AND ct.id NOT IN | .
707         qq| (SELECT a.customer_id FROM ar a, customer cv | .
708         qq|  WHERE cv.id = a.customer_id)|;
709     }
710     if ($cv eq 'vendor') {
711       $where .=
712         qq| AND ct.id NOT IN | .
713         qq| (SELECT a.vendor_id FROM ap a, vendor cv | .
714         qq|  WHERE cv.id = a.vendor_id)|;
715     }
716     $form->{l_invnumber} = $form->{l_ordnumber} = $form->{l_quonumber} = "";
717   }
718
719   if ($form->{obsolete} eq "Y") {
720     $where .= qq| AND ct.obsolete|;
721   } elsif ($form->{obsolete} eq "N") {
722     $where .= qq| AND NOT ct.obsolete|;
723   }
724
725   if ($form->{business_id}) {
726     $where .= qq| AND (ct.business_id = ?)|;
727     push(@values, conv_i($form->{business_id}));
728   }
729
730   # Nur Kunden finden, bei denen ich selber der Verkäufer bin
731   # Gilt nicht für Lieferanten
732   if ($cv eq 'customer' &&   !$main::auth->assert('customer_vendor_all_edit', 1)) {
733     $where .= qq| AND ct.salesman_id = (select em.id from employee em where em.login = ?)|;
734     push(@values, $form->{login});
735   }
736
737   my ($cvar_where, @cvar_values) = CVar->build_filter_query('module'         => 'CT',
738                                                             'trans_id_field' => 'ct.id',
739                                                             'filter'         => $form);
740
741   if ($cvar_where) {
742     $where .= qq| AND ($cvar_where)|;
743     push @values, @cvar_values;
744   }
745
746   if ($form->{addr_street}) {
747     $where .= qq| AND (ct.street ILIKE ?)|;
748     push @values, '%' . $form->{addr_street} . '%';
749   }
750
751   if ($form->{addr_zipcode}) {
752     $where .= qq| AND (ct.zipcode ILIKE ?)|;
753     push @values, $form->{addr_zipcode} . '%';
754   }
755
756   my $query =
757     qq|SELECT ct.*, b.description AS business, e.name as salesman | .
758     (qq|, NULL AS invnumber, NULL AS ordnumber, NULL AS quonumber, NULL AS invid, NULL AS module, NULL AS formtype, NULL AS closed | x!! $join_records) .
759     qq|FROM $cv ct | .
760     qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
761     qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
762     qq|WHERE $where|;
763
764   my @saved_values = @values;
765   # redo for invoices, orders and quotations
766   if ($join_records) {
767     my $union = "UNION";
768
769     if ($form->{l_invnumber}) {
770       my $ar = $cv eq 'customer' ? 'ar' : 'ap';
771       my $module = $ar eq 'ar' ? 'is' : 'ir';
772       push(@values, @saved_values);
773       $query .=
774         qq| UNION | .
775         qq|SELECT ct.*, b.description AS business, e.name as salesman, | .
776         qq|  a.invnumber, a.ordnumber, a.quonumber, a.id AS invid, | .
777         qq|  '$module' AS module, 'invoice' AS formtype, | .
778         qq|  (a.amount = a.paid) AS closed | .
779         qq|FROM $cv ct | .
780         qq|JOIN $ar a ON (a.${cv}_id = ct.id) | .
781         qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
782         qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
783         qq|WHERE $where AND (a.invoice = '1')|;
784     }
785
786     if ( $form->{l_ordnumber} ) {
787       push(@values, @saved_values);
788       $query .=
789         qq| UNION | .
790         qq|SELECT ct.*, b.description AS business, e.name as salesman, | .
791         qq|  ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
792         qq|  'oe' AS module, 'order' AS formtype, o.closed | .
793         qq|FROM $cv ct | .
794         qq|JOIN oe o ON (o.${cv}_id = ct.id) | .
795         qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
796         qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
797         qq|WHERE $where AND (o.quotation = '0')|;
798     }
799
800     if ( $form->{l_quonumber} ) {
801       push(@values, @saved_values);
802       $query .=
803         qq| UNION | .
804         qq|SELECT ct.*, b.description AS business, e.name as salesman, | .
805         qq|  ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
806         qq|  'oe' AS module, 'quotation' AS formtype, o.closed | .
807         qq|FROM $cv ct | .
808         qq|JOIN oe o ON (o.${cv}_id = ct.id) | .
809         qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
810         qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
811         qq|WHERE $where AND (o.quotation = '1')|;
812     }
813   }
814
815   $query .= qq| ORDER BY $sortorder|;
816
817   $form->{CT} = selectall_hashref_query($form, $dbh, $query, @values);
818
819   $main::lxdebug->leave_sub();
820 }
821
822 sub get_contact {
823   $main::lxdebug->enter_sub();
824
825   my ( $self, $myconfig, $form ) = @_;
826
827   die 'Missing argument: cp_id' unless $::form->{cp_id};
828
829   my $dbh   = $form->dbconnect($myconfig);
830   my $query =
831     qq|SELECT * FROM contacts c | .
832     qq|WHERE cp_id = ? ORDER BY cp_id limit 1|;
833   my $sth = prepare_execute_query($form, $dbh, $query, $form->{cp_id});
834   my $ref = $sth->fetchrow_hashref("NAME_lc");
835
836   map { $form->{$_} = $ref->{$_} } keys %$ref;
837
838   $query = qq|SELECT COUNT(cp_id) AS used FROM (
839     SELECT cp_id FROM oe UNION
840     SELECT cp_id FROM ar UNION
841     SELECT cp_id FROM ap UNION
842     SELECT cp_id FROM delivery_orders
843   ) AS cpid WHERE cp_id = ? OR ? = 0|;
844   ($form->{cp_used}) = selectfirst_array_query($form, $dbh, $query, ($form->{cp_id})x2);
845
846   $sth->finish;
847   $dbh->disconnect;
848
849   $main::lxdebug->leave_sub();
850 }
851
852 sub get_shipto {
853   $main::lxdebug->enter_sub();
854
855   my ( $self, $myconfig, $form ) = @_;
856   my $dbh   = $form->dbconnect($myconfig);
857   my $query = qq|SELECT * FROM shipto WHERE shipto_id = ?|;
858   my $sth = prepare_execute_query($form, $dbh, $query, $form->{shipto_id});
859
860   my $ref = $sth->fetchrow_hashref("NAME_lc");
861
862   map { $form->{$_} = $ref->{$_} } keys %$ref;
863
864   $query = qq|SELECT COUNT(shipto_id) AS used FROM (
865     SELECT shipto_id FROM oe UNION
866     SELECT shipto_id FROM ar UNION
867     SELECT shipto_id FROM delivery_orders
868   ) AS stid WHERE shipto_id = ? OR ? = 0|;
869   ($form->{shiptoused}) = selectfirst_array_query($form, $dbh, $query, ($form->{shipto_id})x2);
870
871   $sth->finish;
872   $dbh->disconnect;
873
874   $main::lxdebug->leave_sub();
875 }
876
877 sub get_delivery {
878   $main::lxdebug->enter_sub();
879
880   my ( $self, $myconfig, $form ) = @_;
881   my $dbh = $form->dbconnect($myconfig);
882
883   my $arap = $form->{db} eq "vendor" ? "ap" : "ar";
884   my $db = $form->{db} eq "customer" ? "customer" : "vendor";
885   my $qty_sign = $form->{db} eq 'vendor' ? ' * -1 AS qty' : '';
886
887   my $where = " WHERE 1=1 ";
888   my @values;
889
890   if ($form->{shipto_id} && ($arap eq "ar")) {
891     $where .= "AND ${arap}.shipto_id = ?";
892     push(@values, $form->{shipto_id});
893   } else {
894     $where .= "AND ${arap}.${db}_id = ?";
895     push(@values, $form->{id});
896   }
897
898   if ($form->{from}) {
899     $where .= "AND ${arap}.transdate >= ?";
900     push(@values, conv_date($form->{from}));
901   }
902   if ($form->{to}) {
903     $where .= "AND ${arap}.transdate <= ?";
904     push(@values, conv_date($form->{to}));
905   }
906   my $query =
907     qq|SELECT s.shiptoname, i.qty $qty_sign, | .
908     qq|  ${arap}.id, ${arap}.transdate, ${arap}.invnumber, ${arap}.ordnumber, | .
909     qq|  i.description, i.unit, i.sellprice, | .
910     qq|  oe.id AS oe_id, invoice | .
911     qq|FROM $arap | .
912     qq|LEFT JOIN shipto s ON | .
913     ($arap eq "ar"
914      ? qq|(ar.shipto_id = s.shipto_id) |
915      : qq|(ap.id = s.trans_id) |) .
916     qq|LEFT JOIN invoice i ON (${arap}.id = i.trans_id) | .
917     qq|LEFT join parts p ON (p.id = i.parts_id) | .
918     qq|LEFT JOIN oe ON (oe.ordnumber = ${arap}.ordnumber AND NOT ${arap}.ordnumber = '') | .
919     $where .
920     qq|ORDER BY ${arap}.transdate DESC LIMIT 15|;
921
922   $form->{DELIVERY} = selectall_hashref_query($form, $dbh, $query, @values);
923
924   $dbh->disconnect;
925
926   $main::lxdebug->leave_sub();
927 }
928
929 sub _save_note {
930   $main::lxdebug->enter_sub();
931
932   my $self   = shift;
933   my %params = @_;
934
935   my $form   = $main::form;
936
937   Common::check_params(\%params, 'dbh');
938
939   if (!$form->{NOTE_subject}) {
940     $main::lxdebug->leave_sub();
941     return;
942   }
943
944   my $dbh = $params{dbh};
945
946   my %follow_up;
947   my %note = (
948     'id'           => $form->{NOTE_id},
949     'subject'      => $form->{NOTE_subject},
950     'body'         => $form->{NOTE_body},
951     'trans_id'     => $form->{id},
952     'trans_module' => 'ct',
953   );
954
955   $note{id} = Notes->save(%note);
956
957   if ($form->{FU_date}) {
958     %follow_up = (
959       'id'               => $form->{FU_id},
960       'note_id'          => $note{id},
961       'follow_up_date'   => $form->{FU_date},
962       'created_for_user' => $form->{FU_created_for_user},
963       'done'             => $form->{FU_done} ? 1 : 0,
964       'subject'          => $form->{NOTE_subject},
965       'body'             => $form->{NOTE_body},
966       'LINKS'            => [
967         {
968           'trans_id'     => $form->{id},
969           'trans_type'   => $form->{db} eq 'customer' ? 'customer' : 'vendor',
970           'trans_info'   => $form->{name},
971         },
972       ],
973     );
974
975     $follow_up{id} = FU->save(%follow_up);
976
977   } elsif ($form->{FU_id}) {
978     do_query($form, $dbh, qq|DELETE FROM follow_up_links WHERE follow_up_id = ?|, conv_i($form->{FU_id}));
979     do_query($form, $dbh, qq|DELETE FROM follow_ups      WHERE id = ?|,           conv_i($form->{FU_id}));
980   }
981
982   delete @{$form}{grep { /^NOTE_|^FU_/ } keys %{ $form }};
983
984   $main::lxdebug->leave_sub();
985 }
986
987 sub _delete_selected_notes {
988   $main::lxdebug->enter_sub();
989
990   my $self   = shift;
991   my %params = @_;
992
993   Common::check_params(\%params, 'dbh');
994
995   my $form = $main::form;
996   my $dbh  = $params{dbh};
997
998   foreach my $i (1 .. $form->{NOTES_rowcount}) {
999     next unless ($form->{"NOTE_delete_$i"} && $form->{"NOTE_id_$i"});
1000
1001     Notes->delete('dbh' => $params{dbh},
1002                   'id'  => $form->{"NOTE_id_$i"});
1003   }
1004
1005   $main::lxdebug->leave_sub();
1006 }
1007
1008 # TODO: remove in 2.7.0 stable
1009 sub delete_shipto {
1010   $main::lxdebug->enter_sub();
1011
1012   my $self      = shift;
1013   my $shipto_id = shift;
1014
1015   my $form      = $main::form;
1016   my %myconfig  = %main::myconfig;
1017   my $dbh       = $form->get_standard_dbh(\%myconfig);
1018
1019   do_query($form, $dbh, qq|UPDATE shipto SET trans_id = NULL WHERE shipto_id = ?|, $shipto_id);
1020
1021   $dbh->commit();
1022
1023   $main::lxdebug->leave_sub();
1024 }
1025
1026 # TODO: remove in 2.7.0 stable
1027 sub delete_contact {
1028   $main::lxdebug->enter_sub();
1029
1030   my $self      = shift;
1031   my $cp_id     = shift;
1032
1033   my $form      = $main::form;
1034   my %myconfig  = %main::myconfig;
1035   my $dbh       = $form->get_standard_dbh(\%myconfig);
1036
1037   do_query($form, $dbh, qq|UPDATE contacts SET cp_cv_id = NULL WHERE cp_id = ?|, $cp_id);
1038
1039   $dbh->commit();
1040
1041   $main::lxdebug->leave_sub();
1042 }
1043
1044 sub get_bank_info {
1045   $main::lxdebug->enter_sub();
1046
1047   my $self     = shift;
1048   my %params   = @_;
1049
1050   Common::check_params(\%params, qw(vc id));
1051
1052   my $myconfig = \%main::myconfig;
1053   my $form     = $main::form;
1054
1055   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
1056
1057   my $table        = $params{vc} eq 'customer' ? 'customer' : 'vendor';
1058   my @ids          = ref $params{id} eq 'ARRAY' ? @{ $params{id} } : ($params{id});
1059   my $placeholders = join ", ", ('?') x scalar @ids;
1060   my $query        = qq|SELECT id, name, account_number, bank, bank_code, iban, bic
1061                         FROM ${table}
1062                         WHERE id IN (${placeholders})|;
1063
1064   my $result       = selectall_hashref_query($form, $dbh, $query, map { conv_i($_) } @ids);
1065
1066   if (ref $params{id} eq 'ARRAY') {
1067     $result = { map { $_->{id} => $_ } @{ $result } };
1068   } else {
1069     $result = $result->[0] || { 'id' => $params{id} };
1070   }
1071
1072   $main::lxdebug->leave_sub();
1073
1074   return $result;
1075 }
1076
1077 sub parse_excel_file {
1078   $main::lxdebug->enter_sub();
1079
1080   my ($self, $myconfig, $form) = @_;
1081   my $locale = $main::locale;
1082
1083   $form->{formname}   = 'sales_quotation';
1084   $form->{type}   = 'sales_quotation';
1085   $form->{format} = 'excel';
1086   $form->{media}  = 'screen';
1087   $form->{quonumber} = 1;
1088
1089
1090   # $form->{"notes"} will be overridden by the customer's/vendor's "notes" field. So save it here.
1091   $form->{ $form->{"formname"} . "notes" } = $form->{"notes"};
1092
1093   my $inv                  = "quo";
1094   my $due                  = "req";
1095   $form->{"${inv}date"} = $form->{transdate};
1096   $form->{label}        = $locale->text('Quotation');
1097   my $numberfld            = "sqnumber";
1098   my $order                = 1;
1099
1100   # assign number
1101   $form->{what_done} = $form->{formname};
1102
1103   map({ delete($form->{$_}); } grep(/^cp_/, keys(%{ $form })));
1104
1105   my $output_dateformat = $myconfig->{"dateformat"};
1106   my $output_numberformat = $myconfig->{"numberformat"};
1107   my $output_longdates = 1;
1108
1109   # map login user variables
1110   map { $form->{"login_$_"} = $myconfig->{$_} } ("name", "email", "fax", "tel", "company");
1111
1112   # format item dates
1113   for my $field (qw(transdate_oe deliverydate_oe)) {
1114     map {
1115       $form->{$field}[$_] = $locale->date($myconfig, $form->{$field}[$_], 1);
1116     } 0 .. $#{ $form->{$field} };
1117   }
1118
1119   if ($form->{shipto_id}) {
1120     $form->get_shipto($myconfig);
1121   }
1122
1123   $form->{notes} =~ s/^\s+//g;
1124
1125   $form->{templates} = $myconfig->{templates};
1126
1127   delete $form->{printer_command};
1128
1129   $form->get_employee_info($myconfig);
1130
1131   my ($cvar_date_fields, $cvar_number_fields) = CVar->get_field_format_list('module' => 'CT', 'prefix' => 'vc_');
1132
1133   if (scalar @{ $cvar_date_fields }) {
1134     format_dates($output_dateformat, $output_longdates, @{ $cvar_date_fields });
1135   }
1136
1137   while (my ($precision, $field_list) = each %{ $cvar_number_fields }) {
1138     reformat_numbers($output_numberformat, $precision, @{ $field_list });
1139   }
1140
1141   $form->{excel} = 1;
1142   my $extension            = 'xls';
1143
1144   $form->{IN}         = "$form->{formname}.${extension}";
1145
1146   delete $form->{OUT};
1147
1148   $form->parse_template($myconfig);
1149
1150   $main::lxdebug->leave_sub();
1151 }
1152
1153 sub search_contacts {
1154   $::lxdebug->enter_sub;
1155
1156   my $self      = shift;
1157   my %params    = @_;
1158
1159   my $dbh       = $params{dbh} || $::form->get_standard_dbh;
1160
1161   my %sortspecs = (
1162     'cp_name'   => 'cp_name, cp_givenname',
1163     'vcname'    => 'vcname, cp_name, cp_givenname',
1164     'vcnumber'  => 'vcnumber, cp_name, cp_givenname',
1165     );
1166
1167   my %sortcols  = map { $_ => 1 } qw(cp_name cp_givenname cp_phone1 cp_phone2 cp_mobile1 cp_email cp_street cp_zipcode cp_city cp_position vcname vcnumber);
1168
1169   my $order_by  = $sortcols{$::form->{sort}} ? $::form->{sort} : 'cp_name';
1170   $::form->{sort} = $order_by;
1171   $order_by     = $sortspecs{$order_by} if ($sortspecs{$order_by});
1172
1173   my $sortdir   = $::form->{sortdir} ? 'ASC' : 'DESC';
1174   $order_by     =~ s/,/ ${sortdir},/g;
1175   $order_by    .= " $sortdir";
1176
1177   my @where_tokens = ();
1178   my @values;
1179
1180   if ($params{search_term}) {
1181     my @tokens;
1182     push @tokens,
1183       'cp.cp_name      ILIKE ?',
1184       'cp.cp_givenname ILIKE ?',
1185       'cp.cp_email     ILIKE ?';
1186     push @values, ('%' . $params{search_term} . '%') x 3;
1187
1188     if (($params{search_term} =~ m/\d/) && ($params{search_term} !~ m/[^\d \(\)+\-]/)) {
1189       my $number =  $params{search_term};
1190       $number    =~ s/[^\d]//g;
1191       $number    =  join '[ /\(\)+\-]*', split(m//, $number);
1192
1193       push @tokens, map { "($_ ~ '$number')" } qw(cp_phone1 cp_phone2 cp_mobile1 cp_mobile2);
1194     }
1195
1196     push @where_tokens, map { "($_)" } join ' OR ', @tokens;
1197   }
1198
1199   my ($cvar_where, @cvar_values) = CVar->build_filter_query('module'         => 'Contacts',
1200                                                             'trans_id_field' => 'cp.cp_id',
1201                                                             'filter'         => $params{filter});
1202
1203   if ($cvar_where) {
1204     push @where_tokens, $cvar_where;
1205     push @values, @cvar_values;
1206   }
1207
1208   if (my $filter = $params{filter}) {
1209     for (qw(name title givenname email project abteilung)) {
1210       next unless $filter->{"cp_$_"};
1211       add_token(\@where_tokens, \@values, col =>  "cp.cp_$_", val => $filter->{"cp_$_"}, method => 'ILIKE', esc => 'substr');
1212     }
1213
1214     push @where_tokens, 'cp.cp_cv_id IS NOT NULL' if $filter->{status} eq 'active';
1215     push @where_tokens, 'cp.cp_cv_id IS NULL'     if $filter->{status} eq 'orphaned';
1216   }
1217
1218   my $where = @where_tokens ? 'WHERE ' . join ' AND ', @where_tokens : '';
1219
1220   my $query     = qq|SELECT cp.*,
1221                        COALESCE(c.id,             v.id)           AS vcid,
1222                        COALESCE(c.name,           v.name)         AS vcname,
1223                        COALESCE(c.customernumber, v.vendornumber) AS vcnumber,
1224                        CASE WHEN c.name IS NULL THEN 'vendor' ELSE 'customer' END AS db
1225                      FROM contacts cp
1226                      LEFT JOIN customer c ON (cp.cp_cv_id = c.id)
1227                      LEFT JOIN vendor v   ON (cp.cp_cv_id = v.id)
1228                      $where
1229                      ORDER BY $order_by|;
1230
1231   my $contacts  = selectall_hashref_query($::form, $dbh, $query, @values);
1232
1233   $::lxdebug->leave_sub;
1234
1235   return @{ $contacts };
1236 }
1237
1238
1239 1;