Verkäufer-/Bearbeiter-Drop-Downs in mehreren Masken sortiert anzeigen
[kivitendo-erp.git] / bin / mozilla / ic.pl
index 8016874..473467b 100644 (file)
@@ -38,6 +38,7 @@ use List::MoreUtils qw(any);
 use SL::AM;
 use SL::CVar;
 use SL::IC;
 use SL::AM;
 use SL::CVar;
 use SL::IC;
+use SL::Helper::Flash;
 use SL::ReportGenerator;
 
 #use SL::PE;
 use SL::ReportGenerator;
 
 #use SL::PE;
@@ -93,7 +94,7 @@ sub add {
 sub search {
   $lxdebug->enter_sub();
 
 sub search {
   $lxdebug->enter_sub();
 
-  $auth->assert('part_service_assembly_edit');
+  $auth->assert('part_service_assembly_details');
 
   $form->{revers}       = 0;  # switch for backward sorting
   $form->{lastsort}     = ""; # memory for which table was sort at last time
 
   $form->{revers}       = 0;  # switch for backward sorting
   $form->{lastsort}     = ""; # memory for which table was sort at last time
@@ -105,8 +106,6 @@ sub search {
   $form->{title} = $locale->text($form->{title});
   $form->{title} = $locale->text('Assemblies') if ($is_xyz{is_assembly});
 
   $form->{title} = $locale->text($form->{title});
   $form->{title} = $locale->text('Assemblies') if ($is_xyz{is_assembly});
 
-  $form->{jsscript} = 1;
-
   $form->{CUSTOM_VARIABLES}                  = CVar->get_configs('module' => 'IC');
   ($form->{CUSTOM_VARIABLES_FILTER_CODE},
    $form->{CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables'      => $form->{CUSTOM_VARIABLES},
   $form->{CUSTOM_VARIABLES}                  = CVar->get_configs('module' => 'IC');
   ($form->{CUSTOM_VARIABLES_FILTER_CODE},
    $form->{CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables'      => $form->{CUSTOM_VARIABLES},
@@ -288,7 +287,6 @@ sub update_prices {
 #  $form->header;
 #
 #  print qq|
 #  $form->header;
 #
 #  print qq|
-#<body>
 #  <form method=post action=ic.pl>
 #    <table width=100%>
 #     <tr>
 #  <form method=post action=ic.pl>
 #    <table width=100%>
 #     <tr>
@@ -388,7 +386,6 @@ sub update_prices {
 #    . $locale->text('TOP100') . qq|">
 #
 #</form>
 #    . $locale->text('TOP100') . qq|">
 #
 #</form>
-#</body>
 #|;
 #  $lxdebug->leave_sub();
 #}    #end list()
 #|;
 #  $lxdebug->leave_sub();
 #}    #end list()
@@ -724,8 +721,6 @@ sub addtop100 {
   my $colspan = $#column_index + 1;
 
   print qq|
   my $colspan = $#column_index + 1;
 
   print qq|
-<body>
-
 <table width=100%>
   <tr>
     <th class=listtop colspan=$colspan>$form->{title}</th>
 <table width=100%>
   <tr>
     <th class=listtop colspan=$colspan>$form->{title}</th>
@@ -976,12 +971,10 @@ sub addtop100 {
   print qq|
 <!--    <input type=hidden name=ndxs_counter value="$form->{ndxs_counter}">-->
 
   print qq|
 <!--    <input type=hidden name=ndxs_counter value="$form->{ndxs_counter}">-->
 
-    <input class=submit type=submit name=action value="|
-    . $locale->text('choice') . qq|">
+<!--    <input class=submit type=submit name=action value="|
+    . $locale->text('choice') . qq|"> -->
 
   </form>
 
   </form>
-
-</body>
 |;
 
   $lxdebug->leave_sub();
 |;
 
   $lxdebug->leave_sub();
@@ -1016,7 +1009,7 @@ sub addtop100 {
 sub generate_report {
   $lxdebug->enter_sub();
 
 sub generate_report {
   $lxdebug->enter_sub();
 
-  $auth->assert('part_service_assembly_edit');
+  $auth->assert('part_service_assembly_details');
 
   my ($revers, $lastsort, $description);
 
 
   my ($revers, $lastsort, $description);
 
@@ -1030,6 +1023,7 @@ sub generate_report {
     'bin'                => { 'text' => $locale->text('Bin'), },
     'deliverydate'       => { 'text' => $locale->text('deliverydate'), },
     'description'        => { 'text' => $locale->text('Part Description'), },
     'bin'                => { 'text' => $locale->text('Bin'), },
     'deliverydate'       => { 'text' => $locale->text('deliverydate'), },
     'description'        => { 'text' => $locale->text('Part Description'), },
+    'notes'              => { 'text' => $locale->text('Notes'), },
     'drawing'            => { 'text' => $locale->text('Drawing'), },
     'ean'                => { 'text' => $locale->text('EAN'), },
     'image'              => { 'text' => $locale->text('Image'), },
     'drawing'            => { 'text' => $locale->text('Drawing'), },
     'ean'                => { 'text' => $locale->text('EAN'), },
     'image'              => { 'text' => $locale->text('Image'), },
@@ -1200,19 +1194,27 @@ sub generate_report {
 
   # soldtotal doesn't make sense with more than one bsooqr option.
   # so reset it to sold (the most common option), and issue a warning
 
   # soldtotal doesn't make sense with more than one bsooqr option.
   # so reset it to sold (the most common option), and issue a warning
+  # ...
+  # also it doesn't make sense without bsooqr. disable and issue a warning too
   my @bsooqr = qw(sold bought onorder ordered rfq quoted);
   my @bsooqr = qw(sold bought onorder ordered rfq quoted);
-  if ($form->{l_subtotal} && 1 < grep { $form->{$_} } @bsooqr) {
+  my $bsooqr_mode = grep { $form->{$_} } @bsooqr;
+  if ($form->{l_subtotal} && 1 < $bsooqr_mode) {
     my $enabled       = first { $form->{$_} } @bsooqr;
     $form->{$_}       = ''   for @bsooqr;
     $form->{$enabled} = 'Y';
 
     push @options, $::locale->text('Subtotal cannot distinguish betweens record types. Only one of the selected record types will be displayed: #1', $optiontexts{$enabled});
   }
     my $enabled       = first { $form->{$_} } @bsooqr;
     $form->{$_}       = ''   for @bsooqr;
     $form->{$enabled} = 'Y';
 
     push @options, $::locale->text('Subtotal cannot distinguish betweens record types. Only one of the selected record types will be displayed: #1', $optiontexts{$enabled});
   }
+  if ($form->{l_soldtotal} && !$bsooqr_mode) {
+    delete $form->{l_soldtotal};
+
+    flash('warning', $::locale->text('Soldtotal does not make sense without any bsooqr options'));
+  }
 
   IC->all_parts(\%myconfig, \%$form);
 
   my @columns = qw(
 
   IC->all_parts(\%myconfig, \%$form);
 
   my @columns = qw(
-    partnumber description partsgroup bin onhand rop soldtotal unit listprice
+    partnumber description notes partsgroup bin onhand rop soldtotal unit listprice
     linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
     priceupdate weight image drawing microfiche invnumber ordnumber quonumber
     transdate name serialnumber deliverydate ean projectnumber projectdescription
     linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
     priceupdate weight image drawing microfiche invnumber ordnumber quonumber
     transdate name serialnumber deliverydate ean projectnumber projectdescription
@@ -1242,8 +1244,14 @@ sub generate_report {
   map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
   map { $column_defs{$_}->{align}   = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal), @pricegroup_columns;
 
   map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
   map { $column_defs{$_}->{align}   = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal), @pricegroup_columns;
 
-  my @hidden_variables = (qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups), @itemstatus_keys, @callback_keys,
-                              map({ "cvar_$_->{name}" } @searchable_custom_variables), map { "l_$_" } @columns);
+  my @hidden_variables = (
+    qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups),
+    @itemstatus_keys,
+    @callback_keys,
+    map({ "cvar_$_->{name}" } @searchable_custom_variables),
+    map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
+    map({ "l_$_" } @columns),
+  );
 
   my $callback         = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
 
 
   my $callback         = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
 
@@ -1266,7 +1274,7 @@ sub generate_report {
     'assembly' => $locale->text('assembly_list'),
   );
 
     'assembly' => $locale->text('assembly_list'),
   );
 
-  $report->set_options('top_info_text'         => $locale->text('Options') . ': ' . join(', ', grep $_, @options),
+  $report->set_options('raw_top_info_text'     => $form->parse_html_template('ic/generate_report_top', { options => \@options }),
                        'raw_bottom_info_text'  => $form->parse_html_template('ic/generate_report_bottom'),
                        'output_format'         => 'HTML',
                        'title'                 => $form->{title},
                        'raw_bottom_info_text'  => $form->parse_html_template('ic/generate_report_bottom'),
                        'output_format'         => 'HTML',
                        'title'                 => $form->{title},
@@ -1316,7 +1324,7 @@ sub generate_report {
     $ref->{lastcost}      *= $ref->{exchangerate} / $ref->{price_factor};
 
     # use this for assemblies
     $ref->{lastcost}      *= $ref->{exchangerate} / $ref->{price_factor};
 
     # use this for assemblies
-    my $soldtotal = $ref->{soldtotal};
+    my $soldtotal = $bsooqr_mode ? $ref->{soldtotal} : $ref->{onhand};
 
     if ($ref->{assemblyitem}) {
       $row->{partnumber}{align}   = 'right';
 
     if ($ref->{assemblyitem}) {
       $row->{partnumber}{align}   = 'right';
@@ -1354,8 +1362,8 @@ sub generate_report {
     if ($ref->{module} eq 'oe') {
       # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
       #
     if ($ref->{module} eq 'oe') {
       # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
       #
-      # | ist bestellt  | Vom Kunde bestellt |  -> edit_oe_ord_link
-      # | Anfrage       | Angebot            |  -> edit_oe_quo_link
+      # | ist bestellt  | Von Kunden bestellt |  -> edit_oe_ord_link
+      # | Anfrage       | Angebot             |  -> edit_oe_quo_link
 
       my $edit_oe_ord_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'purchase_order' : 'sales_order'), 'id=' . E($ref->{trans_id}), 'callback');
       my $edit_oe_quo_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'request_quotation' : 'sales_quotation'), 'id=' . E($ref->{trans_id}), 'callback');
 
       my $edit_oe_ord_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'purchase_order' : 'sales_order'), 'id=' . E($ref->{trans_id}), 'callback');
       my $edit_oe_quo_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'request_quotation' : 'sales_quotation'), 'id=' . E($ref->{trans_id}), 'callback');
@@ -1461,7 +1469,7 @@ sub parts_subtotal {
 sub edit {
   $lxdebug->enter_sub();
 
 sub edit {
   $lxdebug->enter_sub();
 
-  $auth->assert('part_service_assembly_edit');
+  $auth->assert('part_service_assembly_details');
 
   # show history button
   $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
 
   # show history button
   $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
@@ -1482,13 +1490,12 @@ sub edit {
 sub link_part {
   $lxdebug->enter_sub();
 
 sub link_part {
   $lxdebug->enter_sub();
 
-  $auth->assert('part_service_assembly_edit');
+  $auth->assert('part_service_assembly_details');
 
   IC->create_links("IC", \%myconfig, \%$form);
 
   # currencies
 
   IC->create_links("IC", \%myconfig, \%$form);
 
   # currencies
-  map({ $form->{selectcurrency} .= "<option>$_\n" }
-      split(/:/, $form->{currencies}));
+  map({ $form->{selectcurrency} .= "<option>$_\n" } $::form->get_all_currencies());
 
   # parts and assemblies have the same links
   my $item = $form->{item};
 
   # parts and assemblies have the same links
   my $item = $form->{item};
@@ -1573,7 +1580,7 @@ sub link_part {
 sub form_header {
   $lxdebug->enter_sub();
 
 sub form_header {
   $lxdebug->enter_sub();
 
-  $auth->assert('part_service_assembly_edit');
+  $auth->assert('part_service_assembly_details');
 
   $form->{pg_keys}          = sub { "$_[0]->{partsgroup}--$_[0]->{id}" };
   $form->{description_area} = ($form->{rows} = $form->numtextrows($form->{description}, 40)) > 1;
 
   $form->{pg_keys}          = sub { "$_[0]->{partsgroup}--$_[0]->{id}" };
   $form->{description_area} = ($form->{rows} = $form->numtextrows($form->{description}, 40)) > 1;
@@ -1584,22 +1591,38 @@ sub form_header {
 
   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
                    'partsgroup'    => 'all_partsgroup',
 
   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
                    'partsgroup'    => 'all_partsgroup',
-                   'vendors'       => 'ALL_VENDORS',);
+                   'vendors'       => 'ALL_VENDORS',
+                   'warehouses'    => { 'key'    => 'WAREHOUSES',
+                                        'bins'   => 'BINS', });
+  # leerer wert für Lager und Lagerplatz korrekt einstellt
+  # ID 0 sollte in Ordnung sein, da der Zähler sowieso höher ist
+  my $no_default_bin_entry = { 'id' => '0', description => '--', 'BINS' => [ { id => '0', description => ''} ] };
+  push @ { $form->{WAREHOUSES} }, $no_default_bin_entry;
+  if (my $max = scalar @{ $form->{WAREHOUSES} }) {
+    my ($default_warehouse_id, $default_bin_id);
+    if ($form->{action} eq 'add') { # default only for new entries
+      $default_warehouse_id = $::instance_conf->get_warehouse_id;
+      $default_bin_id       = $::instance_conf->get_bin_id;
+    }
+    $form->{warehouse_id} ||= $default_warehouse_id || $form->{WAREHOUSES}->[$max -1]->{id};
+    $form->{bin_id}       ||= $default_bin_id       ||  $form->{WAREHOUSES}->[$max -1]->{BINS}->[0]->{id};
+  }
 
 
+  $form->{LANGUAGES}        = SL::DB::Manager::Language->get_all_sorted;
+  $form->{translations_map} = { map { ($_->{language_id} => $_) } @{ $form->{translations} || [] } };
 
   IC->retrieve_buchungsgruppen(\%myconfig, $form);
   @{ $form->{BUCHUNGSGRUPPEN} } = grep { $_->{id} eq $form->{buchungsgruppen_id} || ($form->{id} && $form->{orphaned}) || !$form->{id} } @{ $form->{BUCHUNGSGRUPPEN} };
 
 
   IC->retrieve_buchungsgruppen(\%myconfig, $form);
   @{ $form->{BUCHUNGSGRUPPEN} } = grep { $_->{id} eq $form->{buchungsgruppen_id} || ($form->{id} && $form->{orphaned}) || !$form->{id} } @{ $form->{BUCHUNGSGRUPPEN} };
 
-  # use JavaScript Calendar or not (yes!)
-  $form->{jsscript} = 1;
+  if (($form->{partnumber} ne '') && !SL::TransNumber->new(number => $form->{partnumber}, type => $form->{item}, id => $form->{id})->is_unique) {
+    flash('info', $::locale->text('This partnumber is not unique. You should change it.'));
+  }
 
   my $units = AM->retrieve_units(\%myconfig, $form);
   $form->{ALL_UNITS} = [ map +{ name => $_ }, sort { $units->{$a}{sortkey} <=> $units->{$b}{sortkey} } keys %$units ];
 
   $form->{defaults} = AM->get_defaults();
 
 
   my $units = AM->retrieve_units(\%myconfig, $form);
   $form->{ALL_UNITS} = [ map +{ name => $_ }, sort { $units->{$a}{sortkey} <=> $units->{$b}{sortkey} } keys %$units ];
 
   $form->{defaults} = AM->get_defaults();
 
-  $form->{fokus} = "ic.partnumber";
-
   $form->{CUSTOM_VARIABLES} = CVar->get_custom_variables('module' => 'IC', 'trans_id' => $form->{id});
 
   CVar->render_inputs('variables' => $form->{CUSTOM_VARIABLES}, show_disabled_message => 1)
   $form->{CUSTOM_VARIABLES} = CVar->get_custom_variables('module' => 'IC', 'trans_id' => $form->{id});
 
   CVar->render_inputs('variables' => $form->{CUSTOM_VARIABLES}, show_disabled_message => 1)
@@ -1611,6 +1634,9 @@ sub form_header {
   #                                                     BUCHUNGSGRUPPEN   => $form->{BUCHUNGSGRUPPEN},
   #                                                     payment_terms     => $form->{payment_terms},
   #                                                     all_partsgroup    => $form->{all_partsgroup}});
   #                                                     BUCHUNGSGRUPPEN   => $form->{BUCHUNGSGRUPPEN},
   #                                                     payment_terms     => $form->{payment_terms},
   #                                                     all_partsgroup    => $form->{all_partsgroup}});
+
+  $form->{show_edit_buttons} = $main::auth->check_right($form->{login}, 'part_service_assembly_edit');
+
   print $form->parse_html_template('ic/form_header');
   $lxdebug->leave_sub();
 }
   print $form->parse_html_template('ic/form_header');
   $lxdebug->leave_sub();
 }
@@ -1618,7 +1644,7 @@ sub form_header {
 sub form_footer {
   $lxdebug->enter_sub();
 
 sub form_footer {
   $lxdebug->enter_sub();
 
-  $auth->assert('part_service_assembly_edit');
+  $auth->assert('part_service_assembly_details');
 
   print $form->parse_html_template('ic/form_footer');
 
 
   print $form->parse_html_template('ic/form_footer');
 
@@ -1669,15 +1695,15 @@ sub assembly_row {
   }
 
   my %header = (
   }
 
   my %header = (
-   runningnumber => { text =>  $locale->text('No.'),              nowrap => 1, width => '5%'  },
-   qty           => { text =>  $locale->text('Qty'),              nowrap => 1, width => '10%' },
-   unit          => { text =>  $locale->text('Unit'),             nowrap => 1, width => '5%'  },
-   partnumber    => { text =>  $locale->text('Part Number'),      nowrap => 1, width => '20%' },
-   description   => { text =>  $locale->text('Part Description'), nowrap => 1, width => '50%' },
-   lastcost      => { text =>  $locale->text('Purchase Prices'),  nowrap => 1, width => '50%' },
-   total         => { text =>  $locale->text('Sale Prices'),      nowrap => 1,                },
-   bom           => { text =>  $locale->text('BOM'),                                          },
-   partsgroup    => { text =>  $locale->text('Group'),                                        },
+   runningnumber => { text =>  $locale->text('No.'),              nowrap => 1, width => '5%',  align => 'left',},
+   qty           => { text =>  $locale->text('Qty'),              nowrap => 1, width => '10%', align => 'left',},
+   unit          => { text =>  $locale->text('Unit'),             nowrap => 1, width => '5%',  align => 'left',},
+   partnumber    => { text =>  $locale->text('Part Number'),      nowrap => 1, width => '20%', align => 'left',},
+   description   => { text =>  $locale->text('Part Description'), nowrap => 1, width => '50%', align => 'left',},
+   lastcost      => { text =>  $locale->text('Purchase Prices'),  nowrap => 1, width => '50%', align => 'right',},
+   total         => { text =>  $locale->text('Sale Prices'),      nowrap => 1,                 align => 'right',},
+   bom           => { text =>  $locale->text('BOM'),                                           align => 'center',},
+   partsgroup    => { text =>  $locale->text('Group'),                                         align => 'left',},
   );
 
   my @ROWS;
   );
 
   my @ROWS;
@@ -1694,7 +1720,7 @@ sub assembly_row {
     $form->{"qty_$i"}    = $form->format_amount(\%myconfig, $form->{"qty_$i"});
     $linetotal           = $form->format_amount(\%myconfig, $linetotal, 2);
     $line_purchase_price = $form->format_amount(\%myconfig, $line_purchase_price, 2);
     $form->{"qty_$i"}    = $form->format_amount(\%myconfig, $form->{"qty_$i"});
     $linetotal           = $form->format_amount(\%myconfig, $linetotal, 2);
     $line_purchase_price = $form->format_amount(\%myconfig, $line_purchase_price, 2);
-    $href                = qq|$form->{script}?action=edit&id=$form->{"id_$i"}&rowcount=$i&previousform=$previousform|;
+    $href                = build_std_url("action=edit", qq|id=$form->{"id_$i"}|, "rowcount=$numrows", "currow=$i", "previousform=$previousform");
     map { $row{$_}{data} = "" } qw(qty unit partnumber description bom partsgroup runningnumber);
 
     # last row
     map { $row{$_}{data} = "" } qw(qty unit partnumber description bom partsgroup runningnumber);
 
     # last row
@@ -1714,7 +1740,8 @@ sub assembly_row {
         $row{bom}{data}           = $form->{"bom_$i"} ? "x" : "&nbsp;";
         $row{qty}{align}          = 'right';
       } else {
         $row{bom}{data}           = $form->{"bom_$i"} ? "x" : "&nbsp;";
         $row{qty}{align}          = 'right';
       } else {
-        $row{partnumber}{data}    = qq|<a href=$href>$form->{"partnumber_$i"}</a>|;
+        $row{partnumber}{data}    = qq|$form->{"partnumber_$i"}|;
+        $row{partnumber}{link}     = $href;
         $row{qty}{data}           = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
         $row{runningnumber}{data} = qq|<input name="runningnumber_$i" size=3 value="$i">|;
         $row{bom}{data}   = sprintf qq|<input name="bom_$i" type=checkbox class=checkbox value=1 %s>|,
         $row{qty}{data}           = qq|<input name="qty_$i" size=5 value="$form->{"qty_$i"}">|;
         $row{runningnumber}{data} = qq|<input name="runningnumber_$i" size=3 value="$i">|;
         $row{bom}{data}   = sprintf qq|<input name="bom_$i" type=checkbox class=checkbox value=1 %s>|,
@@ -1752,14 +1779,22 @@ sub assembly_row {
 sub update {
   $lxdebug->enter_sub();
 
 sub update {
   $lxdebug->enter_sub();
 
+  $auth->assert('part_service_assembly_edit');
+
   # parse pricegroups. and no, don't rely on check_form for this...
   map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
   # parse pricegroups. and no, don't rely on check_form for this...
   map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
+  $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) for qw(sellprice listprice ve gv);
+
+  if ($form->{item} eq 'part') {
+    $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) for qw(weight rop);
+  }
 
   # same for makemodel lastcosts
   # but parse_amount not necessary for assembly component lastcosts
   unless ($form->{item} eq "assembly") {
     map { $form->{"lastcost_$_"} = $form->parse_amount(\%myconfig, $form->{"lastcost_$_"}) } 1 .. $form->{"makemodel_rows"};
 
   # same for makemodel lastcosts
   # but parse_amount not necessary for assembly component lastcosts
   unless ($form->{item} eq "assembly") {
     map { $form->{"lastcost_$_"} = $form->parse_amount(\%myconfig, $form->{"lastcost_$_"}) } 1 .. $form->{"makemodel_rows"};
-  };
+    $form->{lastcost} = $form->parse_amount(\%myconfig, $form->{lastcost});
+  }
 
   if ($form->{item} eq "assembly") {
     my $i = $form->{assembly_rows};
 
   if ($form->{item} eq "assembly") {
     my $i = $form->{assembly_rows};
@@ -1838,6 +1873,11 @@ sub save {
   $form->error($locale->text('Description must not be empty!')) unless $form->{description};
   $form->error($locale->text('Partnumber must not be set to empty!')) if $form->{id} && !$form->{partnumber};
 
   $form->error($locale->text('Description must not be empty!')) unless $form->{description};
   $form->error($locale->text('Partnumber must not be set to empty!')) if $form->{id} && !$form->{partnumber};
 
+  # undef warehouse_id if the empty value is selected
+  if ( ($form->{warehouse_id} == 0) && ($form->{bin_id} == 0) ) {
+    undef $form->{warehouse_id};
+    undef $form->{bin_id};
+  }
   # save part
   if (IC->save(\%myconfig, \%$form) == 3) {
     $form->error($locale->text('Partnumber not unique!'));
   # save part
   if (IC->save(\%myconfig, \%$form) == 3) {
     $form->error($locale->text('Partnumber not unique!'));
@@ -1875,8 +1915,12 @@ sub save {
         qw(weight listprice sellprice rop);
 
       $form->{assembly_rows}--;
         qw(weight listprice sellprice rop);
 
       $form->{assembly_rows}--;
-      $i = $form->{assembly_rows};
-      $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
+      if ($newform{currow}) {
+        $i = $newform{currow};
+      } else {
+        $i = $form->{assembly_rows};
+      }
+      $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"} > 0);
 
       $form->{sellprice} -= $form->{"sellprice_$i"} * $form->{"qty_$i"};
       $form->{weight}    -= $form->{"weight_$i"} * $form->{"qty_$i"};
 
       $form->{sellprice} -= $form->{"sellprice_$i"} * $form->{"qty_$i"};
       $form->{weight}    -= $form->{"weight_$i"} * $form->{"qty_$i"};
@@ -1893,7 +1937,7 @@ sub save {
 
       # set values for last invoice/order item
       $i = $form->{rowcount};
 
       # set values for last invoice/order item
       $i = $form->{rowcount};
-      $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"});
+      $form->{"qty_$i"} = 1 unless ($form->{"qty_$i"} > 0);
 
       map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit listprice inventory_accno income_accno expense_accno sellprice lastcost price_factor_id);
       map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
 
       map { $form->{"${_}_$i"} = $newform{$_} } qw(partnumber description bin unit listprice inventory_accno income_accno expense_accno sellprice lastcost price_factor_id);
       map { $form->{"ic_${_}_$i"} = $newform{$_} } @ic_cvar_fields;
@@ -2006,7 +2050,7 @@ sub delete {
 sub price_row {
   $lxdebug->enter_sub();
 
 sub price_row {
   $lxdebug->enter_sub();
 
-  $auth->assert('part_service_assembly_edit');
+  $auth->assert('part_service_assembly_details');
 
   my ($numrows) = @_;
 
 
   my ($numrows) = @_;
 
@@ -2021,50 +2065,6 @@ sub price_row {
   $lxdebug->leave_sub();
 }
 
   $lxdebug->leave_sub();
 }
 
-sub parts_language_selection {
-  $lxdebug->enter_sub();
-
-  $auth->assert('part_service_assembly_edit');
-
-  my $languages = IC->retrieve_languages(\%myconfig, $form);
-
-  if ($form->{language_values} ne "") {
-    foreach my $item (split(/---\+\+\+---/, $form->{language_values})) {
-      my ($language_id, $translation, $longdescription) = split(/--\+\+--/, $item);
-
-      foreach my $language (@{ $languages }) {
-        next unless ($language->{id} == $language_id);
-
-        $language->{translation}     = $translation;
-        $language->{longdescription} = $longdescription;
-
-        $language->{translation_area}     = ($language->{translation_rows} = $form->numtextrows($language->{translation}, 40)) > 1;
-        $language->{longdescription_rows} = max 4, $form->numtextrows($language->{longdescription}, 40);
-
-        last;
-      }
-    }
-  }
-
-  my @header_sort = qw(name longdescription);
-  my %header_title = ( "name" => $locale->text("Name"),
-                       "longdescription" => $locale->text("Long Description"),
-                       );
-
-  my @header =
-    map(+{ "column_title" => $header_title{$_},
-           "column" => $_,
-         },
-        @header_sort);
-
-  $form->{"title"} = $locale->text("Language Values");
-  $form->header();
-  print $form->parse_html_template("ic/parts_language_selection", { "HEADER"    => \@header,
-                                                                    "LANGUAGES" => $languages, });
-
-  $lxdebug->leave_sub();
-}
-
 sub ajax_autocomplete {
   $main::lxdebug->enter_sub();
 
 sub ajax_autocomplete {
   $main::lxdebug->enter_sub();