5 use List::Util qw(first);
6 use Scalar::Util qw(blessed);
10 use SL::MoreCommon qw(any listify);
13 $main::lxdebug->enter_sub();
18 my $myconfig = \%main::myconfig;
19 my $form = $main::form;
21 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
24 if ($params{module}) {
25 $where = 'WHERE module = ?';
26 push @values, $params{module};
30 SELECT *, date_trunc('seconds', localtimestamp) AS current_timestamp
31 FROM custom_variable_configs $where ORDER BY sortkey
34 $::form->{CVAR_CONFIGS} = {} unless 'HASH' eq ref $::form->{CVAR_CONFIGS};
35 if (!$::form->{CVAR_CONFIGS}->{$params{module}}) {
36 my $configs = selectall_hashref_query($form, $dbh, $query, @values);
38 foreach my $config (@{ $configs }) {
39 if ($config->{type} eq 'select') {
40 $config->{OPTIONS} = [ map { { 'value' => $_ } } split(m/\#\#/, $config->{options}) ];
42 } elsif ($config->{type} eq 'number') {
43 $config->{precision} = $1 if ($config->{options} =~ m/precision=(\d+)/i);
45 } elsif ($config->{type} eq 'textfield') {
46 $config->{width} = 30;
47 $config->{height} = 5;
48 $config->{width} = $1 if ($config->{options} =~ m/width=(\d+)/i);
49 $config->{height} = $1 if ($config->{options} =~ m/height=(\d+)/i);
51 } elsif ($config->{type} eq 'text') {
52 $config->{maxlength} = $1 if ($config->{options} =~ m/maxlength=(\d+)/i);
56 $self->_unpack_flags($config);
58 $::form->{CVAR_CONFIGS}->{$params{module}} = $configs;
61 $main::lxdebug->leave_sub();
63 return $::form->{CVAR_CONFIGS}->{$params{module}};
67 $main::lxdebug->enter_sub();
72 Common::check_params(\%params, qw(id));
74 my $myconfig = \%main::myconfig;
75 my $form = $main::form;
77 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
79 my $query = qq|SELECT * FROM custom_variable_configs WHERE id = ?|;
81 my $config = selectfirst_hashref_query($form, $dbh, $query, conv_i($params{id})) || { };
83 $self->_unpack_flags($config);
85 $main::lxdebug->leave_sub();
91 $main::lxdebug->enter_sub();
96 foreach my $flag (split m/:/, $config->{flags}) {
97 if ($flag =~ m/(.*?)=(.*)/) {
98 $config->{"flag_${1}"} = $2;
100 $config->{"flag_${flag}"} = 1;
104 $main::lxdebug->leave_sub();
108 $main::lxdebug->enter_sub();
113 Common::check_params(\%params, qw(module config));
115 my $myconfig = \%main::myconfig;
116 my $form = $main::form;
118 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
120 my $q_id = qq|SELECT nextval('custom_variable_configs_id')|;
121 my $h_id = prepare_query($form, $dbh, $q_id);
124 qq|INSERT INTO custom_variable_configs (name, description, type, default_value, options, searchable, includeable, included_by_default, module, flags, id, sortkey)
125 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
126 (SELECT COALESCE(MAX(sortkey) + 1, 1) FROM custom_variable_configs))|;
127 my $h_new = prepare_query($form, $dbh, $q_new);
130 qq|UPDATE custom_variable_configs SET
131 name = ?, description = ?,
132 type = ?, default_value = ?,
133 options = ?, searchable = ?,
134 includeable = ?, included_by_default = ?,
135 module = ?, flags = ?
137 my $h_update = prepare_query($form, $dbh, $q_update);
140 if ('ARRAY' eq ref $params{config}) {
141 @configs = @{ $params{config} };
143 @configs = ($params{config});
146 foreach my $config (@configs) {
147 my ($h_actual, $q_actual);
149 if (!$config->{id}) {
150 do_statement($form, $h_id, $q_id);
151 ($config->{id}) = $h_id->fetchrow_array();
157 $h_actual = $h_update;
158 $q_actual = $q_update;
161 do_statement($form, $h_actual, $q_actual, @{$config}{qw(name description type default_value options)},
162 $config->{searchable} ? 't' : 'f', $config->{includeable} ? 't' : 'f', $config->{included_by_default} ? 't' : 'f',
163 $params{module}, $config->{flags}, conv_i($config->{id}));
172 $main::lxdebug->leave_sub();
176 $main::lxdebug->enter_sub();
181 Common::check_params(\%params, qw(id));
183 my $myconfig = \%main::myconfig;
184 my $form = $main::form;
186 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
188 do_query($form, $dbh, qq|DELETE FROM custom_variables WHERE config_id = ?|, conv_i($params{id}));
189 do_query($form, $dbh, qq|DELETE FROM custom_variables_validity WHERE config_id = ?|, conv_i($params{id}));
190 do_query($form, $dbh, qq|DELETE FROM custom_variable_configs WHERE id = ?|, conv_i($params{id}));
194 $main::lxdebug->leave_sub();
197 sub get_custom_variables {
198 $main::lxdebug->enter_sub();
203 Common::check_params(\%params, qw(module));
205 my $myconfig = \%main::myconfig;
206 my $form = $main::form;
208 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
210 my $trans_id = $params{trans_id} ? 'OR (v.trans_id = ?) ' : '';
213 qq|SELECT text_value, timestamp_value, timestamp_value::date AS date_value, number_value, bool_value
214 FROM custom_variables
215 WHERE (config_id = ?) AND (trans_id = ?)|;
216 $q_var .= qq| AND (sub_module = ?)| if $params{sub_module};
217 my $h_var = prepare_query($form, $dbh, $q_var);
219 my $custom_variables = $self->get_configs(module => $params{module});
221 foreach my $cvar (@{ $custom_variables }) {
222 if ($cvar->{type} eq 'textfield') {
226 $cvar->{width} = $1 if ($cvar->{options} =~ m/width=(\d+)/i);
227 $cvar->{height} = $1 if ($cvar->{options} =~ m/height=(\d+)/i);
229 } elsif ($cvar->{type} eq 'text') {
230 $cvar->{maxlength} = $1 if ($cvar->{options} =~ m/maxlength=(\d+)/i);
232 } elsif ($cvar->{type} eq 'number') {
233 $cvar->{precision} = $1 if ($cvar->{options} =~ m/precision=(\d+)/i);
235 } elsif ($cvar->{type} eq 'select') {
236 $cvar->{OPTIONS} = [ map { { 'value' => $_ } } split(m/\#\#/, $cvar->{options}) ];
239 my ($act_var, $valid);
240 if ($params{trans_id}) {
241 my @values = (conv_i($cvar->{id}), conv_i($params{trans_id}));
242 push @values, $params{sub_module} if $params{sub_module};
244 do_statement($form, $h_var, $q_var, @values);
245 $act_var = $h_var->fetchrow_hashref();
247 $valid = $self->get_custom_variables_validity(config_id => $cvar->{id}, trans_id => $params{trans_id});
251 $cvar->{value} = $cvar->{type} eq 'date' ? $act_var->{date_value}
252 : $cvar->{type} eq 'timestamp' ? $act_var->{timestamp_value}
253 : $cvar->{type} eq 'number' ? $act_var->{number_value}
254 : $cvar->{type} eq 'customer' ? $act_var->{number_value}
255 : $cvar->{type} eq 'vendor' ? $act_var->{number_value}
256 : $cvar->{type} eq 'part' ? $act_var->{number_value}
257 : $cvar->{type} eq 'bool' ? $act_var->{bool_value}
258 : $act_var->{text_value};
259 $cvar->{valid} = $valid;
263 if ($cvar->{type} eq 'date') {
264 if ($cvar->{default_value} eq 'NOW') {
265 $cvar->{value} = $cvar->{current_date};
267 $cvar->{value} = $cvar->{default_value};
270 } elsif ($cvar->{type} eq 'timestamp') {
271 if ($cvar->{default_value} eq 'NOW') {
272 $cvar->{value} = $cvar->{current_timestamp};
274 $cvar->{value} = $cvar->{default_value};
277 } elsif ($cvar->{type} eq 'bool') {
278 $cvar->{value} = $cvar->{default_value} * 1;
280 } elsif ($cvar->{type} eq 'number') {
281 $cvar->{value} = $cvar->{default_value} * 1 if ($cvar->{default_value} ne '');
284 $cvar->{value} = $cvar->{default_value};
288 if ($cvar->{type} eq 'number') {
289 $cvar->{value} = $form->format_amount($myconfig, $cvar->{value} * 1, $cvar->{precision});
290 } elsif ($cvar->{type} eq 'customer') {
291 require SL::DB::Customer;
292 $cvar->{value} = SL::DB::Manager::Customer->find_by(id => $cvar->{value} * 1);
293 } elsif ($cvar->{type} eq 'vendor') {
294 require SL::DB::Vendor;
295 $cvar->{value} = SL::DB::Manager::Vendor->find_by(id => $cvar->{value} * 1);
296 } elsif ($cvar->{type} eq 'part') {
297 require SL::DB::Part;
298 $cvar->{value} = SL::DB::Manager::Part->find_by(id => $cvar->{value} * 1);
304 $main::lxdebug->leave_sub();
306 return $custom_variables;
309 sub save_custom_variables {
310 $main::lxdebug->enter_sub();
315 Common::check_params(\%params, qw(module trans_id variables));
317 my $myconfig = \%main::myconfig;
318 my $form = $main::form;
320 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
322 my @configs = $params{configs} ? @{ $params{configs} } : grep { $_->{module} eq $params{module} } @{ CVar->get_configs() };
325 qq|DELETE FROM custom_variables
327 AND (config_id IN (SELECT DISTINCT id
328 FROM custom_variable_configs
330 my @values = (conv_i($params{trans_id}), $params{module});
332 if ($params{sub_module}) {
333 $query .= qq| AND (sub_module = ?)|;
334 push @values, $params{sub_module};
337 do_query($form, $dbh, $query, @values);
340 qq|INSERT INTO custom_variables (config_id, sub_module, trans_id, bool_value, timestamp_value, text_value, number_value)
341 VALUES (?, ?, ?, ?, ?, ?, ?)|;
342 my $sth = prepare_query($form, $dbh, $query);
344 foreach my $config (@configs) {
345 my @values = (conv_i($config->{id}), "$params{sub_module}", conv_i($params{trans_id}));
347 my $value = $params{variables}->{"$params{name_prefix}cvar_$config->{name}$params{name_postfix}"};
349 if (($config->{type} eq 'text') || ($config->{type} eq 'textfield') || ($config->{type} eq 'select')) {
350 push @values, undef, undef, $value, undef;
352 } elsif (($config->{type} eq 'date') || ($config->{type} eq 'timestamp')) {
353 push @values, undef, conv_date($value), undef, undef;
355 } elsif ($config->{type} eq 'number') {
356 push @values, undef, undef, undef, conv_i($form->parse_amount($myconfig, $value));
358 } elsif ($config->{type} eq 'bool') {
359 push @values, $value ? 't' : 'f', undef, undef, undef;
360 } elsif (any { $config->{type} eq $_ } qw(customer vendor part)) {
361 push @values, undef, undef, undef, $value * 1;
364 do_statement($form, $sth, $query, @values);
366 if ($params{save_validity}) {
367 my $valid_index = "$params{name_prefix}cvar_$config->{name}$params{name_postfix}_valid";
368 $self->save_custom_variables_validity(trans_id => $params{trans_id},
369 config_id => $config->{id},
370 validity => ($params{variables}{$valid_index} || $params{always_valid} ? 1 : 0)
377 $dbh->commit() unless $params{dbh};
379 $main::lxdebug->leave_sub();
383 $main::lxdebug->enter_sub(2);
388 Common::check_params(\%params, qw(variables));
390 my $myconfig = \%main::myconfig;
391 my $form = $main::form;
393 my %options = ( name_prefix => "$params{name_prefix}",
394 name_postfix => "$params{name_postfix}",
395 hide_non_editable => $params{hide_non_editable},
396 show_disabled_message => $params{show_disabled_message},
399 foreach my $var (@{ $params{variables} }) {
400 $var->{HTML_CODE} = $form->parse_html_template('amcvar/render_inputs', { var => $var, %options });
401 $var->{VALID_BOX} = $form->parse_html_template('amcvar/render_checkboxes', { var => $var, %options });
404 $main::lxdebug->leave_sub(2);
407 sub render_search_options {
408 $main::lxdebug->enter_sub();
413 Common::check_params(\%params, qw(variables));
415 my $myconfig = \%main::myconfig;
416 my $form = $main::form;
418 $params{hidden_cvar_filters} = $myconfig->{hide_cvar_search_options};
420 $params{include_prefix} = 'l_' unless defined($params{include_prefix});
421 $params{include_value} ||= '1';
422 $params{filter_prefix} ||= '';
424 my $filter = $form->parse_html_template('amcvar/search_filter', \%params);
425 my $include = $form->parse_html_template('amcvar/search_include', \%params);
427 $main::lxdebug->leave_sub();
429 return ($filter, $include);
432 sub build_filter_query {
433 $main::lxdebug->enter_sub();
438 Common::check_params(\%params, qw(module trans_id_field filter));
440 my $myconfig = \%main::myconfig;
441 my $form = $main::form;
443 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
445 my $configs = $self->get_configs(%params);
447 my (@where, @values);
449 foreach my $config (@{ $configs }) {
450 next unless ($config->{searchable});
452 my $name = "cvar_$config->{name}";
454 my (@sub_values, @sub_where, $not);
456 if (($config->{type} eq 'text') || ($config->{type} eq 'textfield')) {
457 next unless ($params{filter}->{$name});
459 push @sub_where, qq|cvar.text_value ILIKE ?|;
460 push @sub_values, '%' . $params{filter}->{$name} . '%'
462 } elsif ($config->{type} eq 'select') {
463 next unless ($params{filter}->{$name});
465 push @sub_where, qq|cvar.text_value = ?|;
466 push @sub_values, $params{filter}->{$name};
468 } elsif (($config->{type} eq 'date') || ($config->{type} eq 'timestamp')) {
469 my $name_from = "${name}_from";
470 my $name_to = "${name}_to";
472 if ($params{filter}->{$name_from}) {
473 push @sub_where, qq|cvar.timestamp_value >= ?|;
474 push @sub_values, conv_date($params{filter}->{$name_from});
477 if ($params{filter}->{$name_to}) {
478 push @sub_where, qq|cvar.timestamp_value <= ?|;
479 push @sub_values, conv_date($params{filter}->{$name_to});
482 } elsif ($config->{type} eq 'number') {
483 next if ($params{filter}->{$name} eq '');
485 my $f_op = $params{filter}->{"${name}_qtyop"};
491 } elsif ($f_op eq '=/=') {
495 } elsif ($f_op eq '<') {
499 } elsif ($f_op eq '<=') {
503 } elsif (($f_op eq '>') || ($f_op eq '>=')) {
510 push @sub_where, qq|cvar.number_value $op ?|;
511 push @sub_values, $form->parse_amount($myconfig, $params{filter}->{$name});
513 } elsif ($config->{type} eq 'bool') {
514 next unless ($params{filter}->{$name});
516 $not = 'NOT' if ($params{filter}->{$name} eq 'no');
517 push @sub_where, qq|COALESCE(cvar.bool_value, false) = TRUE|;
518 } elsif (any { $config->{type} eq $_ } qw(customer vendor part)) {
519 next unless $params{filter}->{$name};
521 my $table = $config->{type};
522 push @sub_where, qq|cvar.number_value * 1 IN (SELECT id FROM $table WHERE name ILIKE ?)|;
523 push @sub_values, "%$params{filter}->{$name}%";
524 } elsif ($config->{type} eq 'part') {
525 next unless $params{filter}->{$name};
527 push @sub_where, qq|cvar.number_value * 1 IN (SELECT id FROM parts WHERE partnumber ILIKE ?)|;
528 push @sub_values, "%$params{filter}->{$name}%";
532 add_token(\@sub_where, \@sub_values, col => 'cvar.sub_module', val => $params{sub_module} || '');
537 FROM custom_variables cvar
538 LEFT JOIN custom_variable_configs cvarcfg ON (cvar.config_id = cvarcfg.id)
539 WHERE (cvarcfg.module = ?)
541 AND (cvar.trans_id = $params{trans_id_field})
542 AND | . join(' AND ', map { "($_)" } @sub_where) . qq|)|;
543 push @values, $params{module}, conv_i($config->{id}), @sub_values;
547 my $query = join ' AND ', @where;
549 $main::lxdebug->leave_sub();
551 return ($query, @values);
554 sub add_custom_variables_to_report {
555 $main::lxdebug->enter_sub();
560 Common::check_params(\%params, qw(module trans_id_field column_defs data configs));
562 my $myconfig = \%main::myconfig;
563 my $form = $main::form;
564 my $locale = $main::locale;
566 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
568 my $configs = [ grep { $_->{includeable} && $params{column_defs}->{"cvar_$_->{name}"}->{visible} } @{ $params{configs} } ];
570 if (!scalar(@{ $params{data} }) || ! scalar(@{ $configs })) {
571 $main::lxdebug->leave_sub();
575 # allow sub_module to be a coderef or a fixed value
576 if (ref $params{sub_module} ne 'CODE') {
577 my $sub_module = "$params{sub_module}";
578 $params{sub_module} = sub { $sub_module };
581 my %cfg_map = map { $_->{id} => $_ } @{ $configs };
582 my @cfg_ids = keys %cfg_map;
585 qq|SELECT text_value, timestamp_value, timestamp_value::date AS date_value, number_value, bool_value, config_id
586 FROM custom_variables
587 WHERE (config_id IN (| . join(', ', ('?') x scalar(@cfg_ids)) . qq|))
589 AND (sub_module = ?)|;
590 my $sth = prepare_query($form, $dbh, $query);
592 foreach my $row (@{ $params{data} }) {
593 do_statement($form, $sth, $query, @cfg_ids, conv_i($row->{$params{trans_id_field}}), $params{sub_module}->($row));
595 while (my $ref = $sth->fetchrow_hashref()) {
596 my $cfg = $cfg_map{$ref->{config_id}};
598 $row->{"cvar_$cfg->{name}"} =
599 $cfg->{type} eq 'date' ? $ref->{date_value}
600 : $cfg->{type} eq 'timestamp' ? $ref->{timestamp_value}
601 : $cfg->{type} eq 'number' ? $form->format_amount($myconfig, $ref->{number_value} * 1, $cfg->{precision})
602 : $cfg->{type} eq 'customer' ? (SL::DB::Manager::Customer->find_by(id => 1*$ref->{number_value}) || SL::DB::Customer->new)->name
603 : $cfg->{type} eq 'vendor' ? (SL::DB::Manager::Vendor->find_by(id => 1*$ref->{number_value}) || SL::DB::Vendor->new)->name
604 : $cfg->{type} eq 'part' ? (SL::DB::Manager::Part->find_by(id => 1*$ref->{number_value}) || SL::DB::Part->new)->partnumber
605 : $cfg->{type} eq 'bool' ? ($ref->{bool_value} ? $locale->text('Yes') : $locale->text('No'))
606 : $ref->{text_value};
612 $main::lxdebug->leave_sub();
615 sub get_field_format_list {
616 $main::lxdebug->enter_sub();
621 Common::check_params(\%params, qw(module));
623 my $myconfig = \%main::myconfig;
624 my $form = $main::form;
626 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
628 my $configs = $self->get_configs(%params);
630 my $date_fields = [];
631 my $number_fields = {};
633 foreach my $config (@{ $configs }) {
634 my $name = "$params{prefix}cvar_$config->{name}";
636 if ($config->{type} eq 'date') {
637 push @{ $date_fields }, $name;
639 } elsif ($config->{type} eq 'number') {
640 $number_fields->{$config->{precision}} ||= [];
641 push @{ $number_fields->{$config->{precision}} }, $name;
645 $main::lxdebug->leave_sub();
647 return ($date_fields, $number_fields);
650 sub save_custom_variables_validity {
651 $main::lxdebug->enter_sub();
656 Common::check_params(\%params, qw(config_id trans_id validity));
658 my $myconfig = \%main::myconfig;
659 my $form = $main::form;
661 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
663 my (@where, @values);
664 add_token(\@where, \@values, col => "config_id", val => $params{config_id}, esc => \&conv_i);
665 add_token(\@where, \@values, col => "trans_id", val => $params{trans_id}, esc => \&conv_i);
667 my $where = scalar @where ? "WHERE " . join ' AND ', @where : '';
668 my $query = qq|DELETE FROM custom_variables_validity $where|;
670 do_query($form, $dbh, $query, @values);
673 qq|INSERT INTO custom_variables_validity (config_id, trans_id)
675 my $sth = prepare_query($form, $dbh, $query);
677 unless ($params{validity}) {
678 foreach my $config_id (listify $params{config_id}) {
679 foreach my $trans_id (listify $params{trans_id}) {
680 do_statement($form, $sth, $query, conv_i($config_id), conv_i($trans_id));
687 $dbh->commit() unless $params{dbh};
689 $main::lxdebug->leave_sub();
692 sub get_custom_variables_validity {
693 $main::lxdebug->enter_sub(2);
698 Common::check_params(\%params, qw(config_id trans_id));
700 my $myconfig = \%main::myconfig;
701 my $form = $main::form;
703 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
705 my $query = qq|SELECT COUNT(*) FROM custom_variables_validity WHERE config_id = ? AND trans_id = ?|;
707 my ($invalid) = selectfirst_array_query($form, $dbh, $query, conv_i($params{config_id}), conv_i($params{trans_id}));
709 $main::lxdebug->leave_sub(2);
714 sub custom_variables_validity_by_trans_id {
715 $main::lxdebug->enter_sub(2);
720 return sub { 0 } unless $params{trans_id};
722 my $myconfig = \%main::myconfig;
723 my $form = $main::form;
725 my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig);
727 my $query = qq|SELECT config_id, COUNT(*) FROM custom_variables_validity WHERE trans_id = ? GROUP BY config_id|;
729 my %invalids = selectall_as_map($form, $dbh, $query, 'config_id', 'count', $params{trans_id});
731 $main::lxdebug->leave_sub(2);
733 return sub { !$invalids{+shift} };
737 my ($self, $value, $config) = @_;
739 return $::form->parse_amount(\%::myconfig, $value) if $config->{type} eq 'number';
740 return DateTime->from_lxoffice($value) if $config->{type} eq 'date';
741 return !ref $value ? SL::DB::Manager::Customer->find_by(id => $value * 1) : $value if $config->{type} eq 'customer';
742 return !ref $value ? SL::DB::Manager::Vendor->find_by(id => $value * 1) : $value if $config->{type} eq 'vendor';
743 return !ref $value ? SL::DB::Manager::Part->find_by(id => $value * 1) : $value if $config->{type} eq 'part';
747 sub format_to_template {
748 my ($self, $value, $config) = @_;
749 # stupid template expects everything formated. except objects
750 # do not use outside of print routines for legacy templates
752 return $::form->format_amount(\%::myconfig, $value) if $config->{type} eq 'number';
753 return $value->to_lxoffice if $config->{type} eq 'date' && blessed $value && $value->can('to_lxoffice');
763 SL::CVar.pm - Custom Variables module
767 # dealing with configs
769 my $all_configs = CVar->get_configs()
770 my $config = CVar->get_config(id => '1234')
772 CVar->save_config($config);
773 CVar->delete->config($config)
775 # dealing with custom vars
777 CVar->get_custom_variables(module => 'ic')
781 Suppose the following scenario:
783 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.
785 Validity is assumed. If you modify validity, you actually save B<invalidity>.
786 Invalidity is saved as a function of config_id, and the trans_id
788 In the naive way, disable an attribute for a specific id (simple)