+sub save_custom_variables_validity {
+  my ($self, %params) = @_;
+  $main::lxdebug->enter_sub();
+
+  my $rc = SL::DB->client->with_transaction(\&_save_custom_variables_validity, $self, %params);
+
+  $::lxdebug->leave_sub;
+  return $rc;
+}
+
+sub _save_custom_variables_validity {
+  my $self     = shift;
+  my %params   = @_;
+
+  Common::check_params(\%params, qw(config_id trans_id validity));
+
+  my $myconfig = \%main::myconfig;
+  my $form     = $main::form;
+
+  my $dbh      = $params{dbh} || SL::DB->client->dbh;
+
+  my (@where, @values);
+  add_token(\@where, \@values, col => "config_id", val => $params{config_id}, esc => \&conv_i);
+  add_token(\@where, \@values, col => "trans_id",  val => $params{trans_id},  esc => \&conv_i);
+
+  my $where = scalar @where ? "WHERE " . join ' AND ', @where : '';
+  my $query = qq|DELETE FROM custom_variables_validity $where|;
+
+  do_query($form, $dbh, $query, @values);
+
+  $query  =
+    qq|INSERT INTO custom_variables_validity (config_id, trans_id)
+       VALUES                                (?,         ?       )|;
+  my $sth = prepare_query($form, $dbh, $query);
+
+  unless ($params{validity}) {
+    foreach my $config_id (listify($params{config_id})) {
+      foreach my $trans_id (listify($params{trans_id})) {
+        do_statement($form, $sth, $query, conv_i($config_id), conv_i($trans_id));
+      }
+    }
+  }
+
+  $sth->finish();
+
+  return 1;
+}
+
+my %_validity_sub_module_mapping = (
+  orderitems           => { table => 'orderitems',           result_column => 'parts_id', trans_id_column => 'id', },
+  delivery_order_items => { table => 'delivery_order_items', result_column => 'parts_id', trans_id_column => 'id', },
+  invoice              => { table => 'invoice',              result_column => 'parts_id', trans_id_column => 'id', },
+);
+
+sub get_custom_variables_validity {
+  my $self     = shift;
+  my %params   = @_;
+
+  Common::check_params(\%params, qw(config_id trans_id));
+
+  my $myconfig = \%main::myconfig;
+  my $form     = $main::form;
+
+  my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
+
+  my $query;
+
+  if ($params{sub_module}) {
+    my %mapping = %{ $_validity_sub_module_mapping{ $params{sub_module} } || croak("Invalid sub_module '" . $params{sub_module} . "'") };
+    $query = <<SQL;
+      SELECT cvv.id
+      FROM $mapping{table} mt
+      LEFT JOIN custom_variables_validity cvv ON (cvv.trans_id = mt.$mapping{result_column})
+      WHERE (cvv.config_id                = ?)
+        AND (mt.$mapping{trans_id_column} = ?)
+      LIMIT 1
+SQL
+  } else {
+    $query = <<SQL;
+      SELECT id
+      FROM custom_variables_validity
+      WHERE (config_id = ?)
+        AND (trans_id  = ?)
+      LIMIT 1
+SQL
+  }
+
+  my ($invalid) = selectfirst_array_query($form, $dbh, $query, conv_i($params{config_id}), conv_i($params{trans_id}));
+
+  return !$invalid;
+}
+
+sub custom_variables_validity_by_trans_id {
+  $main::lxdebug->enter_sub(2);
+
+  my $self     = shift;
+  my %params   = @_;
+
+  return sub { 0 } unless $params{trans_id};
+
+  my $myconfig = \%main::myconfig;
+  my $form     = $main::form;
+
+  my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
+
+  my $query    = qq|SELECT DISTINCT config_id FROM custom_variables_validity WHERE trans_id = ?|;
+
+  my %invalids = map { +($_->{config_id} => 1) } selectall_hashref_query($form, $dbh, $query, $params{trans_id});
+
+  $main::lxdebug->leave_sub(2);
+
+  return sub { !$invalids{+shift} };
+}
+
+sub parse {
+  my ($self, $value, $config) = @_;
+
+  return $::form->parse_amount(\%::myconfig, $value)          if $config->{type} eq 'number';
+  return DateTime->from_lxoffice($value)                      if $config->{type} eq 'date';
+  return !ref $value ? SL::DB::Manager::Customer->find_by(id => $value * 1) : $value  if $config->{type} eq 'customer';
+  return !ref $value ? SL::DB::Manager::Vendor->find_by(id => $value * 1)   : $value  if $config->{type} eq 'vendor';
+  return !ref $value ? SL::DB::Manager::Part->find_by(id => $value * 1)     : $value  if $config->{type} eq 'part';
+  return $value;
+}
+
+sub format_to_template {
+  my ($self, $value, $config) = @_;
+  # stupid template expects everything formated. except objects
+  # do not use outside of print routines for legacy templates
+
+  return $::form->format_amount(\%::myconfig, $value) if $config->{type} eq 'number';
+  return $value->to_lxoffice if $config->{type} eq 'date' && blessed $value && $value->can('to_lxoffice');
+  return $value;
+}
+
+sub get_non_editable_ic_cvars {
+  $main::lxdebug->enter_sub(2);
+  my $self     = shift;
+  my %params   = @_;
+
+  Common::check_params(\%params, qw(form dbh row sub_module may_converted_from));
+  my $form               = $params{form};
+  my $dbh                = $params{dbh};
+  my $row                = $params{row};
+  my $sub_module         = $params{sub_module};
+  my $may_converted_from = $params{may_converted_from};
+
+  my $cvars;
+  if (! $form->{"${sub_module}_id_${row}"}) {
+    my $conv_from = 0;
+    foreach (@{ $may_converted_from }) {
+      if ($form->{"converted_from_${_}_id_$row"}) {
+        $cvars = CVar->get_custom_variables(dbh        => $dbh,
+                                            module     => 'IC',
+                                            sub_module => $_,
+                                            trans_id   => $form->{"converted_from_${_}_id_$row"},
+                                           );
+        $conv_from = 1;
+        last;
+      }
+    }
+    # get values for CVars from master data for new items
+    if (!$conv_from) {
+      $cvars = CVar->get_custom_variables(dbh      => $dbh,
+                                          module   => 'IC',
+                                          trans_id => $form->{"id_$row"},
+                                         );
+    }
+  } else {
+    # get values for CVars from custom_variables for existing items
+    $cvars = CVar->get_custom_variables(dbh        => $dbh,
+                                        module     => 'IC',
+                                        sub_module => $sub_module,
+                                        trans_id   => $form->{"${sub_module}_id_${row}"},
+                                       );
+  }
+  # map only non-editable CVars to form
+  foreach (@{ $cvars }) {
+    next if $_->{flag_editable};
+    $form->{"ic_cvar_$_->{name}_$row"} = $_->{value}
+  }
+
+  $main::lxdebug->leave_sub(2);
+}