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