-sub save {
-  $main::lxdebug->enter_sub();
-
-  my ($self, $myconfig, $form) = @_;
-  my @values;
-  # connect to database, turn off AutoCommit
-  my $dbh = $form->get_standard_dbh;
-  my $restricter = SL::HTML::Restrict->create;
-
-  # save the part
-  # make up a unique handle and store in partnumber field
-  # then retrieve the record based on the unique handle to get the id
-  # replace the partnumber field with the actual variable
-  # add records for makemodel
-
-  # if there is a $form->{id} then replace the old entry
-  # delete all makemodel entries and add the new ones
-
-  # undo amount formatting
-  map { $form->{$_} = $form->parse_amount($myconfig, $form->{$_}) }
-    qw(rop weight listprice sellprice gv lastcost);
-
-  my $makemodel = ($form->{make_1} || $form->{model_1} || ($form->{makemodel_rows} > 1)) ? 1 : 0;
-
-  $form->{assembly} = ($form->{item} eq 'assembly') ? 1 : 0;
-
-  my ($query, $sth);
-
-  my $priceupdate = ', priceupdate = current_date';
-
-  if ($form->{id}) {
-    my $trans_number = SL::TransNumber->new(type => $form->{item}, dbh => $dbh, number => $form->{partnumber}, id => $form->{id});
-    if (!$trans_number->is_unique) {
-      $::lxdebug->leave_sub;
-      return 3;
-    }
-
-    # get old price
-    $query = qq|SELECT sellprice, weight FROM parts WHERE id = ?|;
-    my ($sellprice, $weight) = selectrow_query($form, $dbh, $query, conv_i($form->{id}));
-
-    # if item is part of an assembly adjust all assemblies
-    $query = qq|SELECT id, qty FROM assembly WHERE parts_id = ?|;
-    $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
-    while (my ($id, $qty) = $sth->fetchrow_array) {
-      &update_assembly($dbh, $form, $id, $qty, $sellprice * 1, $weight * 1);
-    }
-    $sth->finish;
-
-    # delete makemodel records
-    do_query($form, $dbh, qq|DELETE FROM makemodel WHERE parts_id = ?|, conv_i($form->{id}));
-
-    if ($form->{item} eq 'assembly') {
-      # delete assembly records
-      do_query($form, $dbh, qq|DELETE FROM assembly WHERE id = ?|, conv_i($form->{id}));
-    }
-
-    # delete translations
-    do_query($form, $dbh, qq|DELETE FROM translation WHERE parts_id = ?|, conv_i($form->{id}));
-
-    # Check whether or not the prices have changed. If they haven't
-    # then 'priceupdate' should not be updated.
-    my $previous_values = selectfirst_hashref_query($form, $dbh, qq|SELECT * FROM parts WHERE id = ?|, conv_i($form->{id})) || {};
-    $priceupdate        = '' if (all { $previous_values->{$_} == $form->{$_} } qw(sellprice lastcost listprice));
-
-  } else {
-    my $trans_number = SL::TransNumber->new(type => $form->{item}, dbh => $dbh, number => $form->{partnumber}, save => 1);
-
-    if ($form->{partnumber} && !$trans_number->is_unique) {
-      $::lxdebug->leave_sub;
-      return 3;
-    }
-
-    $form->{partnumber} ||= $trans_number->create_unique;
-
-    ($form->{id}) = selectrow_query($form, $dbh, qq|SELECT nextval('id')|);
-    do_query($form, $dbh, qq|INSERT INTO parts (id, partnumber, unit) VALUES (?, ?, ?)|, $form->{id}, $form->{partnumber}, $form->{unit});
-
-    $form->{orphaned} = 1;
-  }
-  my $partsgroup_id = undef;
-
-  if ($form->{partsgroup}) {
-    (my $partsgroup, $partsgroup_id) = split(/--/, $form->{partsgroup});
-  }
-
-  my ($subq_inventory, $subq_expense, $subq_income);
-  if ($form->{"item"} eq "part") {
-    $subq_inventory =
-      qq|(SELECT bg.inventory_accno_id
-          FROM buchungsgruppen bg
-          WHERE bg.id = | . conv_i($form->{"buchungsgruppen_id"}, 'NULL') . qq|)|;
-  } else {
-    $subq_inventory = "NULL";
-  }
-
-  if ($form->{"item"} ne "assembly") {
-    $subq_expense =
-      qq|(SELECT tc.expense_accno_id
-          FROM taxzone_charts tc
-          WHERE tc.buchungsgruppen_id = | . conv_i($form->{"buchungsgruppen_id"}, 'NULL') . qq| and tc.taxzone_id = 0)|;
-  } else {
-    $subq_expense = "NULL";
-  }
-
-  normalize_text_blocks();
-
-  $query =
-    qq|UPDATE parts SET
-         partnumber = ?,
-         description = ?,
-         makemodel = ?,
-         alternate = 'f',
-         assembly = ?,
-         listprice = ?,
-         sellprice = ?,
-         lastcost = ?,
-         weight = ?,
-         unit = ?,
-         notes = ?,
-         formel = ?,
-         rop = ?,
-         warehouse_id = ?,
-         bin_id = ?,
-         buchungsgruppen_id = ?,
-         payment_id = ?,
-         inventory_accno_id = $subq_inventory,
-         income_accno_id = (SELECT tc.income_accno_id FROM taxzone_charts tc WHERE tc.taxzone_id = 0 and tc.buchungsgruppen_id = ?),
-         expense_accno_id = $subq_expense,
-         obsolete = ?,
-         image = ?,
-         drawing = ?,
-         shop = ?,
-         ve = ?,
-         gv = ?,
-         ean = ?,
-         has_sernumber = ?,
-         not_discountable = ?,
-         microfiche = ?,
-         partsgroup_id = ?,
-         price_factor_id = ?
-         $priceupdate
-       WHERE id = ?|;
-  @values = ($form->{partnumber},
-             $form->{description},
-             $makemodel ? 't' : 'f',
-             $form->{assembly} ? 't' : 'f',
-             $form->{listprice},
-             $form->{sellprice},
-             $form->{lastcost},
-             $form->{weight},
-             $form->{unit},
-             $restricter->process($form->{notes}),
-             $form->{formel},
-             $form->{rop},
-             conv_i($form->{warehouse_id}),
-             conv_i($form->{bin_id}),
-             conv_i($form->{buchungsgruppen_id}),
-             conv_i($form->{payment_id}),
-             conv_i($form->{buchungsgruppen_id}),
-             $form->{obsolete} ? 't' : 'f',
-             $form->{image},
-             $form->{drawing},
-             $form->{shop} ? 't' : 'f',
-             conv_i($form->{ve}),
-             conv_i($form->{gv}),
-             $form->{ean},
-             $form->{has_sernumber} ? 't' : 'f',
-             $form->{not_discountable} ? 't' : 'f',
-             $form->{microfiche},
-             conv_i($partsgroup_id),
-             conv_i($form->{price_factor_id}),
-             conv_i($form->{id})
-  );
-  do_query($form, $dbh, $query, @values);
-
-  # delete translation records
-  do_query($form, $dbh, qq|DELETE FROM translation WHERE parts_id = ?|, conv_i($form->{id}));
-
-  my @translations = grep { $_->{language_id} && $_->{translation} } @{ $form->{translations} || [] };
-  if (@translations) {
-    $query = qq|INSERT into translation (parts_id, language_id, translation, longdescription)
-                VALUES ( ?, ?, ?, ? )|;
-    $sth   = $dbh->prepare($query);
-
-    foreach my $translation (@translations) {
-      do_statement($form, $sth, $query, conv_i($form->{id}), conv_i($translation->{language_id}), $translation->{translation}, $restricter->process($translation->{longdescription}));
-    }
-
-    $sth->finish();
-  }
-
-  # delete price records
-  do_query($form, $dbh, qq|DELETE FROM prices WHERE parts_id = ?|, conv_i($form->{id}));
-
-  $query = qq|INSERT INTO prices (parts_id, pricegroup_id, price) VALUES(?, ?, ?)|;
-  $sth   = prepare_query($form, $dbh, $query);
-
-  for my $i (1 .. $form->{price_rows}) {
-    my $price = $form->parse_amount($myconfig, $form->{"price_$i"});
-    next unless $price;
-
-    @values = (conv_i($form->{id}), conv_i($form->{"pricegroup_id_$i"}), $price);
-    do_statement($form, $sth, $query, @values);
-  }
-
-  $sth->finish;
-
-  # insert makemodel records
-    my $lastupdate = '';
-    my $value = 0;
-    for my $i (1 .. $form->{makemodel_rows}) {
-      if (($form->{"make_$i"}) || ($form->{"model_$i"})) {
-        #hli
-        $value = $form->parse_amount($myconfig, $form->{"lastcost_$i"});
-        if ($value == $form->parse_amount($myconfig, $form->{"old_lastcost_$i"}))
-        {
-            if ($form->{"lastupdate_$i"} eq "") {
-                $lastupdate = 'now()';
-            } else {
-                $lastupdate = $dbh->quote($form->{"lastupdate_$i"});
-            }
-        } else {
-            $lastupdate = 'now()';
-        }
-        $query = qq|INSERT INTO makemodel (parts_id, make, model, lastcost, lastupdate, sortorder) | .
-                 qq|VALUES (?, ?, ?, ?, ?, ?)|;
-        @values = (conv_i($form->{id}), conv_i($form->{"make_$i"}), $form->{"model_$i"}, $value, $lastupdate, conv_i($form->{"sortorder_$i"}) );
-
-        do_query($form, $dbh, $query, @values);
-      }
-    }
-
-  # add assembly records
-  if ($form->{item} eq 'assembly') {
-    # check additional assembly row
-    my $i = $form->{assembly_rows};
-    # if last row is not empty add them
-    if ($form->{"partnumber_$i"} ne "") {
-      $query = qq|SELECT id FROM parts WHERE partnumber = ?|;
-      my ($partid) = selectrow_query($form, $dbh, $query,$form->{"partnumber_$i"} );
-      if ( $partid ) {
-        $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
-        $form->{"id_$i"} = $partid;
-        $form->{"bom_$i"} = 0;
-        $form->{assembly_rows}++;
-      }
-      else {
-        $::form->error($::locale->text("uncorrect partnumber ").$form->{"partnumber_$i"});
-      }
-    }
-
-    for my $i (1 .. $form->{assembly_rows}) {
-      $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
-
-      if ($form->{"qty_$i"} != 0) {
-        $form->{"bom_$i"} *= 1;
-        $query = qq|INSERT INTO assembly (id, parts_id, qty, bom) | .
-                 qq|VALUES (?, ?, ?, ?)|;
-        @values = (conv_i($form->{id}), conv_i($form->{"id_$i"}), conv_i($form->{"qty_$i"}), $form->{"bom_$i"} ? 't' : 'f');
-        do_query($form, $dbh, $query, @values);
-      }
-    }
-    my @a = localtime;
-    $a[5] += 1900;
-    $a[4]++;
-    my $shippingdate = "$a[5]-$a[4]-$a[3]";
-
-    $form->get_employee($dbh);
-
-  }
-
-  #set expense_accno=inventory_accno if they are different => bilanz
-  my $vendor_accno =
-    ($form->{expense_accno} != $form->{inventory_accno})
-    ? $form->{inventory_accno}
-    : $form->{expense_accno};
-
-  # get tax rates and description
-  my $accno_id =
-    ($form->{vc} eq "customer") ? $form->{income_accno} : $vendor_accno;
-  $query =
-    qq|SELECT c.accno, c.description, t.rate, t.taxnumber
-       FROM chart c, tax t
-       WHERE (c.id = t.chart_id) AND (t.taxkey IN (SELECT taxkey_id FROM chart where accno = ?))
-       ORDER BY c.accno|;
-  my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
-
-  $form->{taxaccount} = "";
-  while (my $ptr = $stw->fetchrow_hashref("NAME_lc")) {
-    $form->{taxaccount} .= "$ptr->{accno} ";
-    if (!($form->{taxaccount2} =~ /\Q$ptr->{accno}\E/)) {
-      $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
-      $form->{"$ptr->{accno}_description"} = $ptr->{description};
-      $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
-      $form->{taxaccount2} .= " $ptr->{accno} ";
-    }
-  }
-
-  CVar->save_custom_variables(dbh           => $dbh,
-                              module        => 'IC',
-                              trans_id      => $form->{id},
-                              variables     => $form,
-                              save_validity => 1);
-
-  # commit
-  my $rc = $dbh->commit;
-
-  $main::lxdebug->leave_sub();
-
-  return $rc;
-}
-
-sub update_assembly {
-  $main::lxdebug->enter_sub();
-
-  my ($dbh, $form, $id, $qty, $sellprice, $weight) = @_;
-
-  my $query = qq|SELECT id, qty FROM assembly WHERE parts_id = ?|;
-  my $sth = prepare_execute_query($form, $dbh, $query, conv_i($id));
-
-  while (my ($pid, $aqty) = $sth->fetchrow_array) {
-    &update_assembly($dbh, $form, $pid, $aqty * $qty, $sellprice, $weight);
-  }
-  $sth->finish;
-
-  $query =
-    qq|UPDATE parts SET sellprice = sellprice + ?, weight = weight + ?
-       WHERE id = ?|;
-  my @values = ($qty * ($form->{sellprice} - $sellprice),
-             $qty * ($form->{weight} - $weight), conv_i($id));
-  do_query($form, $dbh, $query, @values);
-
-  $main::lxdebug->leave_sub();
-}
-
-sub retrieve_assemblies {
-  $main::lxdebug->enter_sub();
-
-  my ($self, $myconfig, $form) = @_;
-
-  # connect to database
-  my $dbh = $form->get_standard_dbh;
-
-  my $where = qq|NOT p.obsolete|;
-  my @values;
-
-  if ($form->{partnumber}) {
-    $where .= qq| AND (p.partnumber ILIKE ?)|;
-    push(@values, '%' . $form->{partnumber} . '%');
-  }
-
-  if ($form->{description}) {
-    $where .= qq| AND (p.description ILIKE ?)|;
-    push(@values, '%' . $form->{description} . '%');
-  }
-
-  # retrieve assembly items
-  my $query =
-    qq|SELECT p.id, p.partnumber, p.description,
-              p.onhand, p.rop,
-         (SELECT sum(p2.inventory_accno_id)
-          FROM parts p2, assembly a
-          WHERE (p2.id = a.parts_id) AND (a.id = p.id)) AS inventory
-       FROM parts p
-       WHERE NOT p.obsolete AND p.assembly $where|;
-
-  $form->{assembly_items} = selectall_hashref_query($form, $dbh, $query, @values);
-
-  $main::lxdebug->leave_sub();
-}
-
-sub delete {
-  $main::lxdebug->enter_sub();
-
-  my ($self, $myconfig, $form) = @_;
-  my @values = (conv_i($form->{id}));
-  # connect to database, turn off AutoCommit
-  my $dbh = $form->get_standard_dbh;
-
-  my %columns = ( "assembly" => "id", "parts" => "id" );
-
-  for my $table (qw(prices makemodel inventory assembly translation parts)) {
-    my $column = defined($columns{$table}) ? $columns{$table} : "parts_id";
-    do_query($form, $dbh, qq|DELETE FROM $table WHERE $column = ?|, @values);
-  }
-
-  # commit
-  my $rc = $dbh->commit;
-
-  $main::lxdebug->leave_sub();
-
-  return $rc;
-}
-