Verkaufs- und Einkaufslieferschein um Standardlagerverfahren erweitert (#2284)
authorJan Büren <jan@kivitendo-premium.de>
Tue, 4 Jun 2013 12:54:31 +0000 (14:54 +0200)
committerJan Büren <jan@kivitendo-premium.de>
Wed, 5 Jun 2013 05:57:29 +0000 (07:57 +0200)
s.a. Changelog, bzw. Trac-Eintrag 2284 hierzu.

SL/Controller/ClientConfig.pm
SL/DB/MetaSetup/Default.pm
SL/InstanceConfiguration.pm
SL/WH.pm
bin/mozilla/do.pl
doc/changelog
locale/de/all
templates/webpages/client_config/form.html
templates/webpages/do/form_footer.html

index fd39fc3..1699d04 100644 (file)
@@ -43,6 +43,12 @@ sub action_edit {
 
   map { $self->{$_} = SL::DB::Default->get->$_ } qw(sales_order_show_delete purchase_order_show_delete sales_delivery_order_show_delete purchase_delivery_order_show_delete);
 
+  # All warehouse / transfer default values
+  map { $self->{$_} = SL::DB::Default->get->$_ } qw(transfer_default transfer_default_use_master_default_bin transfer_default_ignore_onhand
+                                                    warehouse_id_ignore_onhand bin_id_ignore_onhand warehouse_id bin_id);
+
+  # for the default warehouse and bin we get the list and
+  # set a empty value with warehouse_id and bin_id = 0
   map { $self->{$_} = SL::DB::Default->get->$_ } qw(warehouse_id bin_id);
   $::form->get_lists('warehouses' => { 'key'    => 'WAREHOUSES',
                                        'bins'   => 'BINS', });
@@ -51,9 +57,12 @@ sub action_edit {
   my $no_default_bin_entry = { 'id' => '0', description => '--', 'BINS' => [ { id => '0', description => ''} ] };
   push @ { $self->{WAREHOUSES} }, $no_default_bin_entry;
 
+  # set defaults to empty
   if (my $max = scalar @{ $self->{WAREHOUSES} }) {
     $self->{warehouse_id} ||= $self->{WAREHOUSES}->[$max -1]->{id};
     $self->{bin_id}       ||= $self->{WAREHOUSES}->[$max -1]->{BINS}->[0]->{id};
+    $self->{warehouse_id_ignore_onhand} ||= $self->{WAREHOUSES}->[$max -1]->{id};
+    $self->{bin_id_ignore_onhand}       ||= $self->{WAREHOUSES}->[$max -1]->{BINS}->[0]->{id};
   }
 
   $self->{show_weight} = SL::DB::Default->get->show_weight;
@@ -84,7 +93,14 @@ sub action_save {
     undef $::form->{warehouse_id};
     undef $::form->{bin_id};
   }
-  map { SL::DB::Default->get->update_attributes($_ => $::form->{$_}); } qw(warehouse_id bin_id);
+  # undef warehouse_id_ignore_onhand if the empty value is selected
+  if ( ($::form->{warehouse_id_ignore_onhand} == 0) && ($::form->{bin_id_ignore_onhand} == 0) ) {
+    undef $::form->{warehouse_id_ignore_onhand};
+    undef $::form->{bin_id_ignore_onhand};
+  }
+
+  # All warehouse / transfer default values
+  map { SL::DB::Default->get->update_attributes($_ => $::form->{$_}); } qw(transfer_default transfer_default_use_master_default_bin transfer_default_ignore_onhand
 
   SL::DB::Default->get->update_attributes('show_weight'     => $::form->{show_weight});
 
index 6c1e5ec..cbacbf4 100644 (file)
@@ -71,26 +71,41 @@ __PACKAGE__->meta->setup(
     bin_id                              => { type => 'integer' },
     currency_id                         => { type => 'integer', not_null => 1 },
     show_weight                         => { type => 'boolean', default => 'false', not_null => 1 },
-  ],
+    transfer_default                        => { type => 'boolean', default => 'true' },
+    transfer_default_use_master_default_bin => { type => 'boolean', default => 'false' },
+    transfer_default_ignore_onhand          => { type => 'boolean', default => 'false' },
+    warehouse_id_ignore_onhand              => { type => 'integer' },
+    bin_id_ignore_onhand                    => { type => 'integer' },
+ ],
 
   primary_key_columns => [ 'id' ],
 
   allow_inline_column_values => 1,
-
   foreign_keys => [
     bin => {
       class       => 'SL::DB::Bin',
       key_columns => { bin_id => 'id' },
     },
 
-    warehouse => {
-      class       => 'SL::DB::Warehouse',
-      key_columns => { warehouse_id => 'id' },
+    bin_obj => {
+      class       => 'SL::DB::Bin',
+      key_columns => { bin_id_ignore_onhand => 'id' },
     },
+
     currency => {
       class       => 'SL::DB::Currency',
       key_columns => { currency_id => 'id' },
     },
+
+    warehouse => {
+      class       => 'SL::DB::Warehouse',
+      key_columns => { warehouse_id => 'id' },
+    },
+
+    warehouse_obj => {
+      class       => 'SL::DB::Warehouse',
+      key_columns => { warehouse_id_ignore_onhand => 'id' },
+    },
   ],
 );
 
index d9b7129..97ece86 100644 (file)
@@ -153,6 +153,32 @@ sub get_default_bin_id {
   my ($self) = @_;
   return ($self->{data}->{bin_id});
 }
+sub get_default_warehouse_id_ignore_onhand {
+  my ($self) = @_;
+  return ($self->{data}->{warehouse_id_ignore_onhand});
+}
+
+sub get_default_bin_id_ignore_onhand {
+  my ($self) = @_;
+  return ($self->{data}->{bin_id_ignore_onhand});
+}
+
+
+sub get_transfer_default {
+  my ($self) = @_;
+  return ($self->{data}->{transfer_default});
+}
+
+sub get_transfer_default_use_master_default_bin {
+  my ($self) = @_;
+  return ($self->{data}->{transfer_default_use_master_default_bin});
+}
+
+sub get_transfer_default_ignore_onhand {
+  my ($self) = @_;
+  return ($self->{data}->{transfer_default_ignore_onhand});
+}
+
 
 1;
 
@@ -274,6 +300,27 @@ Returns the default warehouse_id
 
 Returns the default bin_id
 
+=item C<get_default_warehouse_id_ignore_onhand>
+
+Returns the default warehouse_id for transfers without checking the
+current stock quantity
+
+=item C<get_default_bin_id_ignore_onhand>
+
+Returns the default bin_id for transfers without checking the.
+current stock quantity
+
+
+
+=item C<get_transfer_default>
+
+=item C<get_transfer_default_use_master_default_bin>
+
+=item C<get_transfer_default_ignore_onhand>
+
+Returns the default behavior for the transfer out default feature (true or false)
+
+
 =back
 
 =head1 BUGS
index f7ca693..3087dff 100644 (file)
--- a/SL/WH.pm
+++ b/SL/WH.pm
@@ -914,7 +914,50 @@ $main::lxdebug->enter_sub();
 
   return $part_description;
 }
+#
+# Eingabe:  Teilenummer, Lagerplatz_Id (bin_id)
+# Ausgabe:  Die maximale Anzahl der Teile in diesem Lagerplatz
+#           Bzw. Fehler, falls Chargen oder bestbefore
+#           bei eingelagerten Teilen definiert sind.
+#
+sub get_max_qty_parts_bin {
+$main::lxdebug->enter_sub();
+
+  my $self     = shift;
+  my %params   = @_;
+
+  Common::check_params(\%params, qw(parts_id bin_id)); #die brauchen wir
+
+  my $myconfig = \%main::myconfig;
+  my $form     = $main::form;
+
+  my $dbh      = $params{dbh} || $form->get_standard_dbh();
+
+  my $query = qq| SELECT SUM(qty), chargenumber, bestbefore  FROM inventory where parts_id = ?
+                            AND bin_id = ? GROUP BY chargenumber, bestbefore|;
+
+  my $sth_QTY      = prepare_execute_query($form, $dbh, $query, ,$params{parts_id}, $params{bin_id}); #info: aufruf an DBUtils.pm
 
+  my $max_qty_parts = 0; #Initialisierung mit 0
+  # falls derselbe artikel mehrmals eingelagert ist
+  # chargennummer, muss entsprechend händisch agiert werden
+  my $i = 0;
+  my $error;
+  while (my $ref = $sth_QTY->fetchrow_hashref()) {  # wir laufen über alle Haltbarkeiten und Chargen(s.a. SQL-Query oben)
+    $max_qty_parts += $ref->{sum};
+    $i++;
+    if ($ref->{chargenumber} || $ref->{bestbefore}){
+      $error=1;
+    }
+  }
+  #if ($i < 1){
+  #  $error = 2;
+  #}
+
+  $main::lxdebug->leave_sub();
+
+  return ($max_qty_parts, $error);
+}
 
 1;
 
index a155e8a..fec239f 100644 (file)
@@ -340,7 +340,8 @@ sub form_footer {
 
   $form->{PRINT_OPTIONS} = print_options('inline' => 1);
 
-  print $form->parse_html_template('do/form_footer');
+  print $form->parse_html_template('do/form_footer',
+    {transfer_default         => ($::instance_conf->get_transfer_default)});
 
   $main::lxdebug->leave_sub();
 }
@@ -1520,7 +1521,8 @@ sub dispatcher {
   my $form     = $main::form;
   my $locale   = $main::locale;
 
-  foreach my $action (qw(update ship_to print e_mail save transfer_out transfer_in mark_closed save_as_new invoice delete)) {
+  foreach my $action (qw(update ship_to print e_mail save transfer_out transfer_out_default
+                         transfer_in transfer_in_default mark_closed save_as_new invoice delete)) {
     if ($form->{"action_${action}"}) {
       call_sub($action);
       return;
@@ -1529,3 +1531,164 @@ sub dispatcher {
 
   $form->error($locale->text('No action defined.'));
 }
+
+sub transfer_out_default {
+  $main::lxdebug->enter_sub();
+
+  my $form     = $main::form;
+
+  transfer_in_out_default('direction' => 'out');
+
+  $main::lxdebug->leave_sub();
+}
+
+sub transfer_in_default {
+  $main::lxdebug->enter_sub();
+
+  my $form     = $main::form;
+
+  transfer_in_out_default('direction' => 'in');
+
+  $main::lxdebug->leave_sub();
+}
+
+# Falls das Standardlagerverfahren aktiv ist, wird
+# geprüft, ob alle Standardlagerplätze für die Auslager-
+# artikel vorhanden sind UND ob die Warenmenge ausreicht zum
+# Auslagern. Falls nicht wird entsprechend eine Fehlermeldung
+# generiert. Offen Chargennummer / bestbefore wird nicht berücksichtigt
+sub transfer_in_out_default {
+  $main::lxdebug->enter_sub();
+
+  my $form     = $main::form;
+  my %myconfig = %main::myconfig;
+  my $locale   = $main::locale;
+  my %params   = @_;
+
+  my (%missing_default_bins, %qty_parts, @all_requests, %part_info_map, $default_warehouse_id, $default_bin_id);
+
+  Common::check_params(\%params, qw(direction));
+
+  # entsprechende defaults holen, falls standardlagerplatz verwendet werden soll
+  if ($::instance_conf->get_transfer_default_use_master_default_bin) {
+    $default_warehouse_id = $::instance_conf->get_default_warehouse_id;
+    $default_bin_id       = $::instance_conf->get_default_bin_id;
+  }
+
+
+  my @part_ids = map { $form->{"id_${_}"} } (1 .. $form->{rowcount});
+  if (@part_ids) {
+    my $units         = AM->retrieve_units(\%myconfig, $form);
+    %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
+    foreach my $i (1 .. $form->{rowcount}) {
+      next unless ($form->{"id_$i"});
+      my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
+      my $qty =   $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
+      $qty_parts{$form->{"id_$i"}} += $qty;
+
+
+      $part_info_map{$form->{"id_$i"}}{bin_id}       ||= $default_bin_id;
+      $part_info_map{$form->{"id_$i"}}{warehouse_id} ||= $default_warehouse_id;
+
+      push @all_requests, {
+                        'chargenumber' => '',  #?? die müsste entsprechend geholt werden
+                        #'bestbefore' => undef, # TODO wird nicht berücksichtigt
+                        'bin_id' => $part_info_map{$form->{"id_$i"}}{bin_id},
+                        'qty' => $qty,
+                        'parts_id' => $form->{"id_$i"},
+                        'comment' => 'Default transfer DO',
+                        'ok' => 1,
+                        'unit' => $part_info_map{$form->{"id_$i"}}{unit},
+                        'warehouse_id' => $part_info_map{$form->{"id_$i"}}{warehouse_id},
+                        'oe_id' => $form->{id},
+                        'project_id' => $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id}
+                      };
+    }
+
+    # jetzt wird erst überprüft, ob die Stückzahl entsprechend stimmt.
+    if ($params{direction} eq 'out') {  # wird nur für ausgehende Mengen benötigit
+      foreach my $key (keys %qty_parts) {
+
+        $missing_default_bins{$key}{missing_bin} = 1 unless ($part_info_map{$key}{bin_id});
+        next unless ($part_info_map{$key}{bin_id}); # abbruch
+
+        my ($max_qty, $error) = WH->get_max_qty_parts_bin(parts_id => $key, bin_id => $part_info_map{$key}{bin_id});
+        if ($error == 1) {
+          # wir können nicht entscheiden, welche charge oder mhd (bestbefore) ausgewählt sein soll
+          # deshalb rückmeldung nach oben geben, manuell auszulagern
+          # TODO Bei nur einem Treffer mit Charge oder bestbefore wäre das noch möglich
+          $missing_default_bins{$key}{chargenumber} = 1;
+        }
+        if ($max_qty < $qty_parts{$key}){
+          $missing_default_bins{$key}{missing_qty} = $max_qty - $qty_parts{$key};
+        }
+      }
+    }
+  } # if @parts_id
+
+  # Abfrage für Fehlerbehandlung (nur bei direction == out)
+  if (scalar (keys %missing_default_bins)) {
+    my $fehlertext;
+    foreach my $fehler (keys %missing_default_bins) {
+
+      my $ware = WH->get_part_description(parts_id => $fehler);
+      if ($missing_default_bins{$fehler}{missing_bin}){
+        $fehlertext .= "Kein Standardlagerplatz definiert bei $ware <br>";
+      }
+      if ($missing_default_bins{$fehler}{missing_qty}) {  # missing_qty
+        $fehlertext .= "Es fehlen " . $missing_default_bins{$fehler}{missing_qty}*-1 .
+                       " von $ware auf dem Standard-Lagerplatz " . $part_info_map{$fehler}{bin} .   " zum Auslagern<br>";
+      }
+      if ($missing_default_bins{$fehler}{chargenumber}){
+        $fehlertext .= "Die Ware hat eine Chargennummer oder eine Mindesthaltbarkeit definiert.
+                        Hier kann man nicht automatisch entscheiden.
+                        Bitte diesen Lieferschein manuell auslagern.
+                        Bei: $ware";
+      }
+      # auslagern soll immer gehen, auch wenn nicht genügend auf lager ist.
+      # der lagerplatz ist hier extra konfigurierbar, bspw. Lager-Korrektur mit
+      # Lagerplatz Lagerplatz-Korrektur
+      my $default_warehouse_id_ignore_onhand = $::instance_conf->get_default_warehouse_id_ignore_onhand;
+      my $default_bin_id_ignore_onhand       = $::instance_conf->get_default_bin_id_ignore_onhand;
+      if ($::instance_conf->get_transfer_default_ignore_onhand && $default_bin_id_ignore_onhand) {
+        # entsprechende defaults holen
+        # falls chargenumber, bestbefore oder anzahl nicht stimmt, auf automatischen
+        # lagerplatz wegbuchen!
+        foreach (@all_requests) {
+          if ($_->{parts_id} eq $fehler){
+          $_->{bin_id}        = $default_bin_id_ignore_onhand;
+          $_->{warehouse_id}  = $default_warehouse_id_ignore_onhand;
+          }
+        }
+      } else {
+        #$main::lxdebug->message(0, 'Fehlertext: ' . $fehlertext);
+        $form->show_generic_error($locale->text("Cannot transfer. <br> Reason:<br>#1", $fehlertext ), 'back_button' => 1);
+      }
+    }
+  }
+
+
+  # hier der eigentliche fallunterschied für in oder out
+  my $prefix   = $params{direction} eq 'in' ? 'in' : 'out';
+
+  # dieser array_ref ist für DO->save da:
+  # einmal die all_requests in YAML verwandeln, damit delivery_order_items_stock
+  # gefüllt werden kann.
+  my $i = 1;
+  foreach (@all_requests){
+    $form->{"stock_${prefix}_$i"} = YAML::Dump([$_]);
+    $i++;
+  }
+
+  save(no_redirect => 1); # Wir können auslagern, deshalb beleg speichern
+                          # und in delivery_order_items_stock speichern
+  DO->transfer_in_out('direction' => $prefix,
+                      'requests'  => \@all_requests);
+
+  SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
+
+  $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'out';
+  $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'in';
+  $form->redirect;
+
+}
index a328ffb..6537653 100644 (file)
@@ -4,6 +4,28 @@
 
 
 
+Größere neue Features:
+
+- Lagerverwaltung sinnvoller mit Stammdaten verknüpft
+  Freitextfeld-Lagerplatz in Stammdaten durch Lager und Lagerplatz ersetzt.
+  Entsprechende Vorauswahl beim Einkaufslieferschein. Der Standardlagerplatz wird
+  schon direkt vorausgewählt.
+  Ferner wird der Standardlagerplatz unter Lager -> Einlagern entsprechend auch
+  vorausgewählt.
+  Den Standardlagerplatz kann man unter Mandantenkonfiguration voreinstellen.
+  Der voreingestellte Standardlagerplatz ist dann die Vorauswahl für neu angelegte
+  Waren.
+  Sowohl Einkaufs- als auch Verkaufslieferschein haben einen neue Funktion,
+  Ein- / Auslagern über Standardlagerplatz. Diese Funktion ist an- bzw.
+  abschaltbar in der Mandatenkonfiguration (standardmässig an).
+  Die Funktion lässt sich noch wie folgt konfigurieren:
+  - Falls kein Standardlagerplatz in den Stammdaten hinterlegt ist, verwende den
+    vorkonfigurierten Standardlagerplatz.
+  - Falls der Bestand nicht ausreicht zum Auslagern oder eine Mindesthaltbarkeit, bzw.
+    Chargennummer vergeben (welches ein Abbruchkriterium beim Auslagern ist), lager dennoch
+    aus und verwende hierfür den vorkonfigurierte Fehlbestands-, bzw. Fehlbuchungslagerplatz.
+
+
 
 
 Kleinere neue Features und Detailverbesserungen:
@@ -14,8 +36,6 @@ Kleinere neue Features und Detailverbesserungen:
   da hier die Code-Anpassungen vergessen wurde.
 - Offene Forderungen / Verbindlichkeiten
   Stichtagsbezogene Auswahl korrekt mit Fälligkeit verrechnet
-
-
 2012-12-10 - Release 3.0.0
 
 Größere neue Features:
index 5a5009c..bde70d3 100755 (executable)
@@ -400,6 +400,7 @@ $self->{texts} = {
   'Cannot save preferences!'    => 'Einstellungen können nicht gespeichert werden!',
   'Cannot save quotation!'      => 'Angebot kann nicht gespeichert werden!',
   'Cannot storno storno invoice!' => 'Kann eine Stornorechnung nicht stornieren',
+  'Cannot transfer. <br> Reason:<br>#1' => 'Kann nicht auslagern. <br>Grund:<br>#1',
   'Carry over shipping address' => 'Lieferadresse &uuml;bernehmen',
   'Cash'                        => 'Zahlungsverkehr',
   'Cc'                          => 'Cc',
@@ -617,8 +618,14 @@ $self->{texts} = {
   'Default (no language selected)' => 'Standard (keine Sprache ausgewählt)',
   'Default Accounts'            => 'Standardkonten',
   'Default Bin'                 => 'Standard-Lagerplatz',
+  'Default Bin with ignoring onhand' => 'Standard-Lagerplatz für Lagerbewegungen ohne Überprüfung auf verfügbare Menge ',
   'Default Customer/Vendor Language' => 'Standard-Kunden-/Lieferantensprache',
+  'Default Transfer'            => 'Ein- / Auslagern über Standardlagerplätze',
+  'Default Transfer Out always succeed. The current part onhand is ignored and the inventory can have negative stocks (not recommended).' => 'Auslagern über Standardlagerplatz funktioniert immer (verfügbare Menge wird nicht geprüft). Die Lagerbewegung wird auf den unten konfigurierten Lagerplatz gebucht (nicht empfohlen).',
+  'Default Transfer Out with negative inventory' => 'Standard-Auslagern ohne Prüfung auf Bestand',
+  'Default Transfer with Master Bin' => 'Standardlagerplatz für Standard-Auslagern verwenden, falls keiner für die Ware explizit definiert wurde',
   'Default Warehouse'           => 'Standard-Lager',
+  'Default Warehouse with ignoring on hand' => 'Standardlager für Auslagern ohne Prüfung auf Bestand',
   'Default buchungsgruppe'      => 'Standardbuchungsgruppe',
   'Default currency'            => 'Standardwährung',
   'Default currency missing!'   => 'Standardwährung fehlt!',
@@ -1007,9 +1014,11 @@ $self->{texts} = {
   'III'                         => 'III',
   'IV'                          => 'IV',
   'If checked the taxkey will not be exported in the DATEV Export, but only IF chart taxkeys differ from general ledger taxkeys' => 'Falls angehakt wird der DATEV-Steuerschlüssel bei Buchungen auf dieses Konto nicht beim DATEV-Export mitexportiert, allerdings nur wenn zusätzlich der Konto-Steuerschlüssel vom Buchungs (Hauptbuch) Steuerschlüssel abweicht',
+  'If configured this bin will be preselected for all new parts. Also this bin will be used as the master default bin, if default transfer out with master bin is activated.' => 'Falls konfiguriert, wird dieses Lager mit Lagerplatz für neu angelegte Waren vorausgewählt.',
   'If the article type is set to \'mixed\' then a column called \'type\' must be present.' => 'Falls der Artikeltyp auf \'gemischt\' gestellt wird, muss eine Spalte namens \'type\' vorhanden sein.',
   'If the automatic creation of invoices for fees and interest is switched on for a dunning level then the following accounts will be used for the invoice.' => 'Wenn das automatische Erstellen einer Rechnung &uuml;ber Mahngeb&uuml;hren und Zinsen f&uuml;r ein Mahnlevel aktiviert ist, so werden die folgenden Konten f&uuml;r die Rechnung benutzt.',
   'If the database user listed above does not have the right to create a database then enter the name and password of the superuser below:' => 'Falls der oben genannte Datenbankbenutzer nicht die Berechtigung zum Anlegen neuer Datenbanken hat, so k&ouml;nnen Sie hier den Namen und das Passwort des Datenbankadministratoraccounts angeben:',
+  'If the default transfer out always succeed use this bin for negative stock quantity.' => 'Standardlagerplatz für Auslagern ohne Prüfung auf Bestand',
   'If you chose to let kivitendo do the migration then kivitendo will also remove the old member file after creating a backup copy of it in the directory &quot;#1&quot;.' => 'Falls Sie sich entscheiden, kivitendo die Migration durchführen zu lassen, so wird kivitendo ein Backup der alten Dateien im Verzeichnis "#1" erstellen und die Dateien anschließend löschen.',
   'If you enter values for the part number and / or part description then only those bins containing parts whose part number or part description match your input will be shown.' => 'Wenn Sie f&uuml;r die Artikelnummer und / oder die Beschreibung etwas eingeben, so werden nur die Lagerpl&auml;tze angezeigt, in denen Waren eingelagert sind, die Ihre Suchbegriffe enthalten.',
   'If you have not chosen for example the category revenue for a tax and you choose an revenue account to create a transfer in the general ledger, this tax will not be displayed in the tax dropdown.' => 'Wenn Sie z.B. die Kategory Erlös für eine Steuer nicht gewählt haben und ein Erlöskonto beim Erstellen einer Dialogbuchung wählen, wird diese Steuer auch nicht im Dropdown-Menü für die Steuern angezeigt.',
@@ -1807,6 +1816,7 @@ $self->{texts} = {
   'Show Filter'                 => 'Filter zeigen',
   'Show Salesman'               => 'Verkäufer anzeigen',
   'Show TODO list'              => 'Aufgabenliste anzeigen',
+  'Show Transfer via default'   => 'Ein- / Auslagern über Standardlagerplatz anzeigen',
   'Show by default'             => 'Standardm&auml;&szlig;ig anzeigen',
   'Show custom variable search inputs' => 'Suchoptionen für Benutzerdefinierte Variablen verstecken',
   'Show delete button in purchase delivery orders?' => 'Soll der "Löschen"-Knopf bei Einkaufslieferscheinen angezeigt werden?',
@@ -2169,6 +2179,7 @@ $self->{texts} = {
   'This installation uses an unknown chart of accounts (&quot;#1&quot;). This database upgrade cannot create standard buchungsgruppen automatically.' => 'Diese Installation benutzt einen unbekannten Kontenrahmen (&quot;#1&quot;). Dieses Datenbankupgrade kann die Standardbuchungsgruppen nicht automatisch anlegen.',
   'This is a preliminary check for existing sources. Nothing will be created or deleted at this stage!' => 'In diesem Schritt werden bestehende Datenbanken gesucht. Es werden noch keine &Auml;nderungen vorgenommen!',
   'This is a very critical problem.' => 'Dieses Problem ist sehr schwerwiegend.',
+  'This is the default bin for ignoring onhand' => 'Standardlagerplatz für Auslagern ohne Bestandsprüfung',
   'This is the default bin for parts' => 'Standard-Lagerplatz für Stammdaten/Waren',
   'This list is capped at 15 items to keep it fast. If you need a full list, please use reports.' => 'Diese Liste ist auf 15 Zeilen begrenzt. Wenn Sie eine vollständige Liste benötigen, erstellen Sie bitte einen Bericht.',
   'This means that the user has created an AP transaction and chosen a taxkey for sales taxes, or that he has created an AR transaction and chosen a taxkey for input taxes.' => 'Das bedeutet, dass ein Benutzer eine Kreditorenbuchung angelegt und in ihr einen Umsatzsteuer-Steuerschlüssel verwendet oder eine Debitorenbuchung mit Vorsteuer-Steuerschlüssel angelegt hat.',
@@ -2226,7 +2237,9 @@ $self->{texts} = {
   'Transfer To Stock'           => 'Lagereingang',
   'Transfer from warehouse'     => 'Quelllager',
   'Transfer in'                 => 'Einlagern',
+  'Transfer in via default'     => 'Einlagern über Standard-Lagerplatz',
   'Transfer out'                => 'Auslagern',
+  'Transfer out via default'    => 'Auslagern über Standard-Lagerplatz',
   'Transfer qty'                => 'Umlagermenge',
   'Translation'                 => 'Übersetzung',
   'Trial Balance'               => 'Summen- und Saldenliste',
@@ -2283,6 +2296,7 @@ $self->{texts} = {
   'Uploaded on #1, size #2 kB'  => 'Am #1 hochgeladen, Größe #2 kB',
   'Use As New'                  => 'Als neu verwenden',
   'Use Templates'               => 'Benutze Vorlagen',
+  'Use master default bin for Default Transfer, if no default bin for the part is configured' => 'Standardlagerplatz für Ein- / Auslagern über Standard-Lagerplatz, falls für die Ware kein expliziter Lagerplatz konfiguriert ist',
   'User'                        => 'Benutzer',
   'User Config'                 => 'Einstellungen',
   'User Login'                  => 'Als Benutzer anmelden',
index a4df3bf..16df61e 100644 (file)
@@ -1,6 +1,63 @@
 [%- USE T8 %][%- USE L %][% USE LxERP %][% USE HTML %]
+[%- USE JavaScript -%]
+ <script type="text/javascript" src="js/common.js"></script>
+ <script type="text/javascript" src="js/parts_language_selection.js"></script>
+ <script type="text/javascript">
+  <!--
+      warehouses = new Array();
+      [%- USE WAREHOUSES_it = Iterator(SELF.WAREHOUSES) %][%- FOREACH warehouse = WAREHOUSES_it %]
+      warehouses[[% WAREHOUSES_it.count - 1 %]] = new Array();
+      warehouses[[% WAREHOUSES_it.count - 1 %]]['id'] = [% warehouse.id %];
+      warehouses[[% WAREHOUSES_it.count - 1 %]]['bins'] = new Array();
+      [% USE BINS_it = Iterator(warehouse.BINS) %][% FOREACH bin = BINS_it %]
+      warehouses[[% WAREHOUSES_it.count - 1 %]]['bins'][[% BINS_it.count - 1 %]] = new Array();
+      warehouses[[% WAREHOUSES_it.count - 1 %]]['bins'][[% BINS_it.count - 1 %]]['description'] = "[% JavaScript.escape(bin.description) %]";
+      warehouses[[% WAREHOUSES_it.count - 1 %]]['bins'][[% BINS_it.count - 1 %]]['id'] = [% bin.id %];
+      [% END %]
+      [% END %]
 
-[% PROCESS 'common/select_warehouse_bin.html'  WAREHOUSES=SELF.WAREHOUSES warehouse_id=SELF.warehouse_id bin_id=SELF.bin_id %]
+      function warehouse_selected(warehouse_id, bin_id, bin_id_name) {
+
+        // bin_id_name is optional and only used in client_config.html
+        var bin_id_name = bin_id_name || 'bin_id';
+
+        var control = document.getElementById(bin_id_name);
+
+        for (var i = control.options.length - 1; i >= 0; i--) {
+          control.options[i] = null;
+        }
+
+        var warehouse_index = 0;
+
+        for (i = 0; i < warehouses.length; i++)
+          if (warehouses[i]['id'] == warehouse_id) {
+            warehouse_index = i;
+            break;
+          }
+
+        var warehouse = warehouses[warehouse_index];
+        var bin_index = 0;
+
+        for (i = 0; i < warehouse['bins'].length; i++)
+          if (warehouse['bins'][i]['id'] == bin_id) {
+            bin_index = i;
+            break;
+          }
+
+        for (i = 0; i < warehouse['bins'].length; i++) {
+          control.options[i] = new Option(warehouse['bins'][i]['description'], warehouse['bins'][i]['id']);
+        }
+
+
+        control.options[bin_index].selected = true;
+      }
+
+      $(function() {
+        warehouse_selected([% SELF.warehouse_id %], [% SELF.bin_id %], 'bin_id');
+        warehouse_selected([% SELF.warehouse_id_ignore_onhand %], [% SELF.bin_id_ignore_onhand %], 'bin_id_ignore_onhand');
+      })
+    -->
+ </script>
 <h1>[% title | html %]</h1>
 
 [% PROCESS 'common/flash.html' %]
    <th colspan="3">[% 'Warehouse' | $T8 %]</th>
  </tr>
  <tr>
+   <td align="right">[% 'Default Transfer' | $T8 %]</td>
+   <td>
+     [% L.yes_no_tag('transfer_default', SELF.transfer_default) %]
+   </td>
+   <td>
+     [% 'Show Transfer via default' | $T8 %]<br>
+  </td>
+  </tr>
+  <tr>
+   <td align="right">[% 'Default Transfer with Master Bin' | $T8 %]</td>
+   <td>
+     [% L.yes_no_tag('transfer_default_use_master_default_bin', SELF.transfer_default_use_master_default_bin) %]
+   </td>
+   <td>
+     [% 'Use master default bin for Default Transfer, if no default bin for the part is configured' | $T8 %]<br>
+  </td>
+  </tr>
+  <tr>
+   <td align="right">[% 'Default Transfer Out with negative inventory' | $T8 %]</td>
+   <td>
+     [% L.yes_no_tag('transfer_default_ignore_onhand', SELF.transfer_default_ignore_onhand) %]
+   </td>
+   <td>
+     [% 'Default Transfer Out always succeed. The current part onhand is ignored and the inventory can have negative stocks (not recommended).' | $T8 %]<br>
+  </td>
+  </tr>
+
+ <tr> </tr>
+<tr>
+<th align="right" nowrap="true">[% 'Default Warehouse' | $T8 %]</th>
+<td>
+ <select name="warehouse_id" onchange="warehouse_selected(warehouses[this.selectedIndex]['id'], 0)">
+  [%- FOREACH warehouse = SELF.WAREHOUSES %]
+    <option value="[% HTML.escape(warehouse.id) %]"[% IF SELF.warehouse_id == warehouse.id %] selected[% END %]>[% warehouse.description %]</option>
+  [%- END %]
+ </select>
+</td>
+<td>
+   [% 'This is the default bin for parts' | $T8 %]<br>
+   [% 'If configured this bin will be preselected for all new parts. Also this bin will be used as the master default bin, if default transfer out with master bin is activated.' | $T8 %]<br>
+</td>
+</tr>
+<tr>
+<th align="right" nowrap="true">[% 'Default Bin' | $T8 %]</th>
+<td><select id="bin_id" name="bin_id"></select></td>
+</tr>
+<tr>
+<th align="right" nowrap="true">[% 'Default Warehouse with ignoring on hand' | $T8 %]</th>
+<td>
+ <select name="warehouse_id_ignore_onhand" onchange="warehouse_selected(warehouses[this.selectedIndex]['id'], 0, 'bin_id_ignore_onhand')">
+  [%- FOREACH warehouse = SELF.WAREHOUSES %]
+    <option value="[% HTML.escape(warehouse.id) %]"[% IF SELF.warehouse_id_ignore_onhand == warehouse.id %] selected[% END %]>[% warehouse.description %]</option>
+  [%- END %]
+ </select>
+</td>
+<td>
+   [% 'This is the default bin for ignoring onhand' | $T8 %]<br>
+   [% 'If the default transfer out always succeed use this bin for negative stock quantity.' | $T8 %]<br>
+</td>
+</tr>
+<tr>
+<th align="right" nowrap="true">[% 'Default Bin with ignoring onhand' | $T8 %]</th>
+<td><select id="bin_id_ignore_onhand" name="bin_id_ignore_onhand"></select></td>
+</tr>
+<tr>
    <td align="right">[% 'Show Bestbefore' | $T8 %]</td>
    <td>
      [% L.yes_no_tag('show_bestbefore', SELF.show_bestbefore) %]
      [% 'Any stock contents containing a best before date will be impossible to stock out otherwise.' | $T8 %]
    </td>
  </tr>
- <tr> </tr>
- <tr> </tr>
- <tr>
-  <th align="right" nowrap="true">[% 'Default Warehouse' | $T8 %]</th>
-  <td>
-   <select name="warehouse_id" onchange="warehouse_selected(warehouses[this.selectedIndex]['id'], 0)">
-    [%- FOREACH warehouse = SELF.WAREHOUSES %]
-      <option value="[% HTML.escape(warehouse.id) %]"[% IF SELF.warehouse_id == warehouse.id %] selected[% END %]>[% warehouse.description %]</option>
-    [%- END %]
-   </select>
- </td>
-  <td>
-     [% 'This is the default bin for parts' | $T8 %]<br>
-  </td>
- </tr>
- <tr>
-  <th align="right" nowrap="true">[% 'Default Bin' | $T8 %]</th>
-  <td><select id="bin_id" name="bin_id"></select></td>
- </tr>
-
-
  <tr class='listheading'>
    <th colspan="3">[% 'Weight' | $T8 %]</th>
  </tr>
index d87b156..20e6c2a 100644 (file)
    <input class="submit" type="submit" name="action_save" value="[% 'Save' | $T8 %]">
    [%- IF vc == 'customer' %]
    <input class="submit" type="submit" name="action_transfer_out" onclick="return check_transfer_qty()" value="[% 'Transfer out' | $T8 %]">
+   [% IF transfer_default %]
+   <input class="submit" type="submit" name="action_transfer_out_default" value="[% 'Transfer out via default' | $T8 %]">
+   [%- END %]
    [%- ELSE %]
    <input class="submit" type="submit" name="action_transfer_in"  onclick="return check_transfer_qty()" value="[% 'Transfer in' | $T8 %]">
+   [% IF transfer_default %]
+   <input class="submit" type="submit" name="action_transfer_in_default" value="[% 'Transfer in via default' | $T8 %]">
+   [%- END %]
    [%- END %]
    [%- END %]
    [%- IF id %]