1 #=====================================================================
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
7 #=====================================================================
8 # SQL-Ledger Accounting
11 # Author: Dieter Simader
12 # Email: dsimader@sql-ledger.org
13 # Web: http://www.sql-ledger.org
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.
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., 51 Franklin Street, Fifth Floor, Boston,
30 #======================================================================
32 # backend code for customers and vendors
35 # DS. 2000-07-04 Created
37 #======================================================================
45 use SL::Util qw(trim);
51 $main::lxdebug->enter_sub();
53 my ( $self, $myconfig, $form ) = @_;
56 my $dbh = $form->dbconnect($myconfig);
58 my $cv = $form->{db} eq "customer" ? "customer" : "vendor";
59 my $join_records = $form->{l_invnumber} || $form->{l_ordnumber} || $form->{l_quonumber};
64 my %allowed_sort_columns = (
66 "customernumber" => "ct.customernumber",
67 "vendornumber" => "ct.vendornumber",
69 "department_1" => "ct.department_1",
70 "department_2" => "ct.department_2",
71 "contact" => "ct.contact",
72 "phone" => "ct.phone",
74 "email" => "ct.email",
75 "street" => "ct.street",
76 "taxnumber" => "ct.taxnumber",
77 "business" => "b.description",
78 "invnumber" => "ct.invnumber",
79 "ordnumber" => "ct.ordnumber",
80 "quonumber" => "ct.quonumber",
81 "zipcode" => "ct.zipcode",
83 "country" => "ct.country",
85 "discount" => "ct.discount",
86 "insertdate" => "ct.itime",
87 "salesman" => "e.name",
88 "payment" => "pt.description",
89 "taxzone" => "tz.description",
90 "pricegroup" => "pg.pricegroup",
91 "ustid" => "ct.ustid",
92 "creditlimit" => "ct.creditlimit",
93 "commercial_court" => "ct.commercial_court",
94 "dunning_lock" => "ct.dunning_lock",
97 $form->{sort} ||= "name";
99 if ( $join_records ) {
100 # in UNION case order by hash key, e.g. salesman
101 # the UNION created an implicit select around the result
102 $sortorder = $allowed_sort_columns{$form->{sort}} ? $form->{sort} : "name";
104 # in not UNION case order by hash value, e.g. e.name
105 $sortorder = $allowed_sort_columns{$form->{sort}} ? $allowed_sort_columns{$form->{sort}} : "ct.name";
107 my $sortdir = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
109 if ($sortorder !~ /(business|creditlimit|id|discount|itime|dunning_lock)/ && !$join_records) {
110 $sortorder = "lower($sortorder) ${sortdir}";
112 $sortorder .= " ${sortdir}";
115 if ($form->{"${cv}number"}) {
116 $where .= " AND ct.${cv}number ILIKE ?";
117 push(@values, like($form->{"${cv}number"}));
120 foreach my $key (qw(name department_1 department_2 contact email)) {
121 if ($form->{$key} ne '') {
122 $where .= " AND ct.$key ILIKE ?";
123 push(@values, like($form->{$key}));
127 if ($form->{cp_name}) {
128 $where .= " AND ct.id IN (SELECT cp_cv_id FROM contacts WHERE lower(cp_name) LIKE lower(?))";
129 push @values, like($form->{cp_name});
132 if ($form->{addr_street}) {
133 $where .= qq| AND ((ct.street ILIKE ?) | .
136 qq| SELECT sc.trans_id FROM shipto sc | .
137 qq| WHERE (sc.module = 'CT') | .
138 qq| AND (sc.shiptostreet ILIKE ?) | .
140 push @values, (like($form->{addr_street})) x 2;
143 if ($form->{addr_zipcode}) {
144 $where .= qq| AND ((ct.zipcode ILIKE ?) | .
147 qq| SELECT sc.trans_id FROM shipto sc | .
148 qq| WHERE (sc.module = 'CT') | .
149 qq| AND (sc.shiptozipcode ILIKE ?) | .
151 push @values, (like($form->{addr_zipcode})) x 2;
154 if ($form->{addr_city}) {
155 $where .= " AND ((lower(ct.city) LIKE lower(?))
160 WHERE (sc.module = 'CT')
161 AND (lower(sc.shiptocity) LIKE lower(?))
164 push @values, (like($form->{addr_city})) x 2;
167 if ($form->{addr_country}) {
168 $where .= " AND ((lower(ct.country) LIKE lower(?))
173 WHERE (so.module = 'CT')
174 AND (lower(so.shiptocountry) LIKE lower(?))
177 push @values, (like($form->{addr_country})) x 2;
180 if ($form->{addr_gln}) {
181 $where .= " AND ((lower(ct.gln) LIKE lower(?))
186 WHERE (so.module = 'CT')
187 AND (lower(so.shiptogln) LIKE lower(?))
190 push @values, (like($form->{addr_gln})) x 2;
193 if ( $form->{status} eq 'orphaned' ) {
195 qq| AND ct.id NOT IN | .
196 qq| (SELECT o.${cv}_id FROM oe o, $cv cv WHERE cv.id = o.${cv}_id)|;
197 if ($cv eq 'customer') {
199 qq| AND ct.id NOT IN | .
200 qq| (SELECT a.customer_id FROM ar a, customer cv | .
201 qq| WHERE cv.id = a.customer_id)|;
203 if ($cv eq 'vendor') {
205 qq| AND ct.id NOT IN | .
206 qq| (SELECT a.vendor_id FROM ap a, vendor cv | .
207 qq| WHERE cv.id = a.vendor_id)|;
209 $form->{l_invnumber} = $form->{l_ordnumber} = $form->{l_quonumber} = "";
212 if ($form->{obsolete} eq "Y") {
213 $where .= qq| AND ct.obsolete|;
214 } elsif ($form->{obsolete} eq "N") {
215 $where .= qq| AND NOT ct.obsolete|;
218 if ($form->{business_id}) {
219 $where .= qq| AND (ct.business_id = ?)|;
220 push(@values, conv_i($form->{business_id}));
223 if ($form->{salesman_id}) {
224 $where .= qq| AND (ct.salesman_id = ?)|;
225 push(@values, conv_i($form->{salesman_id}));
228 if ($form->{payment_id}) {
229 $where .= qq| AND (ct.payment_id = ?)|;
230 push(@values, $form->{payment_id});
233 if ($form->{taxzone_id}) {
234 $where .= qq| AND (ct.taxzone_id = ?)|;
235 push(@values, $form->{taxzone_id});
238 if($form->{insertdatefrom}) {
239 $where .= qq| AND (ct.itime::DATE >= ?)|;
240 push@values, conv_date($form->{insertdatefrom});
243 if($form->{insertdateto}) {
244 $where .= qq| AND (ct.itime::DATE <= ?)|;
245 push @values, conv_date($form->{insertdateto});
248 if($form->{dunning_lock} ne '') {
249 $where .= qq| AND ct.dunning_lock = ?|;
250 push @values, $form->{dunning_lock};
254 my @tokens = parse_line('\s+', 0, $form->{all});
256 ct.${cv}number ILIKE ? OR
259 push @values, ("%$_%")x2 for @tokens;
262 if (($form->{create_zugferd_invoices} // '') ne '') {
263 $where .= qq| AND (ct.create_zugferd_invoices = ?)|;
264 push @values, $form->{create_zugferd_invoices};
267 if ($form->{all_phonenumbers}) {
268 my $search_term = trim($form->{all_phonenumbers});
269 $search_term =~ s{\p{WSpace}+}{}g;
270 $search_term = join ' *', split(//, $search_term);
272 $where .= qq| AND (ct.phone ~* ? OR
275 (SELECT cp_cv_id FROM contacts
276 WHERE cp_phone1 ~* ? OR
286 push @values, ($search_term)x10;
289 my ($cvar_where, @cvar_values) = CVar->build_filter_query('module' => 'CT',
290 'trans_id_field' => 'ct.id',
294 $where .= qq| AND ($cvar_where)|;
295 push @values, @cvar_values;
298 my $pg_select = $form->{l_pricegroup} ? qq|, pg.pricegroup as pricegroup | : '';
299 my $pg_join = $form->{l_pricegroup} ? qq|LEFT JOIN pricegroup pg ON (ct.pricegroup_id = pg.id) | : '';
301 my $main_cp_select = '';
302 if ($form->{l_main_contact_person}) {
303 $main_cp_select = qq/, (SELECT concat(cp.cp_givenname, ' ', cp.cp_name, ' | ', cp.cp_email, ' | ', cp.cp_phone1)
304 FROM contacts cp WHERE ct.id=cp.cp_cv_id AND cp.cp_main LIMIT 1)
305 AS main_contact_person /;
308 qq|SELECT ct.*, ct.itime::DATE AS insertdate, b.description AS business, e.name as salesman, | .
309 qq| pt.description as payment, tz.description as taxzone | .
312 (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) .
314 qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
315 qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
316 qq|LEFT JOIN payment_terms pt ON (ct.payment_id = pt.id) | .
317 qq|LEFT JOIN tax_zones tz ON (ct.taxzone_id = tz.id) | .
321 my @saved_values = @values;
322 # redo for invoices, orders and quotations
326 if ($form->{l_invnumber}) {
327 my $ar = $cv eq 'customer' ? 'ar' : 'ap';
328 my $module = $ar eq 'ar' ? 'is' : 'ir';
329 push(@values, @saved_values);
332 qq|SELECT ct.*, ct.itime::DATE AS insertdate, b.description AS business, e.name as salesman, | .
333 qq| pt.description as payment, tz.description as taxzone | .
336 qq|, a.invnumber, a.ordnumber, a.quonumber, a.id AS invid, | .
337 qq| '$module' AS module, 'invoice' AS formtype, | .
338 qq| (a.amount = a.paid) AS closed | .
340 qq|JOIN $ar a ON (a.${cv}_id = ct.id) | .
341 qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
342 qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
343 qq|LEFT JOIN payment_terms pt ON (ct.payment_id = pt.id) | .
344 qq|LEFT JOIN tax_zones tz ON (ct.taxzone_id = tz.id) | .
346 qq|WHERE $where AND (a.invoice = '1')|;
349 if ( $form->{l_ordnumber} ) {
350 push(@values, @saved_values);
353 qq|SELECT ct.*, ct.itime::DATE AS insertdate, b.description AS business, e.name as salesman, | .
354 qq| pt.description as payment, tz.description as taxzone | .
357 qq|, ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
358 qq| 'oe' AS module, 'order' AS formtype, o.closed | .
360 qq|JOIN oe o ON (o.${cv}_id = ct.id) | .
361 qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
362 qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
363 qq|LEFT JOIN payment_terms pt ON (ct.payment_id = pt.id) | .
364 qq|LEFT JOIN tax_zones tz ON (ct.taxzone_id = tz.id) | .
366 qq|WHERE $where AND ((o.record_type = 'sales_order') OR (o.record_type = 'purchase_order'))|;
369 if ( $form->{l_quonumber} ) {
370 push(@values, @saved_values);
373 qq|SELECT ct.*, ct.itime::DATE AS insertdate, b.description AS business, e.name as salesman, | .
374 qq| pt.description as payment, tz.description as taxzone | .
377 qq|, ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
378 qq| 'oe' AS module, 'quotation' AS formtype, o.closed | .
380 qq|JOIN oe o ON (o.${cv}_id = ct.id) | .
381 qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
382 qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
383 qq|LEFT JOIN payment_terms pt ON (ct.payment_id = pt.id) | .
384 qq|LEFT JOIN tax_zones tz ON (ct.taxzone_id = tz.id) | .
386 qq|WHERE $where AND ((o.record_type = 'sales_quotation') OR (o.record_type = 'request_quotation'))|;
390 $query .= qq| ORDER BY $sortorder|;
392 $form->{CT} = selectall_hashref_query($form, $dbh, $query, @values);
394 $main::lxdebug->leave_sub();
398 $main::lxdebug->enter_sub();
400 my ( $self, $myconfig, $form ) = @_;
402 die 'Missing argument: cp_id' unless $::form->{cp_id};
404 my $dbh = SL::DB->client->dbh;
406 qq|SELECT * FROM contacts c | .
407 qq|WHERE cp_id = ? ORDER BY cp_id limit 1|;
408 my $sth = prepare_execute_query($form, $dbh, $query, $form->{cp_id});
409 my $ref = $sth->fetchrow_hashref("NAME_lc");
411 map { $form->{$_} = $ref->{$_} } keys %$ref;
413 $query = qq|SELECT COUNT(cp_id) AS used FROM (
414 SELECT cp_id FROM oe UNION
415 SELECT cp_id FROM ar UNION
416 SELECT cp_id FROM ap UNION
417 SELECT cp_id FROM delivery_orders
418 ) AS cpid WHERE cp_id = ? OR ? = 0|;
419 ($form->{cp_used}) = selectfirst_array_query($form, $dbh, $query, ($form->{cp_id})x2);
423 $main::lxdebug->leave_sub();
427 $main::lxdebug->enter_sub();
432 Common::check_params(\%params, qw(vc id));
434 my $myconfig = \%main::myconfig;
435 my $form = $main::form;
437 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
439 my $table = $params{vc} eq 'customer' ? 'customer' : 'vendor';
440 my @ids = ref $params{id} eq 'ARRAY' ? @{ $params{id} } : ($params{id});
441 my $placeholders = join ", ", ('?') x scalar @ids;
442 my $c_mandate = $params{vc} eq 'customer' ? ', mandator_id, mandate_date_of_signature' : '';
443 my $query = qq|SELECT id, name, account_number, bank, bank_code, iban, bic ${c_mandate}
445 WHERE id IN (${placeholders})|;
447 my $result = selectall_hashref_query($form, $dbh, $query, map { conv_i($_) } @ids);
449 if (ref $params{id} eq 'ARRAY') {
450 $result = { map { $_->{id} => $_ } @{ $result } };
452 $result = $result->[0] || { 'id' => $params{id} };
455 $main::lxdebug->leave_sub();
460 sub search_contacts {
461 $::lxdebug->enter_sub;
466 my $dbh = $params{dbh} || $::form->get_standard_dbh;
469 'cp_name' => 'cp_name, cp_givenname',
470 'vcname' => 'vcname, cp_name, cp_givenname',
471 'vcnumber' => 'vcnumber, cp_name, cp_givenname',
474 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);
476 my $order_by = $sortcols{$::form->{sort}} ? $::form->{sort} : 'cp_name';
477 $::form->{sort} = $order_by;
478 $order_by = $sortspecs{$order_by} if ($sortspecs{$order_by});
480 my $sortdir = $::form->{sortdir} ? 'ASC' : 'DESC';
481 $order_by =~ s/,/ ${sortdir},/g;
482 $order_by .= " $sortdir";
484 my @where_tokens = ();
487 if ($params{search_term}) {
490 'cp.cp_name ILIKE ?',
491 'cp.cp_givenname ILIKE ?',
492 'cp.cp_email ILIKE ?';
493 push @values, (like($params{search_term})) x 3;
495 if (($params{search_term} =~ m/\d/) && ($params{search_term} !~ m/[^\d \(\)+\-]/)) {
496 my $number = $params{search_term};
497 $number =~ s/[^\d]//g;
498 $number = join '[ /\(\)+\-]*', split(m//, $number);
500 push @tokens, map { "($_ ~ '$number')" } qw(cp_phone1 cp_phone2 cp_mobile1 cp_mobile2);
503 push @where_tokens, map { "($_)" } join ' OR ', @tokens;
506 my ($cvar_where, @cvar_values) = CVar->build_filter_query('module' => 'Contacts',
507 'trans_id_field' => 'cp.cp_id',
508 'filter' => $params{filter});
511 push @where_tokens, $cvar_where;
512 push @values, @cvar_values;
515 if (my $filter = $params{filter}) {
516 for (qw(name title givenname email project abteilung)) {
517 next unless $filter->{"cp_$_"};
518 add_token(\@where_tokens, \@values, col => "cp.cp_$_", val => $filter->{"cp_$_"}, method => 'ILIKE', esc => 'substr');
521 push @where_tokens, 'cp.cp_cv_id IS NOT NULL' if $filter->{status} eq 'active';
522 push @where_tokens, 'cp.cp_cv_id IS NULL' if $filter->{status} eq 'orphaned';
525 my $where = @where_tokens ? 'WHERE ' . join ' AND ', @where_tokens : '';
527 my $query = qq|SELECT cp.*,
528 COALESCE(c.id, v.id) AS vcid,
529 COALESCE(c.name, v.name) AS vcname,
530 COALESCE(c.customernumber, v.vendornumber) AS vcnumber,
531 CASE WHEN c.name IS NULL THEN 'vendor' ELSE 'customer' END AS db
533 LEFT JOIN customer c ON (cp.cp_cv_id = c.id)
534 LEFT JOIN vendor v ON (cp.cp_cv_id = v.id)
538 my $contacts = selectall_hashref_query($::form, $dbh, $query, @values);
540 $::lxdebug->leave_sub;
542 return @{ $contacts };