Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 455d008c

Von Jan Büren vor mehr als 3 Jahren hinzugefügt

  • ID 455d008cdb1c6e53d442e94babfd6412cce34c38
  • Vorgänger 776972e4
  • Nachfolger 9ea55a4e

WH::transfer_assembly gegen S/H/Inventory::produce_assembly getauscht

Testfälle i.O., weiteres Feature für das Erzeugnis fertigen möglich
und im Changelog kommentiert. S.a. #429

Unterschiede anzeigen:

SL/WH.pm
191 191
  return @trans_ids;
192 192
}
193 193

  
194
sub transfer_assembly {
195
  $main::lxdebug->enter_sub();
196

  
197
  my $self     = shift;
198
  my %params   = @_;
199
  Common::check_params(\%params, qw(assembly_id dst_warehouse_id login qty unit dst_bin_id chargenumber bestbefore comment));
200

  
201
  my $myconfig = \%main::myconfig;
202
  my $form     = $main::form;
203
  my $kannNichtFertigen ="";  # Falls leer dann erfolgreich
204

  
205
  SL::DB->client->with_transaction(sub {
206
    my $dbh      = $params{dbh} || SL::DB->client->dbh;
207

  
208
    # Ablauferklärung
209
    #
210
    # ... Standard-Check oben Ende. Hier die eigentliche SQL-Abfrage
211
    # select parts_id,qty from assembly where id=1064;
212
    # Erweiterung für bug 935 am 23.4.09 -
213
    # Erzeugnisse können Dienstleistungen enthalten, die ja nicht 'lagerbar' sind.
214
    # select parts_id,qty from assembly inner join parts on assembly.parts_id = parts.id
215
    # where assembly.id=1066 and inventory_accno_id IS NOT NULL;
216
    #
217
    # Erweiterung für bug 23.4.09 -2 Erzeugnisse in Erzeugnissen können nicht ausgelagert werden,
218
    # wenn assembly nicht überprüft wird ...
219
    # patch von joachim eingespielt 24.4.2009:
220
    # my $query    = qq|select parts_id,qty from assembly inner join parts
221
    # on assembly.parts_id = parts.id  where assembly.id = ? and
222
    # (inventory_accno_id IS NOT NULL or parts.assembly = TRUE)|;
223

  
224
    # Lager in dem die Bestandteile gesucht werden kann entweder das Ziellager sein oder ist per Mandantenkonfig
225
    # auf das Standardlager des Bestandteiles schaltbar
226

  
227
    my $use_default_warehouse = $::instance_conf->get_transfer_default_warehouse_for_assembly;
228

  
229
    my $query = qq|SELECT assembly.parts_id, assembly.qty, parts.warehouse_id
230
                   FROM assembly INNER JOIN parts ON assembly.parts_id = parts.id
231
                   WHERE assembly.id = ? AND parts.part_type != 'service'|;
232

  
233
    my $sth_part_qty_assembly = prepare_execute_query($form, $dbh, $query, $params{assembly_id});
234

  
235
    my @trans_ids;
236

  
237
    # Hier wird das prepared Statement für die Schleife über alle Lagerplätze vorbereitet
238
    my $transferPartSQL = qq|INSERT INTO inventory (parts_id, warehouse_id, bin_id, chargenumber, bestbefore, comment, employee_id, qty,
239
                             trans_id, trans_type_id, shippingdate)
240
                             VALUES (?, ?, ?, ?, ?, ?, (SELECT id FROM employee WHERE login = ?), ?, ?,
241
                             (SELECT id FROM transfer_type WHERE direction = 'out' AND description = 'used'),
242
                             (SELECT current_date))|;
243
    my $sthTransferPartSQL   = prepare_query($form, $dbh, $transferPartSQL);
244
    my $trans_id;
245

  
246
    # der return-string für die fehlermeldung inkl. welche waren zum fertigen noch fehlen
247

  
248
    my $schleife_durchlaufen=0; # Falls die Schleife nicht ausgeführt wird -> Keine Einzelteile definiert. Bessere Idee? jan
249
    while (my $hash_ref = $sth_part_qty_assembly->fetchrow_hashref()) { #Schleife für select parts_id,(...) from assembly
250
      $schleife_durchlaufen=1;  # Erzeugnis definiert
251

  
252
      my $partsQTY          = $hash_ref->{qty} * $params{qty}; # benötigte teile * anzahl erzeugnisse
253
      my $currentPart_ID    = $hash_ref->{parts_id};
254

  
255
      my $currentPart_WH_ID = $use_default_warehouse && $hash_ref->{warehouse_id} ? $hash_ref->{warehouse_id} : $params{dst_warehouse_id};
256
      my $no_check = 0;
257

  
258
      # Prüfen ob Erzeugnis-Teile Standardlager haben.
259
      if ($use_default_warehouse && ! $hash_ref->{warehouse_id}) {
260
        # Prüfen ob in Mandantenkonfiguration ein Standardlager aktiviert isti.
261
        if ($::instance_conf->get_transfer_default_ignore_onhand) {
262
          $currentPart_WH_ID = $::instance_conf->get_warehouse_id_ignore_onhand;
263
          $no_check = 1;
264
        } else {
265
          $kannNichtFertigen .= "Kein Standardlager: " .
266
                              " Die Ware " . $self->get_part_description(parts_id => $currentPart_ID) .
267
                              " hat kein Standardlager definiert " .
268
                              ", um das Erzeugnis herzustellen. <br>";
269
          next;
270
        }
271
      }
272
      my $warehouse_info    = $self->get_basic_warehouse_info('id'=> $currentPart_WH_ID);
273
      my $warehouse_desc    = $warehouse_info->{"warehouse_description"};
274

  
275
      # Fertigen ohne Prüfung nach Bestand
276
      if ($no_check) {
277
        my $temppart_bin_id       = $::instance_conf->get_bin_id_ignore_onhand;
278
        my $temppart_chargenumber = "";
279
        my $temppart_bestbefore   = localtime();
280
        my $temppart_qty          = $partsQTY * -1;
281
        ($trans_id) = selectrow_query($form, $dbh, qq|SELECT nextval('id')| ) unless $trans_id;
282

  
283
        do_statement($form, $sthTransferPartSQL, $transferPartSQL, $currentPart_ID, $currentPart_WH_ID,
284
                       $temppart_bin_id, $temppart_chargenumber, $temppart_bestbefore, 'Verbraucht für ' .
285
                       $self->get_part_description(parts_id => $params{assembly_id}), $params{login}, $temppart_qty, $trans_id);
286
        next;
287
      }
288
      # Überprüfen, ob diese Anzahl gefertigt werden kann
289
      my $max_parts = $self->get_max_qty_parts(parts_id     => $currentPart_ID, # $self->method() == this.method()
290
                                               warehouse_id => $currentPart_WH_ID);
291

  
292
      if ($partsQTY  > $max_parts){
293
        # Gibt es hier ein Problem mit nicht "escapten" Zeichen?
294
        # 25.4.09 Antwort: Ja.  Aber erst wenn im Frontend die locales-Funktion aufgerufen wird
295

  
296
        $kannNichtFertigen .= "Zum Fertigen fehlen: " . abs($partsQTY - $max_parts) .
297
                              " Einheiten der Ware: " . $self->get_part_description(parts_id => $currentPart_ID) .
298
                              " im Lager: " . $warehouse_desc .
299
                              ", um das Erzeugnis herzustellen. <br>"; # Konnte die Menge nicht mit der aktuellen Anzahl der Waren fertigen
300
        next; # die weiteren Überprüfungen sind unnötig, daher das nächste elemente prüfen (genaue Ausgabe, was noch fehlt)
301
      }
302

  
303
      # Eine kurze Vorabfrage, um den Lagerplatz, Chargennummer und die Mindesthaltbarkeit zu bestimmen
304
      # Offen: Die Summe über alle Lagerplätze wird noch nicht gebildet
305
      # Gelöst: Wir haben vorher schon die Abfrage durchgeführt, ob wir fertigen können.
306
      # Noch besser gelöst: Wir laufen durch alle benötigten Waren zum Fertigen und geben eine Rückmeldung an den Benutzer was noch fehlt
307
      # und lösen den Rest dann so wie bei xplace im Barcode-Programm
308
      # S.a. Kommentar im bin/mozilla-Code mb übernimmt und macht das in ordentlich
309

  
310
      my $tempquery = qq|SELECT SUM(qty), bin_id, chargenumber, bestbefore   FROM inventory
311
                         WHERE warehouse_id = ? AND parts_id = ?  GROUP BY bin_id, chargenumber, bestbefore having SUM(qty)>0|;
312
      my $tempsth   = prepare_execute_query($form, $dbh, $tempquery, $currentPart_WH_ID, $currentPart_ID);
313

  
314
      # Alle Werte zu dem einzelnen Artikel, die wir später auslagern
315
      my $tmpPartsQTY = $partsQTY;
316

  
317
      while (my $temphash_ref = $tempsth->fetchrow_hashref()) {
318
        my $temppart_bin_id       = $temphash_ref->{bin_id}; # kann man hier den quelllagerplatz beim verbauen angeben?
319
        my $temppart_chargenumber = $temphash_ref->{chargenumber};
320
        my $temppart_bestbefore   = conv_date($temphash_ref->{bestbefore});
321
        my $temppart_qty          = $temphash_ref->{sum};
322

  
323
        ($trans_id) = selectrow_query($form, $dbh, qq|SELECT nextval('id')| ) unless $trans_id;
324
        if ($tmpPartsQTY > $temppart_qty) {  # wir haben noch mehr waren zum wegbuchen.
325
                                             # Wir buchen den kompletten Lagerplatzbestand und zählen die Hilfsvariable runter
326
          $tmpPartsQTY = $tmpPartsQTY - $temppart_qty;
327
          $temppart_qty = $temppart_qty * -1; # TODO beim analyiseren des sql-trace, war dieser wert positiv,
328
                                              # wenn * -1 als berechnung in der parameter-übergabe angegeben wird.
329
                                              # Dieser Wert IST und BLEIBT positiv!! Hilfe.
330
                                              # Liegt das daran, dass dieser Wert aus einem SQL-Statement stammt?
331
          push @trans_ids, $trans_id;
332
          do_statement($form, $sthTransferPartSQL, $transferPartSQL, $currentPart_ID, $currentPart_WH_ID,
333
                       $temppart_bin_id, $temppart_chargenumber, $temppart_bestbefore, 'Verbraucht für ' .
334
                       $self->get_part_description(parts_id => $params{assembly_id}), $params{login}, $temppart_qty, $trans_id);
335

  
336
          # hier ist noch ein fehler am besten mit definierten erzeugnissen debuggen 02/2009 jb
337
          # idee: ausbuch algorithmus mit rekursion lösen und an- und abschaltbar machen
338
          # das problem könnte sein, dass strict nicht an war und sth global eine andere zuweisung bekam
339
          # auf jeden fall war der internal-server-error nach aktivierung von strict und warnings plus ein paar my-definitionen weg
340
        } else { # okay, wir haben weniger oder gleich Waren die wir wegbuchen müssen, wir können also aufhören
341
          $tmpPartsQTY *=-1;
342
          do_statement($form, $sthTransferPartSQL, $transferPartSQL, $currentPart_ID, $currentPart_WH_ID,
343
                       $temppart_bin_id, $temppart_chargenumber, $temppart_bestbefore, 'Verbraucht für ' .
344
                       $self->get_part_description(parts_id => $params{assembly_id}), $params{login}, $tmpPartsQTY, $trans_id);
345
          last; # beendet die schleife (springt zum letzten element)
346
        }
347
      }  # ende while SELECT SUM(qty), bin_id, chargenumber, bestbefore   FROM inventory  WHERE warehouse_id
348
    } #ende while select parts_id,qty from assembly where id = ?
349

  
350
    if ($schleife_durchlaufen==0){  # falls die schleife nicht durchlaufen wurde, wurden auch
351
                                    # keine einzelteile definiert
352
        $kannNichtFertigen ="Für dieses Erzeugnis sind keine Einzelteile definiert.
353
                             Dementsprechend kann auch nichts hergestellt werden";
354
    }
355
    # gibt die Fehlermeldung zurück. A.) Keine Teile definiert
356
    #                                B.) Artikel und Anzahl der fehlenden Teile/Dienstleistungen
357
    die "<br><br>" . $kannNichtFertigen if ($kannNichtFertigen);
358

  
359
    # soweit alles gut. Jetzt noch die wirkliche Lagerbewegung für das Erzeugnis ausführen ...
360
    ($trans_id) = selectrow_query($form, $dbh, qq|SELECT nextval('id')| ) unless $trans_id;
361
    my $transferAssemblySQL = qq|INSERT INTO inventory (parts_id, warehouse_id, bin_id, chargenumber, bestbefore,
362
                                                        comment, employee_id, qty, trans_id, trans_type_id, shippingdate)
363
                                 VALUES (?, ?, ?, ?, ?, ?, (SELECT id FROM employee WHERE login = ?), ?, ?,
364
                                 (SELECT id FROM transfer_type WHERE direction = 'in' AND description = 'assembled'),
365
                                 (select current_date))|;
366
    my $sthTransferAssemblySQL   = prepare_query($form, $dbh, $transferAssemblySQL);
367
    do_statement($form, $sthTransferAssemblySQL, $transferAssemblySQL, $params{assembly_id}, $params{dst_warehouse_id},
368
                 $params{dst_bin_id}, $params{chargenumber}, conv_date($params{bestbefore}), $params{comment}, $params{login}, $params{qty}, $trans_id);
369

  
370

  
371
    1;
372
  }) or do { return $kannNichtFertigen };
373

  
374
  $main::lxdebug->leave_sub();
375
  return 1; # Alles erfolgreich
376
}
377

  
378 194
sub get_warehouse_journal {
379 195
  $main::lxdebug->enter_sub();
380 196

  
bin/mozilla/wh.pl
45 45
use SL::IC;
46 46
use SL::WH;
47 47
use SL::OE;
48
# use SL::Helper::Inventory qw(produce_assembly);
48
use SL::Helper::Inventory qw(produce_assembly);
49 49
use SL::Locale::String qw(t8);
50 50
use SL::ReportGenerator;
51 51
use SL::Presenter::Tag qw(checkbox_tag);
......
371 371
  $main::lxdebug->leave_sub();
372 372
}
373 373

  
374
# vorüberlegung jb 22.2.2009
375
# wir benötigen für diese funktion, die anzahl die vom erzeugnis hergestellt werden soll. vielleicht direkt per js fehleingaben verhindern?
376
# ferner dann nochmal mit check_asssembly_max_create gegenprüfen und dann transaktionssicher wegbuchen.
377
# wir brauchen eine hilfsfunktion, die nee. brauchen wir nicht. der algorithmus läuft genau wie bei check max_create, nur dass hier auch eine lagerbewegung (verbraucht) stattfindet
378
# Manko ist derzeit noch, dass unterschiedliche Lagerplätze, bzw. das Quelllager an sich nicht ausgewählt werden können.
379
# Laut Absprache in KW11 09 übernimmt mb hier den rest im April ... jb 18.3.09
380

  
381 374
sub create_assembly {
382 375
  $main::lxdebug->enter_sub();
383 376

  
......
389 382
  if ($form->{qty} <= 0) {
390 383
    $form->show_generic_error($locale->text('Invalid quantity.'));
391 384
  }
392
  # TODO Es wäre schön, hier schon die maximale Anzahl der zu fertigenden Erzeugnisse zu haben
393
  #else { if ($form->{qty} > $maxcreate) { #s.o.
394
  #     $form->show_generic_error($locale->text('Can not create that quantity with current stock'));
395
  #     $form->show_generic_error('Maximale Stückzahl' . $maxcreate);
396
  #   }
397
  #  }
398

  
399 385
  if (!$form->{warehouse_id} || !$form->{bin_id}) {
400 386
    $form->error($locale->text('The warehouse or the bin is missing.'));
401 387
  }
388
  # need part and bin object
389
  my ($bin, $assembly);
390
  $assembly = SL::DB::Manager::Part->find_by(id => $form->{parts_id}, part_type => 'assembly');
391
  $form->show_generic_error($locale->text('Invalid assembly')) unless ref $assembly eq 'SL::DB::Part';
392

  
393
  $bin = SL::DB::Manager::Bin->find_by(id => $form->{bin_id});
394
  $form->show_generic_error($locale->text('Invalid bin')) unless ref $bin eq 'SL::DB::Bin';
402 395

  
403 396
  if (!$::instance_conf->get_show_bestbefore) {
404
      $form->{bestbefore} = '';
397
    $form->{bestbefore} = '';
405 398
  }
406 399

  
407
  # WIESO war das nicht vorher schon ein %HASH?? ein hash ist ein hash! das hat mich mehr als eine Stunde gekostet herauszufinden. grr. jb 3.3.2009
408
  # Anm. jb 18.3. vielleicht auch nur meine unwissenheit in perl-datenstrukturen
409
  my %TRANSFER = (
410
    'transfer_type'    => 'assembly',
411
    'login'            => $::myconfig{login},
412
    'dst_warehouse_id' => $form->{warehouse_id},
413
    'dst_bin_id'       => $form->{bin_id},
414
    'chargenumber'     => $form->{chargenumber},
415
    'bestbefore'       => $form->{bestbefore},
416
    'assembly_id'      => $form->{parts_id},
417
    'qty'              => $form->{qty},
418
    'unit'             => $form->{unit},
419
    'comment'          => $form->{comment}
400
  produce_assembly(
401
              part           => $assembly,               # target assembly
402
              qty            => $form->{qty},            # qty
403
              auto_allocate  => 1,
404
              bin            => $bin,                    # needed unless a global standard target is configured
405
              chargenumber   => $form->{chargenumber},   # optional
406
              bestbefore     => $form->{bestbefore},
407
              comment        => $form->{comment},        # optional
420 408
  );
421 409

  
422
  my $ret = WH->transfer_assembly (%TRANSFER);
423
  # Frage: Ich pack in den return-wert auch gleich die Fehlermeldung. Irgendwelche Nummern als Fehlerkonstanten definieren find ich auch nicht besonders schick...
424
  # Ideen? jb 18.3.09
425
  if ($ret ne "1"){
426
    # Die locale-Funktion kann keine Double-Quotes escapen, deswegen hier erstmal so (ein wahrscheinlich immerwährender Hotfix) s.a. Frage davor jb 25.4.09
427
    $form->show_generic_error($ret);
428
  }
429

  
430 410
  delete @{$form}{qw(parts_id partnumber description qty unit chargenumber bestbefore comment)};
431 411

  
432 412
  $form->{saved_message} = $locale->text('The assembly has been created.');
doc/changelog
10 10

  
11 11
Kleinere neue Features und Detailverbesserungen:
12 12

  
13
- Erzeugnisse können jetzt auf Lagerteile aus anderem Lagern zurückgreifen
14
  und die Fertigung liefert keinen Fehler mehr. Einstellbar in der
15
  Mandantenkonfiguration.
16
- API- Änderung Erzeugnis fertigen nutzt jetzt SL/Helper/Inventory.pm
17

  
13 18
Bugfixes (Tracker: https://www.kivitendo.de/redmine):
14 19

  
15 20

  
locale/de/all
542 542
  'Calculate'                   => 'Berechnen',
543 543
  'Calculate due date automatically' => 'Fälligkeitsdatum automatisch berechnen',
544 544
  'Calling #1 now'              => 'Wähle jetzt #1',
545
  'Can not create that quantity with current stock' => 'Diese Anzahl kann mit dem gegenwärtigen Lagerbestand nicht hergestellt werden.',
546 545
  'Can only delete the "Storno zu" part of the cancellation pair.' => 'Löschen von R(S) Rechnung nicht erlaubt. Löschen der entsprechenden "Storno zu" Gutschrift reaktiviert diese Rechnung wieder.',
547 546
  'Can only save template if amounts,i.e. 1 for debit and credit are set.' => 'Kann die Vorlage nicht speichern. Es wird mindestens ein Betrag im Soll und im Haben benötigt (bspw. 1), damit bspw. Beträge aus Kontoauszügen korrekt gesetzt werden können.',
548 547
  'Can\'t connect to shop. #1'  => 'Kann keine Verbindung zu Shop #1 herstellen.',
......
1786 1785
  'Introduction of clients'     => 'Einführung von Mandanten',
1787 1786
  'Inv. Duedate'                => 'Rg. Fälligkeit',
1788 1787
  'Invalid'                     => 'Ungültig',
1788
  'Invalid assembly'            => 'Ungültiges Erzeugnis',
1789
  'Invalid bin'                 => 'Ungültiger Lagerplatz',
1789 1790
  'Invalid charge number: #1'   => 'Ungültige Chargennummer: #1',
1790 1791
  'Invalid combination of ledger account number length. Mismatch length of #1 with length of #2. Please check your account settings. ' => 'Ungültige Kombination der Nummernkreislänge der Sachkonten. Kann nicht eine Länge von #1 und eine Länge von #2 verarbeiten. Bitte entsprechend die Konteneinstellungen überprüfen.',
1791 1792
  'Invalid duration format'     => 'Falsches Format für Zeitdauer',
locale/en/all
542 542
  'Calculate'                   => '',
543 543
  'Calculate due date automatically' => '',
544 544
  'Calling #1 now'              => '',
545
  'Can not create that quantity with current stock' => '',
546 545
  'Can only delete the "Storno zu" part of the cancellation pair.' => '',
547 546
  'Can only save template if amounts,i.e. 1 for debit and credit are set.' => '',
548 547
  'Can\'t connect to shop. #1'  => '',
......
1786 1785
  'Introduction of clients'     => '',
1787 1786
  'Inv. Duedate'                => '',
1788 1787
  'Invalid'                     => '',
1788
  'Invalid assembly'            => '',
1789
  'Invalid bin'                 => '',
1789 1790
  'Invalid charge number: #1'   => '',
1790 1791
  'Invalid combination of ledger account number length. Mismatch length of #1 with length of #2. Please check your account settings. ' => '',
1791 1792
  'Invalid duration format'     => '',

Auch abrufbar als: Unified diff