Merge branch 'karushka'
[kivitendo-erp.git] / SL / CVar.pm
1 package CVar;
2
3 use List::Util qw(first);
4
5 use SL::DBUtils;
6
7 sub get_configs {
8   $main::lxdebug->enter_sub();
9
10   my $self     = shift;
11   my %params   = @_;
12
13   my $myconfig = \%main::myconfig;
14   my $form     = $main::form;
15
16   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
17
18   my ($where, @values);
19   if ($params{module}) {
20     $where = 'WHERE module = ?';
21     push @values, $params{module};
22   }
23
24   my $query    = qq|SELECT * FROM custom_variable_configs $where ORDER BY sortkey|;
25
26   my $configs  = selectall_hashref_query($form, $dbh, $query, @values);
27
28   foreach my $config (@{ $configs }) {
29     if ($config->{type} eq 'select') {
30       $config->{OPTIONS} = [ map { { 'value' => $_ } } split(m/\#\#/, $config->{options}) ];
31
32     } elsif ($config->{type} eq 'number') {
33       $config->{precision} = $1 if ($config->{options} =~ m/precision=(\d+)/i);
34
35     }
36
37     $self->_unpack_flags($config);
38   }
39
40   $main::lxdebug->leave_sub();
41
42   return $configs;
43 }
44
45 sub get_config {
46   $main::lxdebug->enter_sub();
47
48   my $self     = shift;
49   my %params   = @_;
50
51   Common::check_params(\%params, qw(id));
52
53   my $myconfig = \%main::myconfig;
54   my $form     = $main::form;
55
56   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
57
58   my $query    = qq|SELECT * FROM custom_variable_configs WHERE id = ?|;
59
60   my $config   = selectfirst_hashref_query($form, $dbh, $query, conv_i($params{id})) || { };
61
62   $self->_unpack_flags($config);
63
64   $main::lxdebug->leave_sub();
65
66   return $config;
67 }
68
69 sub _unpack_flags {
70   $main::lxdebug->enter_sub();
71
72   my $self   = shift;
73   my $config = shift;
74
75   foreach my $flag (split m/:/, $config->{flags}) {
76     if ($flag =~ m/(.*?)=(.*)/) {
77       $config->{"flag_${1}"}    = $2;
78     } else {
79       $config->{"flag_${flag}"} = 1;
80     }
81   }
82
83   $main::lxdebug->leave_sub();
84 }
85
86 sub save_config {
87   $main::lxdebug->enter_sub();
88
89   my $self     = shift;
90   my %params   = @_;
91
92   Common::check_params(\%params, qw(module config));
93
94   my $myconfig = \%main::myconfig;
95   my $form     = $main::form;
96
97   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
98
99   my $q_id     = qq|SELECT nextval('custom_variable_configs_id')|;
100   my $h_id     = prepare_query($form, $dbh, $q_id);
101
102   my $q_new    =
103     qq|INSERT INTO custom_variable_configs (name, description, type, default_value, options, searchable, includeable, included_by_default, module, flags, id, sortkey)
104        VALUES                              (?,    ?,           ?,    ?,             ?,       ?,          ?,           ?,                   ?,      ?,     ?,
105          (SELECT COALESCE(MAX(sortkey) + 1, 1) FROM custom_variable_configs))|;
106   my $h_new    = prepare_query($form, $dbh, $q_new);
107
108   my $q_update =
109     qq|UPDATE custom_variable_configs SET
110          name        = ?, description         = ?,
111          type        = ?, default_value       = ?,
112          options     = ?, searchable          = ?,
113          includeable = ?, included_by_default = ?,
114          module      = ?, flags               = ?
115        WHERE id  = ?|;
116   my $h_update = prepare_query($form, $dbh, $q_update);
117
118   my @configs;
119   if ('ARRAY' eq ref $params{config}) {
120     @configs = @{ $params{config} };
121   } else {
122     @configs = ($params{config});
123   }
124
125   foreach my $config (@configs) {
126     my ($h_actual, $q_actual);
127
128     if (!$config->{id}) {
129       do_statement($form, $h_id, $q_id);
130       ($config->{id}) = $h_id->fetchrow_array();
131
132       $h_actual       = $h_new;
133       $q_actual       = $q_new;
134
135     } else {
136       $h_actual       = $h_update;
137       $q_actual       = $q_update;
138     }
139
140     do_statement($form, $h_actual, $q_actual, @{$config}{qw(name description type default_value options)},
141                  $config->{searchable} ? 't' : 'f', $config->{includeable} ? 't' : 'f', $config->{included_by_default} ? 't' : 'f',
142                  $params{module}, $config->{flags}, conv_i($config->{id}));
143   }
144
145   $h_id->finish();
146   $h_new->finish();
147   $h_update->finish();
148
149   $dbh->commit();
150
151   $main::lxdebug->leave_sub();
152 }
153
154 sub delete_config {
155   $main::lxdebug->enter_sub();
156
157   my $self     = shift;
158   my %params   = @_;
159
160   Common::check_params(\%params, qw(id));
161
162   my $myconfig = \%main::myconfig;
163   my $form     = $main::form;
164
165   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
166
167   do_query($form, $dbh, qq|DELETE FROM custom_variables        WHERE config_id = ?|, conv_i($params{id}));
168   do_query($form, $dbh, qq|DELETE FROM custom_variable_configs WHERE id        = ?|, conv_i($params{id}));
169
170   $dbh->commit();
171
172   $main::lxdebug->leave_sub();
173 }
174
175 sub get_custom_variables {
176   $main::lxdebug->enter_sub();
177
178   my $self     = shift;
179   my %params   = @_;
180
181   Common::check_params(\%params, qw(module));
182
183   my $myconfig = \%main::myconfig;
184   my $form     = $main::form;
185
186   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
187
188   my $trans_id = $params{trans_id} ? 'OR (v.trans_id = ?) ' : '';
189
190   my $q_cfg    =
191     qq|SELECT id, name, description, type, default_value, options,
192          date_trunc('seconds', localtimestamp) AS current_timestamp, current_date AS current_date
193        FROM custom_variable_configs
194        WHERE module = ?
195        ORDER BY sortkey|;
196
197   my $q_var    =
198     qq|SELECT text_value, timestamp_value, timestamp_value::date AS date_value, number_value, bool_value
199        FROM custom_variables
200        WHERE (config_id = ?) AND (trans_id = ?)|;
201   $q_var      .= qq| AND (sub_module = ?)| if $params{sub_module};
202   my $h_var    = prepare_query($form, $dbh, $q_var);
203
204   my $custom_variables = selectall_hashref_query($form, $dbh, $q_cfg, $params{module});
205
206   foreach my $cvar (@{ $custom_variables }) {
207     if ($cvar->{type} eq 'textfield') {
208       $cvar->{width}  = 30;
209       $cvar->{height} =  5;
210
211       $cvar->{width}  = $1 if ($cvar->{options} =~ m/width=(\d+)/i);
212       $cvar->{height} = $1 if ($cvar->{options} =~ m/height=(\d+)/i);
213
214     } elsif ($cvar->{type} eq 'text') {
215       $cvar->{maxlength} = $1 if ($cvar->{options} =~ m/maxlength=(\d+)/i);
216
217     } elsif ($cvar->{type} eq 'number') {
218       $cvar->{precision} = $1 if ($cvar->{options} =~ m/precision=(\d+)/i);
219
220     } elsif ($cvar->{type} eq 'select') {
221       $cvar->{OPTIONS} = [ map { { 'value' => $_ } } split(m/\#\#/, $cvar->{options}) ];
222     }
223
224     my $act_var;
225     if ($params{trans_id}) {
226       my @values = (conv_i($cvar->{id}), conv_i($params{trans_id}));
227       push @values, $params{sub_module} if $params{sub_module};
228
229       do_statement($form, $h_var, $q_var, @values);
230       $act_var = $h_var->fetchrow_hashref();
231     }
232
233     if ($act_var) {
234       $cvar->{value} = $cvar->{type} eq 'date'      ? $act_var->{date_value}
235                      : $cvar->{type} eq 'timestamp' ? $act_var->{timestamp_value}
236                      : $cvar->{type} eq 'number'    ? $act_var->{number_value}
237                      : $cvar->{type} eq 'bool'      ? $act_var->{bool_value}
238                      :                                $act_var->{text_value};
239
240     } else {
241       if ($cvar->{type} eq 'date') {
242         if ($cvar->{default_value} eq 'NOW') {
243           $cvar->{value} = $cvar->{current_date};
244         } else {
245           $cvar->{value} = $cvar->{default_value};
246         }
247
248       } elsif ($cvar->{type} eq 'timestamp') {
249         if ($cvar->{default_value} eq 'NOW') {
250           $cvar->{value} = $cvar->{current_timestamp};
251         } else {
252           $cvar->{value} = $cvar->{default_value};
253         }
254
255       } elsif ($cvar->{type} eq 'bool') {
256         $cvar->{value} = $cvar->{default_value} * 1;
257
258       } elsif ($cvar->{type} eq 'number') {
259         $cvar->{value} = $cvar->{default_value} * 1 if ($cvar->{default_value} ne '');
260
261       } else {
262         $cvar->{value} = $cvar->{default_value};
263       }
264     }
265
266     if ($cvar->{type} eq 'number') {
267       $cvar->{value} = $form->format_amount($myconfig, $cvar->{value} * 1, $cvar->{precision});
268     }
269   }
270
271   $h_var->finish();
272
273   $main::lxdebug->leave_sub();
274
275   return $custom_variables;
276 }
277
278 sub save_custom_variables {
279   $main::lxdebug->enter_sub();
280
281   my $self     = shift;
282   my %params   = @_;
283
284   Common::check_params(\%params, qw(module trans_id variables));
285
286   my $myconfig = \%main::myconfig;
287   my $form     = $main::form;
288
289   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
290
291   my @configs  = $params{configs} ? @{ $params{configs} } : grep { $_->{module} eq $params{module} } @{ CVar->get_configs() };
292
293   my $query    =
294     qq|DELETE FROM custom_variables
295        WHERE (trans_id  = ?)
296          AND (config_id IN (SELECT DISTINCT id
297                             FROM custom_variable_configs
298                             WHERE module = ?))|;
299   my @values   = (conv_i($params{trans_id}), $params{module});
300
301   if ($params{sub_module}) {
302     $query .= qq| AND (sub_module = ?)|;
303     push @values, $params{sub_module};
304   }
305
306   do_query($form, $dbh, $query, @values);
307
308   $query  =
309     qq|INSERT INTO custom_variables (config_id, sub_module, trans_id, bool_value, timestamp_value, text_value, number_value)
310        VALUES                       (?,         ?,          ?,        ?,          ?,               ?,          ?)|;
311   my $sth = prepare_query($form, $dbh, $query);
312
313   foreach my $config (@configs) {
314     my @values = (conv_i($config->{id}), "$params{sub_module}", conv_i($params{trans_id}));
315
316     my $value  = $params{variables}->{"$params{name_prefix}cvar_$config->{name}$params{name_postfix}"};
317
318     if (($config->{type} eq 'text') || ($config->{type} eq 'textfield') || ($config->{type} eq 'select')) {
319       push @values, undef, undef, $value, undef;
320
321     } elsif (($config->{type} eq 'date') || ($config->{type} eq 'timestamp')) {
322       push @values, undef, conv_date($value), undef, undef;
323
324     } elsif ($config->{type} eq 'number') {
325       push @values, undef, undef, undef, conv_i($form->parse_amount($myconfig, $value));
326
327     } elsif ($config->{type} eq 'bool') {
328       push @values, $value ? 't' : 'f', undef, undef, undef;
329     }
330
331     do_statement($form, $sth, $query, @values);
332   }
333
334   $sth->finish();
335
336   $dbh->commit();
337
338   $main::lxdebug->leave_sub();
339 }
340
341 sub render_inputs {
342   $main::lxdebug->enter_sub();
343
344   my $self     = shift;
345   my %params   = @_;
346
347   Common::check_params(\%params, qw(variables));
348
349   my $myconfig = \%main::myconfig;
350   my $form     = $main::form;
351
352   my %options  = ( name_prefix       => "$params{name_prefix}",
353                    name_postfix      => "$params{name_postfix}",
354                    hide_non_editable => $params{hide_non_editable},
355                  );
356
357   foreach my $var (@{ $params{variables} }) {
358     $var->{HTML_CODE} = $form->parse_html_template('amcvar/render_inputs', { 'var' => $var, %options });
359   }
360
361   $main::lxdebug->leave_sub();
362 }
363
364 sub render_search_options {
365   $main::lxdebug->enter_sub();
366
367   my $self     = shift;
368   my %params   = @_;
369
370   Common::check_params(\%params, qw(variables));
371
372   my $myconfig = \%main::myconfig;
373   my $form     = $main::form;
374
375   $params{include_prefix}   = 'l_' unless defined($params{include_prefix});
376   $params{include_value}  ||= '1';
377
378   my $filter  = $form->parse_html_template('amcvar/search_filter',  \%params);
379   my $include = $form->parse_html_template('amcvar/search_include', \%params);
380
381   $main::lxdebug->leave_sub();
382
383   return ($filter, $include);
384 }
385
386 sub build_filter_query {
387   $main::lxdebug->enter_sub();
388
389   my $self     = shift;
390   my %params   = @_;
391
392   Common::check_params(\%params, qw(module trans_id_field filter));
393
394   my $myconfig = \%main::myconfig;
395   my $form     = $main::form;
396
397   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
398
399   my $configs  = $self->get_configs(%params);
400
401   my (@where, @values);
402
403   foreach my $config (@{ $configs }) {
404     next unless ($config->{searchable});
405
406     my $name = "cvar_$config->{name}";
407
408     my (@sub_values, @sub_where, $not);
409
410     if (($config->{type} eq 'text') || ($config->{type} eq 'textfield')) {
411       next unless ($params{filter}->{$name});
412
413       push @sub_where,  qq|cvar.text_value ILIKE ?|;
414       push @sub_values, '%' . $params{filter}->{$name} . '%'
415
416     } elsif ($config->{type} eq 'select') {
417       next unless ($params{filter}->{$name});
418
419       push @sub_where,  qq|cvar.text_value = ?|;
420       push @sub_values, $params{filter}->{$name};
421
422     } elsif (($config->{type} eq 'date') || ($config->{type} eq 'timestamp')) {
423       my $name_from = "${name}_from";
424       my $name_to   = "${name}_to";
425
426       if ($params{filter}->{$name_from}) {
427         push @sub_where,  qq|cvar.timestamp_value >= ?|;
428         push @sub_values, conv_date($params{filter}->{$name_from});
429       }
430
431       if ($params{filter}->{$name_to}) {
432         push @sub_where,  qq|cvar.timestamp_value <= ?|;
433         push @sub_values, conv_date($params{filter}->{$name_to});
434       }
435
436     } elsif ($config->{type} eq 'number') {
437       next if ($params{filter}->{$name} eq '');
438
439       my $f_op = $params{filter}->{"${name}_qtyop"};
440
441       if ($f_op eq '==') {
442         $op  = '=';
443
444       } elsif ($f_op eq '=/=') {
445         $not = 'NOT';
446         $op  = '<>';
447
448       } elsif ($f_op eq '<') {
449         $not = 'NOT';
450         $op  = '>=';
451
452       } elsif ($f_op eq '<=') {
453         $not = 'NOT';
454         $op  = '>';
455
456       } elsif (($f_op eq '>') || ($f_op eq '>=')) {
457         $op  = $f_op;
458
459       } else {
460         $op  = '=';
461       }
462
463       push @sub_where,  qq|cvar.number_value $op ?|;
464       push @sub_values, $form->parse_amount($myconfig, $params{filter}->{$name});
465
466     } elsif ($config->{type} eq 'bool') {
467       next unless ($params{filter}->{$name});
468
469       $not = 'NOT' if ($params{filter}->{$name} eq 'no');
470       push @sub_where,  qq|COALESCE(cvar.bool_value, false) = TRUE|;
471     }
472
473     if (@sub_where) {
474       push @sub_where,  qq|cvar.sub_module = ?|;
475       push @sub_values, "$params{sub_module}";
476
477       push @where,
478         qq|$not EXISTS(
479              SELECT cvar.id
480              FROM custom_variables cvar
481              LEFT JOIN custom_variable_configs cvarcfg ON (cvar.config_id = cvarcfg.id)
482              WHERE (cvarcfg.module = ?)
483                AND (cvarcfg.id     = ?)
484                AND (cvar.trans_id  = $params{trans_id_field})
485                AND | . join(' AND ', map { "($_)" } @sub_where) . qq|)|;
486       push @values, $params{module}, conv_i($config->{id}), @sub_values;
487     }
488   }
489
490   my $query = join ' AND ', @where;
491
492   $main::lxdebug->leave_sub();
493
494   return ($query, @values);
495 }
496
497 sub add_custom_variables_to_report {
498   $main::lxdebug->enter_sub();
499
500   my $self      = shift;
501   my %params    = @_;
502
503   Common::check_params(\%params, qw(module trans_id_field column_defs data configs));
504
505   my $myconfig  = \%main::myconfig;
506   my $form      = $main::form;
507   my $locale    = $main::locale;
508
509   my $dbh       = $params{dbh} || $form->get_standard_dbh($myconfig);
510
511   my $configs   = [ grep { $_->{includeable} && $params{column_defs}->{"cvar_$_->{name}"}->{visible} } @{ $params{configs} } ];
512
513   if (!scalar(@{ $params{data} }) || ! scalar(@{ $configs })) {
514     $main::lxdebug->leave_sub();
515     return;
516   }
517
518   my %cfg_map   = map { $_->{id} => $_ } @{ $configs };
519   my @cfg_ids   = keys %cfg_map;
520
521   my $query     =
522     qq|SELECT text_value, timestamp_value, timestamp_value::date AS date_value, number_value, bool_value, config_id
523        FROM custom_variables
524        WHERE (config_id IN (| . join(', ', ('?') x scalar(@cfg_ids)) . qq|))
525          AND (trans_id = ?)
526          AND (sub_module = ?)|;
527   my $sth       = prepare_query($form, $dbh, $query);
528
529   foreach my $row (@{ $params{data} }) {
530     do_statement($form, $sth, $query, @cfg_ids, conv_i($row->{$params{trans_id_field}}), "$params{sub_module}");
531
532     while (my $ref = $sth->fetchrow_hashref()) {
533       my $cfg = $cfg_map{$ref->{config_id}};
534
535       $row->{"cvar_$cfg->{name}"} =
536           $cfg->{type} eq 'date'      ? $ref->{date_value}
537         : $cfg->{type} eq 'timestamp' ? $ref->{timestamp_value}
538         : $cfg->{type} eq 'number'    ? $form->format_amount($myconfig, $ref->{number_value} * 1, $config->{precision})
539         : $cfg->{type} eq 'bool'      ? ($ref->{bool_value} ? $locale->text('Yes') : $locale->text('No'))
540         :                               $ref->{text_value};
541     }
542   }
543
544   $sth->finish();
545
546   $main::lxdebug->leave_sub();
547 }
548
549 sub get_field_format_list {
550   $main::lxdebug->enter_sub();
551
552   my $self          = shift;
553   my %params        = @_;
554
555   Common::check_params(\%params, qw(module));
556
557   my $myconfig      = \%main::myconfig;
558   my $form          = $main::form;
559
560   my $dbh           = $params{dbh} || $form->get_standard_dbh($myconfig);
561
562   my $configs       = $self->get_configs(%params);
563
564   my $date_fields   = [];
565   my $number_fields = {};
566
567   foreach my $config (@{ $configs }) {
568     my $name = "$params{prefix}cvar_$config->{name}";
569
570     if ($config->{type} eq 'date') {
571       push @{ $date_fields }, $name;
572
573     } elsif ($config->{type} eq 'number') {
574       $number_fields->{$config->{precision}} ||= [];
575       push @{ $number_fields->{$config->{precision}} }, $name;
576     }
577   }
578
579   $main::lxdebug->leave_sub();
580
581   return ($date_fields, $number_fields);
582 }
583
584
585 1;