epic-ts
[kivitendo-erp.git] / SL / FU.pm
1 # Follow-Ups
2
3 package FU;
4
5 use List::Util qw(first);
6
7 use SL::Common;
8 use SL::DBUtils;
9 use SL::DB;
10 use SL::Notes;
11
12 use strict;
13
14 sub save {
15   my ($self, %params) = @_;
16   $main::lxdebug->enter_sub();
17
18   my $rc = SL::DB->client->with_transaction(\&_save, $self, %params);
19
20   $::lxdebug->leave_sub;
21   return $rc;
22 }
23
24 sub _save {
25   my $self     = shift;
26   my %params   = @_;
27
28   my $myconfig = \%main::myconfig;
29   my $form     = $main::form;
30
31   my $dbh      = $params{dbh} || SL::DB->client->dbh;
32   my ($query, @values);
33
34   if (!$params{id}) {
35     ($params{id}) = selectrow_query($form, $dbh, qq|SELECT nextval('follow_up_id')|);
36
37     $query = qq|INSERT INTO follow_ups (created_by, done, note_id, follow_up_date, created_for_user, id)
38                 VALUES ((SELECT id FROM employee WHERE login = ?), ?, ?, ?, ?, ?)|;
39
40     push @values, $::myconfig{login};
41
42   } else {
43     $query = qq|UPDATE follow_ups SET done = ?, note_id = ?, follow_up_date = ?, created_for_user = ? WHERE id = ?|;
44   }
45
46   $params{note_id} = Notes->save('id'           => $params{note_id},
47                                  'trans_id'     => $params{id},
48                                  'trans_module' => 'fu',
49                                  'subject'      => $params{subject},
50                                  'body'         => $params{body},
51                                  'dbh'          => $dbh,);
52
53   $params{done} = 1 if (!defined $params{done});
54
55   do_query($form, $dbh, $query, @values, $params{done} ? 't' : 'f', conv_i($params{note_id}), $params{follow_up_date}, conv_i($params{created_for_user}), conv_i($params{id}));
56
57   do_query($form, $dbh, qq|DELETE FROM follow_up_links WHERE follow_up_id = ?|, conv_i($params{id}));
58
59   $query = qq|INSERT INTO follow_up_links (follow_up_id, trans_id, trans_type, trans_info) VALUES (?, ?, ?, ?)|;
60   my $sth   = prepare_query($form, $dbh, $query);
61
62   foreach my $link (@{ $params{LINKS} }) {
63     do_statement($form, $sth, $query, conv_i($params{id}), conv_i($link->{trans_id}), $link->{trans_type}, $link->{trans_info});
64   }
65
66   $sth->finish();
67
68   return 1;
69 }
70
71 sub finish {
72   $main::lxdebug->enter_sub();
73
74   my $self     = shift;
75   my %params   = @_;
76
77   Common::check_params(\%params, 'id');
78
79   my $myconfig = \%main::myconfig;
80   my $form     = $main::form;
81
82   SL::DB->client->with_transaction(sub {
83     do_query($form, SL::DB->client->dbh, qq|UPDATE follow_ups SET done = TRUE WHERE id = ?|, conv_i($params{id}));
84     1;
85   }) or do { die SL::DB->client->error };
86
87   $main::lxdebug->leave_sub();
88 }
89
90 sub delete {
91   $main::lxdebug->enter_sub();
92
93   my $self     = shift;
94   my %params   = @_;
95
96   Common::check_params(\%params, 'id');
97
98   my $myconfig = \%main::myconfig;
99   my $form     = $main::form;
100
101   SL::DB->client->with_transaction(sub {
102     my $dbh      = SL::DB->client->dbh;
103
104     my $id       = conv_i($params{id});
105
106     do_query($form, $dbh, qq|DELETE FROM follow_up_links WHERE follow_up_id = ?|,                         $id);
107     do_query($form, $dbh, qq|DELETE FROM follow_ups      WHERE id = ?|,                                   $id);
108     do_query($form, $dbh, qq|DELETE FROM notes           WHERE (trans_id = ?) AND (trans_module = 'fu')|, $id);
109     1;
110   }) or do { die SL::DB->client->error };
111
112   $main::lxdebug->leave_sub();
113 }
114
115 sub retrieve {
116   $main::lxdebug->enter_sub();
117
118   my $self     = shift;
119   my %params   = @_;
120
121   Common::check_params(\%params, 'id');
122
123   my $myconfig = \%main::myconfig;
124   my $form     = $main::form;
125
126   my $dbh      = $form->get_standard_dbh($myconfig);
127   my ($query, @values);
128
129   my ($employee_id) = selectrow_query($form, $dbh, qq|SELECT id FROM employee WHERE login = ?|, $::myconfig{login});
130   $query            = qq|SELECT fu.*, n.subject, n.body, n.created_by
131                          FROM follow_ups fu
132                          LEFT JOIN notes n ON (fu.note_id = n.id)
133                          WHERE (fu.id = ?)
134                            AND (   (fu.created_by = ?) OR (fu.created_for_user = ?)
135                                 OR (fu.created_by IN (SELECT DISTINCT what FROM follow_up_access WHERE who = ?)))|;
136   my $ref           = selectfirst_hashref_query($form, $dbh, $query, conv_i($params{id}), $employee_id, $employee_id, $employee_id);
137
138   if (!$ref) {
139     $main::lxdebug->leave_sub();
140     return undef;
141   }
142
143   $ref->{LINKS} = $self->retrieve_links(%{ $ref });
144
145   $main::lxdebug->leave_sub();
146
147   return $ref;
148 }
149
150 sub retrieve_links {
151   $main::lxdebug->enter_sub();
152
153   my $self     = shift;
154   my %params   = @_;
155
156   Common::check_params(\%params, qw(id));
157
158   my $myconfig = \%main::myconfig;
159   my $form     = $main::form;
160
161   my $dbh      = $form->get_standard_dbh($myconfig);
162
163   my $query    = qq|SELECT ful.trans_id, ful.trans_type, ful.trans_info, fu.note_id
164                     FROM follow_up_links ful
165                     LEFT JOIN follow_ups fu ON (ful.follow_up_id = fu.id)
166                     WHERE ful.follow_up_id = ?
167                     ORDER BY ful.itime|;
168
169   my $links    = selectall_hashref_query($form, $dbh, $query, conv_i($params{id}));
170
171   foreach my $link_ref (@{ $links }) {
172     my $link_details = FU->link_details(%{ $link_ref });
173     map { $link_ref->{$_} = $link_details->{$_} } keys %{ $link_details} if ($link_details);
174   }
175
176   $main::lxdebug->leave_sub();
177
178   return $links;
179 }
180
181 sub follow_ups {
182   $main::lxdebug->enter_sub();
183
184   my $self     = shift;
185   my %params   = @_;
186
187   my $myconfig = \%main::myconfig;
188   my $form     = $main::form;
189
190   my $dbh      = $form->get_standard_dbh($myconfig);
191   my ($query, $where, $where_user);
192
193   my ($employee_id) = selectrow_query($form, $dbh, qq|SELECT id FROM employee WHERE login = ?|, $::myconfig{login});
194   my @values        = ();
195   my @values_user   = ();
196
197   if ($params{trans_id}) {
198     $where .= qq| AND EXISTS (SELECT * FROM follow_up_links ful
199                               WHERE (ful.follow_up_id = fu.id) AND (ful.trans_id = ?))|;
200     push @values, conv_i($params{trans_id});
201   }
202
203   if ($params{due_only}) {
204     $where .= qq| AND (fu.follow_up_date <= current_date)|;
205   }
206
207   if ($params{done} ne $params{not_done}) {
208     my $not  = $params{not_done} ? 'NOT' : '';
209     $where  .= qq| AND $not COALESCE(fu.done, FALSE)|;
210   }
211
212   if ($params{not_id}) {
213     $where .= qq| AND (fu.id <> ?)|;
214     push @values, conv_i($params{not_id});
215   }
216
217   foreach my $item (qw(subject body)) {
218     next unless ($params{$item});
219     $where .= qq| AND (n.${item} ILIKE ?)|;
220     push @values, like($params{$item});
221   }
222
223   if ($params{reference}) {
224     $where .= qq| AND EXISTS (SELECT ful.follow_up_id
225                               FROM follow_up_links ful
226                               WHERE (ful.follow_up_id = fu.id)
227                                 AND (ful.trans_info ILIKE ?)
228                               LIMIT 1)|;
229     push @values, like($params{reference});
230   }
231
232   if ($params{follow_up_date_from}) {
233     $where .= qq| AND (fu.follow_up_date >= ?)|;
234     push @values, conv_date($params{follow_up_date_from});
235   }
236   if ($params{follow_up_date_to}) {
237     $where .= qq| AND (fu.follow_up_date <= ?)|;
238     push @values, conv_date($params{follow_up_date_to});
239   }
240
241   if ($params{itime_from}) {
242     $where .= qq| AND (date_trunc('DAY', fu.itime) >= ?)|;
243     push @values, conv_date($params{itime_from});
244   }
245   if ($params{itime_to}) {
246     $where .= qq| AND (date_trunc('DAY', fu.itime) <= ?)|;
247     push @values, conv_date($params{itime_to});
248   }
249   if ($params{created_for}) {
250     $where .= qq| AND fu.created_for_user = ?|;
251     push @values, conv_i($params{created_for});
252   }
253
254   if ($params{all_users}) {
255     $where_user = qq|OR (fu.created_by IN (SELECT DISTINCT what FROM follow_up_access WHERE who = ?))|;
256     push @values_user, $employee_id;
257   }
258
259   my $order_by = '';
260
261   if ($form->{sort} ne 'title') {
262     my %sort_columns = (
263       'follow_up_date' => [ qw(fu.follow_up_date fu.id) ],
264       'created_on'     => [ qw(created_on fu.id) ],
265       'subject'        => [ qw(lower(n.subject)) ],
266       );
267
268     my $sortdir = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
269     my $sortkey = $sort_columns{$form->{sort}} ? $form->{sort} : 'follow_up_date';
270     $order_by   = 'ORDER BY ' . join(', ', map { "$_ $sortdir" } @{ $sort_columns{$sortkey} });
271   }
272
273   $query  = qq|SELECT fu.*, n.subject, n.body, n.created_by,
274                  fu.follow_up_date <= current_date AS due,
275                  fu.itime::DATE                    AS created_on,
276                  COALESCE(eby.name,  eby.login)    AS created_by_name,
277                  COALESCE(efor.name, efor.login)   AS created_for_user_name
278                FROM follow_ups fu
279                LEFT JOIN notes    n    ON (fu.note_id          = n.id)
280                LEFT JOIN employee eby  ON (n.created_by        = eby.id)
281                LEFT JOIN employee efor ON (fu.created_for_user = efor.id)
282                WHERE ((fu.created_by = ?) OR (fu.created_for_user = ?)
283                       $where_user)
284                  $where
285                $order_by|;
286
287   my $follow_ups = selectall_hashref_query($form, $dbh, $query, $employee_id, $employee_id, @values_user, @values);
288
289   if (!scalar @{ $follow_ups }) {
290     $main::lxdebug->leave_sub();
291     return $follow_ups;
292   }
293
294   foreach my $fu (@{ $follow_ups }) {
295     $fu->{LINKS} = $self->retrieve_links(%{ $fu });
296   }
297
298   if ($form->{sort} eq 'title') {
299     my $dir_factor = !defined $form->{sortdir} ? 1 : $form->{sortdir} ? 1 : -1;
300     $follow_ups    = [ map  { $_->[1] }
301                        sort { ($a->[0] cmp $b->[0]) * $dir_factor }
302                        map  { my $fu = $follow_ups->[$_]; [ @{ $fu->{LINKS} } ? lc($fu->{LINKS}->[0]->{title}) : '', $fu ] }
303                        (0 .. scalar(@{ $follow_ups }) - 1) ];
304   }
305
306   $main::lxdebug->leave_sub();
307
308   return $follow_ups;
309 }
310
311 sub link_details {
312   $main::lxdebug->enter_sub();
313
314   my $self     = shift;
315   my %params   = @_;
316
317   Common::check_params(\%params, qw(trans_id trans_type));
318
319   my $myconfig = \%main::myconfig;
320   my $form     = $main::form;
321   my $locale   = $main::locale;
322
323   my $q_id     = $form->quote($params{trans_id});
324   my $link;
325
326   if ($params{trans_type} eq 'customer') {
327     $link = {
328       'url'   => 'controller.pl?action=CustomerVendor/edit&db=customer&id=' . $form->quote($params{trans_id}) . '&note_id=' . $form->quote($params{note_id}),
329       'title' => $locale->text('Customer') . " '$params{trans_info}'",
330     };
331
332   } elsif ($params{trans_type} eq 'vendor') {
333     $link = {
334       'url'   => 'controller.pl?action=CustomerVendor/edit&db=vendor&id=' . $params{trans_id} . '&note_id=' . $form->quote($params{note_id}),
335       'title' => $locale->text('Vendor') . " '$params{trans_info}'",
336     };
337
338   } elsif ($params{trans_type} eq 'sales_quotation') {
339     my $script = 'oe.pl';
340     my $action = 'edit';
341     if ($::instance_conf->get_feature_experimental_order) {
342       $script = 'controller.pl';
343       $action = 'Order/edit';
344     }
345     $link = {
346       'url'   => $script . '?action=' . $action . '&type=sales_quotation&id=' . $params{trans_id},
347       'title' => $locale->text('Sales quotation') . " $params{trans_info}",
348     };
349
350   } elsif ($params{trans_type} eq 'sales_delivery_order') {
351
352     $link = {
353       'url'   => 'do.pl?action=edit&type=sales_delivery_order&id=' . $params{trans_id} . '&edit_note_id=' . $form->quote($params{note_id}),
354       'title' => $locale->text('Sales delivery order') .' '. $params{trans_info},
355     };
356
357   } elsif ($params{trans_type} eq 'purchase_delivery_order') {
358
359     $link = {
360       'url'   => 'do.pl?action=edit&type=purchase_delivery_order&id=' . $params{trans_id} . '&edit_note_id=' . $form->quote($params{note_id}),
361       'title' => $locale->text('Purchase delivery order') .' '. $params{trans_info},
362     };
363
364   } elsif ($params{trans_type} eq 'sales_order') {
365     my $script = 'oe.pl';
366     my $action = 'edit';
367     if ($::instance_conf->get_feature_experimental_order) {
368       $script = 'controller.pl';
369       $action = 'Order/edit';
370     }
371     $link = {
372       'url'   => $script . '?action=' . $action . '&type=sales_order&id=' . $params{trans_id},
373       'title' => $locale->text('Sales Order') . " $params{trans_info}",
374     };
375
376   } elsif ($params{trans_type} eq 'sales_invoice') {
377     $link = {
378       'url'   => 'is.pl?action=edit&type=invoice&id=' . $params{trans_id},
379       'title' => $locale->text('Sales Invoice') . " $params{trans_info}",
380     };
381
382   } elsif ($params{trans_type} eq 'purchase_invoice') {
383     $link = {
384       'url'   => 'ir.pl?action=edit&type=purchase_invoice&id=' . $params{trans_id},
385       'title' => $locale->text('Purchase Invoice') . " $params{trans_info}",
386     };
387
388   } elsif ($params{trans_type} eq 'credit_note') {
389     $link = {
390       'url'   => 'is.pl?action=edit&type=credit_note&id=' . $params{trans_id},
391       'title' => $locale->text('Credit Note') . " $params{trans_info}",
392     };
393
394   } elsif ($params{trans_type} eq 'dunning') {
395     $link = {
396       'url'   => 'dn.pl?action=print_dunning&format=pdf&media=screen&dunning_id=' . $params{trans_id},
397       'title' => $locale->text('Dunning') . " $params{trans_info}",
398     };
399
400   } elsif ($params{trans_type} eq 'request_quotation') {
401     my $script = 'oe.pl';
402     my $action = 'edit';
403     if ($::instance_conf->get_feature_experimental_order) {
404       $script = 'controller.pl';
405       $action = 'Order/edit';
406     }
407     $link = {
408       'url'   => $script . '?action=' . $action . '&type=request_quotation&id=' . $params{trans_id},
409       'title' => $locale->text('Request quotation') . " $params{trans_info}",
410     };
411
412   } elsif ($params{trans_type} eq 'purchase_order') {
413     my $script = 'oe.pl';
414     my $action = 'edit';
415     if ($::instance_conf->get_feature_experimental_order) {
416       $script = 'controller.pl';
417       $action = 'Order/edit';
418     }
419     $link = {
420       'url'   => $script . '?action=' . $action . '&type=purchase_order&id=' . $params{trans_id},
421       'title' => $locale->text('Purchase Order') . " $params{trans_info}",
422     };
423
424   } elsif ($params{trans_type} eq 'vendor_invoice') {
425     $link = {
426       'url'   => 'ir.pl?action=edit&type=invoice&id=' . $params{trans_id},
427       'title' => $locale->text('Vendor Invoice') . " $params{trans_info}",
428     };
429
430   } elsif ($params{trans_type} eq 'ar_transaction') {
431     $link = {
432       'url'   => 'ar.pl?action=edit&id=' . $params{trans_id},
433       'title' => $locale->text('AR Transaction') . " $params{trans_info}",
434     };
435
436   } elsif ($params{trans_type} eq 'ap_transaction') {
437     $link = {
438       'url'   => 'ap.pl?action=edit&id=' . $params{trans_id},
439       'title' => $locale->text('AP Transaction') . " $params{trans_info}",
440     };
441
442   } elsif ($params{trans_type} eq 'gl_transaction') {
443     $link = {
444       'url'   => 'gl.pl?action=edit&id=' . $params{trans_id},
445       'title' => $locale->text('GL Transaction') . " $params{trans_info}",
446     };
447
448   }
449
450   $main::lxdebug->leave_sub();
451
452   return $link || { };
453 }
454
455 sub save_access_rights {
456   $main::lxdebug->enter_sub();
457
458   my $self     = shift;
459   my %params   = @_;
460
461   Common::check_params(\%params, 'access');
462
463   my $myconfig = \%main::myconfig;
464   my $form     = $main::form;
465
466   SL::DB->client->with_transaction(sub {
467     my $dbh      = SL::DB->client->dbh;
468
469     my ($id)     = selectrow_query($form, $dbh, qq|SELECT id FROM employee WHERE login = ?|, $::myconfig{login});
470
471     do_query($form, $dbh, qq|DELETE FROM follow_up_access WHERE what = ?|, $id);
472
473     my $query    = qq|INSERT INTO follow_up_access (who, what) VALUES (?, ?)|;
474     my $sth      = prepare_query($form, $dbh, $query);
475
476     while (my ($who, $access_allowed) = each %{ $params{access} }) {
477       next unless ($access_allowed);
478
479       do_statement($form, $sth, $query, conv_i($who), $id);
480     }
481
482     $sth->finish();
483     1;
484   }) or do { die SL::DB->client->error };
485
486   $main::lxdebug->leave_sub();
487 }
488
489 sub retrieve_access_rights {
490   $main::lxdebug->enter_sub();
491
492   my $self     = shift;
493   my %params   = @_;
494
495   my $myconfig = \%main::myconfig;
496   my $form     = $main::form;
497
498   my $dbh      = $form->get_standard_dbh($myconfig);
499
500   my $sth      = prepare_execute_query($form, $dbh, qq|SELECT who FROM follow_up_access WHERE what = (SELECT id FROM employee WHERE login = ?)|, $::myconfig{login});
501   my $access   = {};
502
503   while (my $ref = $sth->fetchrow_hashref()) {
504     $access->{$ref->{who}} = 1;
505   }
506
507   $sth->finish();
508
509   $main::lxdebug->leave_sub();
510
511   return $access;
512 }
513
514 1;