+sub action_stocktaking {
+  my ($self) = @_;
+
+  $::request->{layout}->use_javascript("${_}.js") for qw(kivi.Inventory);
+  $::request->layout->focus('#part_id_name');
+  $self->setup_stock_stocktaking_action_bar;
+  $self->render('inventory/stocktaking/form', title => t8('Stocktaking'));
+}
+
+sub action_save_stocktaking {
+  my ($self) = @_;
+
+  return $self->js->flash('error', t8('Please choose a part.'))->render()
+    if !$::form->{part_id};
+
+  return $self->js->flash('error', t8('A target quantitiy has to be given'))->render()
+    if $::form->{target_qty} eq '';
+
+  my $target_qty = $::form->parse_amount(\%::myconfig, $::form->{target_qty});
+
+  return $self->js->flash('error', t8('Error: A negative target quantity is not allowed.'))->render()
+    if $target_qty < 0;
+
+  my $stocked_qty  = _get_stocked_qty($self->part,
+                                      warehouse_id => $self->warehouse->id,
+                                      bin_id       => $self->bin->id,
+                                      chargenumber => $::form->{chargenumber},
+                                      bestbefore   => $::form->{bestbefore},);
+
+  my $stocked_qty_in_form_units = $self->part->unit_obj->convert_to($stocked_qty, $self->unit);
+
+  if (!$::form->{dont_check_already_counted}) {
+    my $already_counted = _already_counted($self->part,
+                                           warehouse_id => $self->warehouse->id,
+                                           bin_id       => $self->bin->id,
+                                           cutoff_date  => $::form->{cutoff_date_as_date},
+                                           chargenumber => $::form->{chargenumber},
+                                           bestbefore   => $::form->{bestbefore});
+    if (scalar @$already_counted) {
+      my $reply = $self->js->dialog->open({
+        html   => $self->render('inventory/stocktaking/_already_counted_dialog',
+                                { output => 0 },
+                                already_counted           => $already_counted,
+                                stocked_qty               => $stocked_qty,
+                                stocked_qty_in_form_units => $stocked_qty_in_form_units),
+        id     => 'already_counted_dialog',
+        dialog => {
+          title => t8('Already counted'),
+        },
+      })->render;
+
+      return $reply;
+    }
+  }
+
+  # - target_qty is in units given in form ($self->unit)
+  # - WH->transfer expects qtys in given unit (here: unit from form (unit -> $self->unit))
+  # Therefore use stocked_qty in form units for calculation.
+  my $qty        = $target_qty - $stocked_qty_in_form_units;
+  my $src_or_dst = $qty < 0? 'src' : 'dst';
+  $qty           = abs($qty);
+
+  my $transfer_error;
+  # do stock
+  $::form->throw_on_error(sub {
+    eval {
+      WH->transfer({
+        parts                   => $self->part,
+        $src_or_dst.'_bin'      => $self->bin,
+        $src_or_dst.'_wh'       => $self->warehouse,
+        qty                     => $qty,
+        unit                    => $self->unit,
+        transfer_type           => 'stocktaking',
+        chargenumber            => $::form->{chargenumber},
+        bestbefore              => $::form->{bestbefore},
+        ean                     => $::form->{ean},
+        comment                 => $::form->{comment},
+        record_stocktaking      => 1,
+        stocktaking_qty         => $target_qty,
+        stocktaking_cutoff_date => $::form->{cutoff_date_as_date},
+      });
+      1;
+    } or do { $transfer_error = $EVAL_ERROR->error; }
+  });
+
+  return $self->js->flash('error', $transfer_error)->render()
+    if $transfer_error;
+
+  flash_later('info', $::locale->text('Part successful counted'));
+  $self->redirect_to(action              => 'stocktaking',
+                     warehouse_id        => $self->warehouse->id,
+                     bin_id              => $self->bin->id,
+                     cutoff_date_as_date => $self->stocktaking_cutoff_date->to_kivitendo);
+}
+
+sub action_reload_stocktaking_history {
+  my ($self) = @_;
+
+  $::form->{filter}{'cutoff_date:date'} = $self->stocktaking_cutoff_date->to_kivitendo;
+  $::form->{filter}{'employee_id'}      = SL::DB::Manager::Employee->current->id;
+
+  $self->prepare_stocktaking_report;
+  $self->report_generator_list_objects(report => $self->{report}, objects => $self->stocktaking_models->get, layout => 0, header => 0);
+}
+
+sub action_stocktaking_part_changed {
+  my ($self) = @_;
+
+  $self->js
+    ->replaceWith('#unit_id', $self->build_unit_select)
+    ->focus('#target_qty')
+    ->render;
+}
+
+sub action_stocktaking_journal {
+  my ($self) = @_;
+
+  $self->prepare_stocktaking_report(full => 1);
+  $self->report_generator_list_objects(report => $self->{report}, objects => $self->stocktaking_models->get);
+}
+
+sub action_stocktaking_get_warn_qty_threshold {
+  my ($self) = @_;
+
+  return $_[0]->render(\ !!0, { type => 'text' }) if !$::form->{part_id};
+  return $_[0]->render(\ !!0, { type => 'text' }) if $::form->{target_qty} eq '';
+  return $_[0]->render(\ !!0, { type => 'text' }) if 0 == $::instance_conf->get_stocktaking_qty_threshold;
+
+  my $target_qty  = $::form->parse_amount(\%::myconfig, $::form->{target_qty});
+  my $stocked_qty = _get_stocked_qty($self->part,
+                                     warehouse_id => $self->warehouse->id,
+                                     bin_id       => $self->bin->id,
+                                     chargenumber => $::form->{chargenumber},
+                                     bestbefore   => $::form->{bestbefore},);
+  my $stocked_qty_in_form_units = $self->part->unit_obj->convert_to($stocked_qty, $self->unit);
+  my $qty        = $target_qty - $stocked_qty_in_form_units;
+  $qty           = abs($qty);
+
+  my $warn;
+  if ($qty > $::instance_conf->get_stocktaking_qty_threshold) {
+    $warn  = t8('The target quantity of #1 differs more than the threshold quantity of #2.',
+                $::form->{target_qty} . " " . $self->unit->name,
+                $::form->format_amount(\%::myconfig, $::instance_conf->get_stocktaking_qty_threshold, 2));
+    $warn .= "\n";
+    $warn .= t8('Choose "continue" if you want to use this value. Choose "cancel" otherwise.');
+  }
+  return $_[0]->render(\ $warn, { type => 'text' });
+}
+