Beim DATEV-Export Lieferdatum als entscheidenden Tag zur Bestimmung der Steuer berück...
[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 cp_position);
555   my @values  = map(
556     {
557       if ( $_ eq 'cp_gender' ) {
558         $form->{$_} eq 'f' ? 'f' : 'm';
559       } elsif ( $_ eq 'cp_birthday' && $form->{cp_birthday} eq '' ) {
560         undef;
561       } else {
562         $form->{$_};
563       }
564     }
565     @columns
566   );
567
568   my ($query, $cp_id);
569   if ($form->{cp_id}) {
570     $query = qq|UPDATE contacts SET | . join(', ', map { "${_} = ?" } @columns) . qq| WHERE cp_id = ?|;
571     push @values, $form->{cp_id};
572     $cp_id = $form->{cp_id};
573
574   } else {
575     ($cp_id) = selectrow_query($form, $dbh, qq|SELECT nextval('id')|);
576
577     $query = qq|INSERT INTO contacts (| . join(', ', @columns, 'cp_cv_id', 'cp_id') . qq|) VALUES (| . join(', ', ('?') x (2 + scalar @columns)) . qq|)|;
578     push @values, $form->{id}, $cp_id;
579   }
580
581   do_query($form, $dbh, $query, @values);
582
583   return $cp_id;
584 }
585
586 sub delete {
587   $main::lxdebug->enter_sub();
588
589   my ( $self, $myconfig, $form ) = @_;
590   # connect to database
591   my $dbh = $form->dbconnect($myconfig);
592
593   # delete vendor
594   my $cv = $form->{db} eq "customer" ? "customer" : "vendor";
595   my $query = qq|DELETE FROM $cv WHERE id = ?|;
596   do_query($form, $dbh, $query, $form->{id});
597
598   $dbh->disconnect;
599
600   $main::lxdebug->leave_sub();
601 }
602
603 sub search {
604   $main::lxdebug->enter_sub();
605
606   my ( $self, $myconfig, $form ) = @_;
607
608   # connect to database
609   my $dbh = $form->dbconnect($myconfig);
610
611   my $cv = $form->{db} eq "customer" ? "customer" : "vendor";
612   my $join_records = $form->{l_invnumber} || $form->{l_ordnumber} || $form->{l_quonumber};
613
614   my $where = "1 = 1";
615   my @values;
616
617   my %allowed_sort_columns = (
618       "id" => "id",
619       "customernumber" => "customernumber",
620       "vendornumber" => "vendornumber",
621       "name" => "ct.name",
622       "contact" => "contact",
623       "phone" => "phone",
624       "fax" => "fax",
625       "email" => "email",
626       "street" => "street",
627       "taxnumber" => "taxnumber",
628       "business" => "business",
629       "invnumber" => "invnumber",
630       "ordnumber" => "ordnumber",
631       "quonumber" => "quonumber",
632       "zipcode" => "zipcode",
633       "city" => "city",
634       "country" => "country",
635       "salesman" => "e.name"
636     );
637
638   $form->{sort} ||= "name";
639   my $sortorder;
640   if ( $join_records ) {
641     # in UNION case order by hash key, e.g. salesman
642     # the UNION created an implicit select around the result
643     $sortorder = $allowed_sort_columns{$form->{sort}} ? $form->{sort} : "name";
644   } else {
645     # in not UNION case order by hash value, e.g. e.name
646     $sortorder = $allowed_sort_columns{$form->{sort}} ?  $allowed_sort_columns{$form->{sort}} : "ct.name";
647   }
648   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
649
650   if ($sortorder !~ /(business|id)/ && !$join_records) {
651     $sortorder  = "lower($sortorder) ${sortdir}";
652   } else {
653     $sortorder .= " ${sortdir}";
654   }
655
656   if ($form->{"${cv}number"}) {
657     $where .= " AND ct.${cv}number ILIKE ?";
658     push(@values, '%' . $form->{"${cv}number"} . '%');
659   }
660
661   foreach my $key (qw(name contact email)) {
662     if ($form->{$key}) {
663       $where .= " AND ct.$key ILIKE ?";
664       push(@values, '%' . $form->{$key} . '%');
665     }
666   }
667
668   if ($form->{cp_name}) {
669     $where .= " AND ct.id IN (SELECT cp_cv_id FROM contacts WHERE lower(cp_name) LIKE lower(?))";
670     push @values, '%' . $form->{cp_name} . '%';
671   }
672
673   if ($form->{addr_city}) {
674     $where .= " AND ((lower(ct.city) LIKE lower(?))
675                      OR
676                      (ct.id IN (
677                         SELECT trans_id
678                         FROM shipto
679                         WHERE (module = 'CT')
680                           AND (lower(shiptocity) LIKE lower(?))
681                       ))
682                      )";
683     push @values, ('%' . $form->{addr_city} . '%') x 2;
684   }
685
686   if ($form->{addr_country}) {
687     $where .= " AND ((lower(ct.country) LIKE lower(?))
688                      OR
689                      (ct.id IN (
690                         SELECT trans_id
691                         FROM shipto
692                         WHERE (module = 'CT')
693                           AND (lower(shiptocountry) LIKE lower(?))
694                       ))
695                      )";
696     push @values, ('%' . $form->{addr_country} . '%') x 2;
697   }
698
699   if ( $form->{status} eq 'orphaned' ) {
700     $where .=
701       qq| AND ct.id NOT IN | .
702       qq|   (SELECT o.${cv}_id FROM oe o, $cv cv WHERE cv.id = o.${cv}_id)|;
703     if ($cv eq 'customer') {
704       $where .=
705         qq| AND ct.id NOT IN | .
706         qq| (SELECT a.customer_id FROM ar a, customer cv | .
707         qq|  WHERE cv.id = a.customer_id)|;
708     }
709     if ($cv eq 'vendor') {
710       $where .=
711         qq| AND ct.id NOT IN | .
712         qq| (SELECT a.vendor_id FROM ap a, vendor cv | .
713         qq|  WHERE cv.id = a.vendor_id)|;
714     }
715     $form->{l_invnumber} = $form->{l_ordnumber} = $form->{l_quonumber} = "";
716   }
717
718   if ($form->{obsolete} eq "Y") {
719     $where .= qq| AND obsolete|;
720   } elsif ($form->{obsolete} eq "N") {
721     $where .= qq| AND NOT obsolete|;
722   }
723
724   if ($form->{business_id}) {
725     $where .= qq| AND (business_id = ?)|;
726     push(@values, conv_i($form->{business_id}));
727   }
728
729   # Nur Kunden finden, bei denen ich selber der Verkäufer bin
730   # Gilt nicht für Lieferanten
731   if ($cv eq 'customer' &&   !$main::auth->assert('customer_vendor_all_edit', 1)) {
732     $where .= qq| AND ct.salesman_id = (select id from employee where login= ?)|;
733     push(@values, $form->{login});
734   }
735
736   my ($cvar_where, @cvar_values) = CVar->build_filter_query('module'         => 'CT',
737                                                             'trans_id_field' => 'ct.id',
738                                                             'filter'         => $form);
739
740   if ($cvar_where) {
741     $where .= qq| AND ($cvar_where)|;
742     push @values, @cvar_values;
743   }
744
745   if ($form->{addr_street}) {
746     $where .= qq| AND (street ILIKE ?)|;
747     push @values, '%' . $form->{addr_street} . '%';
748   }
749
750   if ($form->{addr_zipcode}) {
751     $where .= qq| AND (zipcode ILIKE ?)|;
752     push @values, $form->{addr_zipcode} . '%';
753   }
754
755   my $query =
756     qq|SELECT ct.*, b.description AS business, e.name as salesman | .
757     (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) .
758     qq|FROM $cv ct | .
759     qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
760     qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
761     qq|WHERE $where|;
762
763   my @saved_values = @values;
764   # redo for invoices, orders and quotations
765   if ($join_records) {
766     my $union = "UNION";
767
768     if ($form->{l_invnumber}) {
769       my $ar = $cv eq 'customer' ? 'ar' : 'ap';
770       my $module = $ar eq 'ar' ? 'is' : 'ir';
771       push(@values, @saved_values);
772       $query .=
773         qq| UNION | .
774         qq|SELECT ct.*, b.description AS business, e.name as salesman, | .
775         qq|  a.invnumber, a.ordnumber, a.quonumber, a.id AS invid, | .
776         qq|  '$module' AS module, 'invoice' AS formtype, | .
777         qq|  (a.amount = a.paid) AS closed | .
778         qq|FROM $cv ct | .
779         qq|JOIN $ar a ON (a.${cv}_id = ct.id) | .
780         qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
781         qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
782         qq|WHERE $where AND (a.invoice = '1')|;
783     }
784
785     if ( $form->{l_ordnumber} ) {
786       push(@values, @saved_values);
787       $query .=
788         qq| UNION | .
789         qq|SELECT ct.*, b.description AS business, e.name as salesman, | .
790         qq|  ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
791         qq|  'oe' AS module, 'order' AS formtype, o.closed | .
792         qq|FROM $cv ct | .
793         qq|JOIN oe o ON (o.${cv}_id = ct.id) | .
794         qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
795         qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
796         qq|WHERE $where AND (o.quotation = '0')|;
797     }
798
799     if ( $form->{l_quonumber} ) {
800       push(@values, @saved_values);
801       $query .=
802         qq| UNION | .
803         qq|SELECT ct.*, b.description AS business, e.name as salesman, | .
804         qq|  ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
805         qq|  'oe' AS module, 'quotation' AS formtype, o.closed | .
806         qq|FROM $cv ct | .
807         qq|JOIN oe o ON (o.${cv}_id = ct.id) | .
808         qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
809         qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
810         qq|WHERE $where AND (o.quotation = '1')|;
811     }
812   }
813
814   $query .= qq| ORDER BY $sortorder|;
815
816   $form->{CT} = selectall_hashref_query($form, $dbh, $query, @values);
817
818   $main::lxdebug->leave_sub();
819 }
820
821 sub get_contact {
822   $main::lxdebug->enter_sub();
823
824   my ( $self, $myconfig, $form ) = @_;
825
826   die 'Missing argument: cp_id' unless $::form->{cp_id};
827
828   my $dbh   = $form->dbconnect($myconfig);
829   my $query =
830     qq|SELECT * FROM contacts c | .
831     qq|WHERE cp_id = ? ORDER BY cp_id limit 1|;
832   my $sth = prepare_execute_query($form, $dbh, $query, $form->{cp_id});
833   my $ref = $sth->fetchrow_hashref("NAME_lc");
834
835   map { $form->{$_} = $ref->{$_} } keys %$ref;
836
837   $query = qq|SELECT COUNT(cp_id) AS used FROM (
838     SELECT cp_id FROM oe UNION
839     SELECT cp_id FROM ar UNION
840     SELECT cp_id FROM ap UNION
841     SELECT cp_id FROM delivery_orders
842   ) AS cpid WHERE cp_id = ? OR ? = 0|;
843   ($form->{cp_used}) = selectfirst_array_query($form, $dbh, $query, ($form->{cp_id})x2);
844
845   $sth->finish;
846   $dbh->disconnect;
847
848   $main::lxdebug->leave_sub();
849 }
850
851 sub get_shipto {
852   $main::lxdebug->enter_sub();
853
854   my ( $self, $myconfig, $form ) = @_;
855   my $dbh   = $form->dbconnect($myconfig);
856   my $query = qq|SELECT * FROM shipto WHERE shipto_id = ?|;
857   my $sth = prepare_execute_query($form, $dbh, $query, $form->{shipto_id});
858
859   my $ref = $sth->fetchrow_hashref("NAME_lc");
860
861   map { $form->{$_} = $ref->{$_} } keys %$ref;
862
863   $query = qq|SELECT COUNT(shipto_id) AS used FROM (
864     SELECT shipto_id FROM oe UNION
865     SELECT shipto_id FROM ar UNION
866     SELECT shipto_id FROM delivery_orders
867   ) AS stid WHERE shipto_id = ? OR ? = 0|;
868   ($form->{shiptoused}) = selectfirst_array_query($form, $dbh, $query, ($form->{shipto_id})x2);
869
870   $sth->finish;
871   $dbh->disconnect;
872
873   $main::lxdebug->leave_sub();
874 }
875
876 sub get_delivery {
877   $main::lxdebug->enter_sub();
878
879   my ( $self, $myconfig, $form ) = @_;
880   my $dbh = $form->dbconnect($myconfig);
881
882   my $arap = $form->{db} eq "vendor" ? "ap" : "ar";
883   my $db = $form->{db} eq "customer" ? "customer" : "vendor";
884   my $qty_sign = $form->{db} eq 'vendor' ? ' * -1 AS qty' : '';
885
886   my $where = " WHERE 1=1 ";
887   my @values;
888
889   if ($form->{shipto_id} && ($arap eq "ar")) {
890     $where .= "AND ${arap}.shipto_id = ?";
891     push(@values, $form->{shipto_id});
892   } else {
893     $where .= "AND ${arap}.${db}_id = ?";
894     push(@values, $form->{id});
895   }
896
897   if ($form->{from}) {
898     $where .= "AND ${arap}.transdate >= ?";
899     push(@values, conv_date($form->{from}));
900   }
901   if ($form->{to}) {
902     $where .= "AND ${arap}.transdate <= ?";
903     push(@values, conv_date($form->{to}));
904   }
905   my $query =
906     qq|SELECT s.shiptoname, i.qty $qty_sign, | .
907     qq|  ${arap}.id, ${arap}.transdate, ${arap}.invnumber, ${arap}.ordnumber, | .
908     qq|  i.description, i.unit, i.sellprice, | .
909     qq|  oe.id AS oe_id, invoice | .
910     qq|FROM $arap | .
911     qq|LEFT JOIN shipto s ON | .
912     ($arap eq "ar"
913      ? qq|(ar.shipto_id = s.shipto_id) |
914      : qq|(ap.id = s.trans_id) |) .
915     qq|LEFT JOIN invoice i ON (${arap}.id = i.trans_id) | .
916     qq|LEFT join parts p ON (p.id = i.parts_id) | .
917     qq|LEFT JOIN oe ON (oe.ordnumber = ${arap}.ordnumber AND NOT ${arap}.ordnumber = '') | .
918     $where .
919     qq|ORDER BY ${arap}.transdate DESC LIMIT 15|;
920
921   $form->{DELIVERY} = selectall_hashref_query($form, $dbh, $query, @values);
922
923   $dbh->disconnect;
924
925   $main::lxdebug->leave_sub();
926 }
927
928 sub _save_note {
929   $main::lxdebug->enter_sub();
930
931   my $self   = shift;
932   my %params = @_;
933
934   my $form   = $main::form;
935
936   Common::check_params(\%params, 'dbh');
937
938   if (!$form->{NOTE_subject}) {
939     $main::lxdebug->leave_sub();
940     return;
941   }
942
943   my $dbh = $params{dbh};
944
945   my %follow_up;
946   my %note = (
947     'id'           => $form->{NOTE_id},
948     'subject'      => $form->{NOTE_subject},
949     'body'         => $form->{NOTE_body},
950     'trans_id'     => $form->{id},
951     'trans_module' => 'ct',
952   );
953
954   $note{id} = Notes->save(%note);
955
956   if ($form->{FU_date}) {
957     %follow_up = (
958       'id'               => $form->{FU_id},
959       'note_id'          => $note{id},
960       'follow_up_date'   => $form->{FU_date},
961       'created_for_user' => $form->{FU_created_for_user},
962       'done'             => $form->{FU_done} ? 1 : 0,
963       'subject'          => $form->{NOTE_subject},
964       'body'             => $form->{NOTE_body},
965       'LINKS'            => [
966         {
967           'trans_id'     => $form->{id},
968           'trans_type'   => $form->{db} eq 'customer' ? 'customer' : 'vendor',
969           'trans_info'   => $form->{name},
970         },
971       ],
972     );
973
974     $follow_up{id} = FU->save(%follow_up);
975
976   } elsif ($form->{FU_id}) {
977     do_query($form, $dbh, qq|DELETE FROM follow_up_links WHERE follow_up_id = ?|, conv_i($form->{FU_id}));
978     do_query($form, $dbh, qq|DELETE FROM follow_ups      WHERE id = ?|,           conv_i($form->{FU_id}));
979   }
980
981   delete @{$form}{grep { /^NOTE_|^FU_/ } keys %{ $form }};
982
983   $main::lxdebug->leave_sub();
984 }
985
986 sub _delete_selected_notes {
987   $main::lxdebug->enter_sub();
988
989   my $self   = shift;
990   my %params = @_;
991
992   Common::check_params(\%params, 'dbh');
993
994   my $form = $main::form;
995   my $dbh  = $params{dbh};
996
997   foreach my $i (1 .. $form->{NOTES_rowcount}) {
998     next unless ($form->{"NOTE_delete_$i"} && $form->{"NOTE_id_$i"});
999
1000     Notes->delete('dbh' => $params{dbh},
1001                   'id'  => $form->{"NOTE_id_$i"});
1002   }
1003
1004   $main::lxdebug->leave_sub();
1005 }
1006
1007 # TODO: remove in 2.7.0 stable
1008 sub delete_shipto {
1009   $main::lxdebug->enter_sub();
1010
1011   my $self      = shift;
1012   my $shipto_id = shift;
1013
1014   my $form      = $main::form;
1015   my %myconfig  = %main::myconfig;
1016   my $dbh       = $form->get_standard_dbh(\%myconfig);
1017
1018   do_query($form, $dbh, qq|UPDATE shipto SET trans_id = NULL WHERE shipto_id = ?|, $shipto_id);
1019
1020   $dbh->commit();
1021
1022   $main::lxdebug->leave_sub();
1023 }
1024
1025 # TODO: remove in 2.7.0 stable
1026 sub delete_contact {
1027   $main::lxdebug->enter_sub();
1028
1029   my $self      = shift;
1030   my $cp_id     = shift;
1031
1032   my $form      = $main::form;
1033   my %myconfig  = %main::myconfig;
1034   my $dbh       = $form->get_standard_dbh(\%myconfig);
1035
1036   do_query($form, $dbh, qq|UPDATE contacts SET cp_cv_id = NULL WHERE cp_id = ?|, $cp_id);
1037
1038   $dbh->commit();
1039
1040   $main::lxdebug->leave_sub();
1041 }
1042
1043 sub get_bank_info {
1044   $main::lxdebug->enter_sub();
1045
1046   my $self     = shift;
1047   my %params   = @_;
1048
1049   Common::check_params(\%params, qw(vc id));
1050
1051   my $myconfig = \%main::myconfig;
1052   my $form     = $main::form;
1053
1054   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
1055
1056   my $table        = $params{vc} eq 'customer' ? 'customer' : 'vendor';
1057   my @ids          = ref $params{id} eq 'ARRAY' ? @{ $params{id} } : ($params{id});
1058   my $placeholders = join ", ", ('?') x scalar @ids;
1059   my $query        = qq|SELECT id, name, account_number, bank, bank_code, iban, bic
1060                         FROM ${table}
1061                         WHERE id IN (${placeholders})|;
1062
1063   my $result       = selectall_hashref_query($form, $dbh, $query, map { conv_i($_) } @ids);
1064
1065   if (ref $params{id} eq 'ARRAY') {
1066     $result = { map { $_->{id} => $_ } @{ $result } };
1067   } else {
1068     $result = $result->[0] || { 'id' => $params{id} };
1069   }
1070
1071   $main::lxdebug->leave_sub();
1072
1073   return $result;
1074 }
1075
1076 sub parse_excel_file {
1077   $main::lxdebug->enter_sub();
1078
1079   my ($self, $myconfig, $form) = @_;
1080   my $locale = $main::locale;
1081
1082   $form->{formname}   = 'sales_quotation';
1083   $form->{type}   = 'sales_quotation';
1084   $form->{format} = 'excel';
1085   $form->{media}  = 'screen';
1086   $form->{quonumber} = 1;
1087
1088
1089   # $form->{"notes"} will be overridden by the customer's/vendor's "notes" field. So save it here.
1090   $form->{ $form->{"formname"} . "notes" } = $form->{"notes"};
1091
1092   my $inv                  = "quo";
1093   my $due                  = "req";
1094   $form->{"${inv}date"} = $form->{transdate};
1095   $form->{label}        = $locale->text('Quotation');
1096   my $numberfld            = "sqnumber";
1097   my $order                = 1;
1098
1099   # assign number
1100   $form->{what_done} = $form->{formname};
1101
1102   map({ delete($form->{$_}); } grep(/^cp_/, keys(%{ $form })));
1103
1104   my $output_dateformat = $myconfig->{"dateformat"};
1105   my $output_numberformat = $myconfig->{"numberformat"};
1106   my $output_longdates = 1;
1107
1108   # map login user variables
1109   map { $form->{"login_$_"} = $myconfig->{$_} } ("name", "email", "fax", "tel", "company");
1110
1111   # format item dates
1112   for my $field (qw(transdate_oe deliverydate_oe)) {
1113     map {
1114       $form->{$field}[$_] = $locale->date($myconfig, $form->{$field}[$_], 1);
1115     } 0 .. $#{ $form->{$field} };
1116   }
1117
1118   if ($form->{shipto_id}) {
1119     $form->get_shipto($myconfig);
1120   }
1121
1122   $form->{notes} =~ s/^\s+//g;
1123
1124   $form->{templates} = $myconfig->{templates};
1125
1126   delete $form->{printer_command};
1127
1128   $form->get_employee_info($myconfig);
1129
1130   my ($cvar_date_fields, $cvar_number_fields) = CVar->get_field_format_list('module' => 'CT', 'prefix' => 'vc_');
1131
1132   if (scalar @{ $cvar_date_fields }) {
1133     format_dates($output_dateformat, $output_longdates, @{ $cvar_date_fields });
1134   }
1135
1136   while (my ($precision, $field_list) = each %{ $cvar_number_fields }) {
1137     reformat_numbers($output_numberformat, $precision, @{ $field_list });
1138   }
1139
1140   $form->{excel} = 1;
1141   my $extension            = 'xls';
1142
1143   $form->{IN}         = "$form->{formname}.${extension}";
1144
1145   delete $form->{OUT};
1146
1147   $form->parse_template($myconfig);
1148
1149   $main::lxdebug->leave_sub();
1150 }
1151
1152 sub search_contacts {
1153   $::lxdebug->enter_sub;
1154
1155   my $self      = shift;
1156   my %params    = @_;
1157
1158   my $dbh       = $params{dbh} || $::form->get_standard_dbh;
1159   my $vc        = $params{db} eq 'customer' ? 'customer' : 'vendor';
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;