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