X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=SL%2FWH.pm;h=bcdffa5118abc6cc6c30b2882d91ede7246d3acb;hb=826d79929635c726f7459296fafa7e630c00e72d;hp=a25aa314e875fc05d86334d1fc22fba37b332efa;hpb=0f214df54e7f85f2bbbfc0dbd06c6add611992c5;p=kivitendo-erp.git diff --git a/SL/WH.pm b/SL/WH.pm index a25aa314e..bcdffa511 100644 --- a/SL/WH.pm +++ b/SL/WH.pm @@ -25,7 +25,8 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1335, USA. #====================================================================== # # Warehouse module @@ -163,132 +164,167 @@ sub transfer_assembly { my $myconfig = \%main::myconfig; my $form = $main::form; - my $dbh = $params{dbh} || $form->get_standard_dbh($myconfig); - - - # Ablauferklärung - # - # ... Standard-Check oben Ende. Hier die eigentliche SQL-Abfrage - # select parts_id,qty from assembly where id=1064; - # Erweiterung für bug 935 am 23.4.09 - - # Erzeugnisse können Dienstleistungen enthalten, die ja nicht 'lagerbar' sind. - # select parts_id,qty from assembly inner join parts on assembly.parts_id = parts.id - # where assembly.id=1066 and inventory_accno_id IS NOT NULL; - # - # Erweiterung für bug 23.4.09 -2 Erzeugnisse in Erzeugnissen können nicht ausgelagert werden, - # wenn assembly nicht überprüft wird ... - # patch von joachim eingespielt 24.4.2009: - # my $query = qq|select parts_id,qty from assembly inner join parts - # on assembly.parts_id = parts.id where assembly.id = ? and - # (inventory_accno_id IS NOT NULL or parts.assembly = TRUE)|; - - - my $query = qq|select assembly.parts_id, assembly.qty, parts.warehouse_id from assembly inner join parts on assembly.parts_id = parts.id - where assembly.id = ? and (inventory_accno_id IS NOT NULL or parts.assembly = TRUE)|; - - my $sth_part_qty_assembly = prepare_execute_query($form, $dbh, $query, $params{assembly_id}); - - # Hier wird das prepared Statement für die Schleife über alle Lagerplätze vorbereitet - my $transferPartSQL = qq|INSERT INTO inventory (parts_id, warehouse_id, bin_id, chargenumber, bestbefore, comment, employee_id, qty, - trans_id, trans_type_id, shippingdate) - VALUES (?, ?, ?, ?, ?, ?, (SELECT id FROM employee WHERE login = ?), ?, nextval('id'), - (SELECT id FROM transfer_type WHERE direction = 'out' AND description = 'used'), - (SELECT current_date))|; - my $sthTransferPartSQL = prepare_query($form, $dbh, $transferPartSQL); + my $kannNichtFertigen =""; # Falls leer dann erfolgreich - # der return-string für die fehlermeldung inkl. welche waren zum fertigen noch fehlen + SL::DB->client->with_transaction(sub { + my $dbh = $params{dbh} || SL::DB->client->dbh; + + # Ablauferklärung + # + # ... Standard-Check oben Ende. Hier die eigentliche SQL-Abfrage + # select parts_id,qty from assembly where id=1064; + # Erweiterung für bug 935 am 23.4.09 - + # Erzeugnisse können Dienstleistungen enthalten, die ja nicht 'lagerbar' sind. + # select parts_id,qty from assembly inner join parts on assembly.parts_id = parts.id + # where assembly.id=1066 and inventory_accno_id IS NOT NULL; + # + # Erweiterung für bug 23.4.09 -2 Erzeugnisse in Erzeugnissen können nicht ausgelagert werden, + # wenn assembly nicht überprüft wird ... + # patch von joachim eingespielt 24.4.2009: + # my $query = qq|select parts_id,qty from assembly inner join parts + # on assembly.parts_id = parts.id where assembly.id = ? and + # (inventory_accno_id IS NOT NULL or parts.assembly = TRUE)|; + + # Lager in dem die Bestandteile gesucht werden kann entweder das Ziellager sein oder ist per Mandantenkonfig + # auf das Standardlager des Bestandteiles schaltbar + + my $use_default_warehouse = $::instance_conf->get_transfer_default_warehouse_for_assembly; + + my $query = qq|SELECT assembly.parts_id, assembly.qty, parts.warehouse_id + FROM assembly INNER JOIN parts ON assembly.parts_id = parts.id + WHERE assembly.id = ? AND parts.part_type != 'service'|; + + my $sth_part_qty_assembly = prepare_execute_query($form, $dbh, $query, $params{assembly_id}); + + # Hier wird das prepared Statement für die Schleife über alle Lagerplätze vorbereitet + my $transferPartSQL = qq|INSERT INTO inventory (parts_id, warehouse_id, bin_id, chargenumber, bestbefore, comment, employee_id, qty, + trans_id, trans_type_id, shippingdate) + VALUES (?, ?, ?, ?, ?, ?, (SELECT id FROM employee WHERE login = ?), ?, nextval('id'), + (SELECT id FROM transfer_type WHERE direction = 'out' AND description = 'used'), + (SELECT current_date))|; + my $sthTransferPartSQL = prepare_query($form, $dbh, $transferPartSQL); + + # der return-string für die fehlermeldung inkl. welche waren zum fertigen noch fehlen + + my $schleife_durchlaufen=0; # Falls die Schleife nicht ausgeführt wird -> Keine Einzelteile definiert. Bessere Idee? jan + while (my $hash_ref = $sth_part_qty_assembly->fetchrow_hashref()) { #Schleife für select parts_id,(...) from assembly + $schleife_durchlaufen=1; # Erzeugnis definiert + + my $partsQTY = $hash_ref->{qty} * $params{qty}; # benötigte teile * anzahl erzeugnisse + my $currentPart_ID = $hash_ref->{parts_id}; + + my $currentPart_WH_ID = $use_default_warehouse && $hash_ref->{warehouse_id} ? $hash_ref->{warehouse_id} : $params{dst_warehouse_id}; + my $no_check = 0; + + # Prüfen ob Erzeugnis-Teile Standardlager haben. + if ($use_default_warehouse && ! $hash_ref->{warehouse_id}) { + # Prüfen ob in Mandantenkonfiguration ein Standardlager aktiviert isti. + if ($::instance_conf->get_transfer_default_ignore_onhand) { + $currentPart_WH_ID = $::instance_conf->get_warehouse_id_ignore_onhand; + $no_check = 1; + } else { + $kannNichtFertigen .= "Kein Standardlager: " . + " Die Ware " . $self->get_part_description(parts_id => $currentPart_ID) . + " hat kein Standardlager definiert " . + ", um das Erzeugnis herzustellen.
"; + next; + } + } + my $warehouse_info = $self->get_basic_warehouse_info('id'=> $currentPart_WH_ID); + my $warehouse_desc = $warehouse_info->{"warehouse_description"}; - my $kannNichtFertigen =""; # Falls leer dann erfolgreich - my $schleife_durchlaufen=0; # Falls die Schleife nicht ausgeführt wird -> Keine Einzelteile definiert. Bessere Idee? jan - while (my $hash_ref = $sth_part_qty_assembly->fetchrow_hashref()) { #Schleife für select parts_id,(...) from assembly - $schleife_durchlaufen=1; # Erzeugnis definiert - my $partsQTY = $hash_ref->{qty} * $params{qty}; # benötigte teile * anzahl erzeugnisse - my $currentPart_ID = $hash_ref->{parts_id}; - my $currentPart_WH_ID = $hash_ref->{warehouse_id}; - my $warehouse_info = $self->get_basic_warehouse_info('id' => $currentPart_WH_ID); - my $warehouse_desc = $warehouse_info->{"warehouse_description"}; - - # Überprüfen, ob diese Anzahl gefertigt werden kann - my $max_parts = $self->get_max_qty_parts(parts_id => $currentPart_ID, # $self->method() == this.method() - warehouse_id => $currentPart_WH_ID); - - if ($partsQTY > $max_parts){ - # Gibt es hier ein Problem mit nicht "escapten" Zeichen? - # 25.4.09 Antwort: Ja. Aber erst wenn im Frontend die locales-Funktion aufgerufen wird - - $kannNichtFertigen .= "Zum Fertigen fehlen: " . abs($partsQTY - $max_parts) . - " Einheiten der Ware: " . $self->get_part_description(parts_id => $currentPart_ID) . - " im Lager: " . $warehouse_desc . - ", um das Erzeugnis herzustellen.
"; # Konnte die Menge nicht mit der aktuellen Anzahl der Waren fertigen - next; # die weiteren Überprüfungen sind unnötig, daher das nächste elemente prüfen (genaue Ausgabe, was noch fehlt) - } + # Fertigen ohne Prüfung nach Bestand + if ($no_check) { + my $temppart_bin_id = $::instance_conf->get_bin_id_ignore_onhand; + my $temppart_chargenumber = ""; + my $temppart_bestbefore = localtime(); + my $temppart_qty = $partsQTY * -1; - # Eine kurze Vorabfrage, um den Lagerplatz, Chargennummer und die Mindesthaltbarkeit zu bestimmen - # Offen: Die Summe über alle Lagerplätze wird noch nicht gebildet - # Gelöst: Wir haben vorher schon die Abfrage durchgeführt, ob wir fertigen können. - # Noch besser gelöst: Wir laufen durch alle benötigten Waren zum Fertigen und geben eine Rückmeldung an den Benutzer was noch fehlt - # und lösen den Rest dann so wie bei xplace im Barcode-Programm - # S.a. Kommentar im bin/mozilla-Code mb übernimmt und macht das in ordentlich - - my $tempquery = qq|SELECT SUM(qty), bin_id, chargenumber, bestbefore FROM inventory - WHERE warehouse_id = ? AND parts_id = ? GROUP BY bin_id, chargenumber, bestbefore having SUM(qty)>0|; - my $tempsth = prepare_execute_query($form, $dbh, $tempquery, $currentPart_WH_ID, $currentPart_ID); - - # Alle Werte zu dem einzelnen Artikel, die wir später auslagern - my $tmpPartsQTY = $partsQTY; - - while (my $temphash_ref = $tempsth->fetchrow_hashref()) { - my $temppart_bin_id = $temphash_ref->{bin_id}; # kann man hier den quelllagerplatz beim verbauen angeben? - my $temppart_chargenumber = $temphash_ref->{chargenumber}; - my $temppart_bestbefore = conv_date($temphash_ref->{bestbefore}); - my $temppart_qty = $temphash_ref->{sum}; - - if ($tmpPartsQTY > $temppart_qty) { # wir haben noch mehr waren zum wegbuchen. - # Wir buchen den kompletten Lagerplatzbestand und zählen die Hilfsvariable runter - $tmpPartsQTY = $tmpPartsQTY - $temppart_qty; - $temppart_qty = $temppart_qty * -1; # TODO beim analyiseren des sql-trace, war dieser wert positiv, - # wenn * -1 als berechnung in der parameter-übergabe angegeben wird. - # Dieser Wert IST und BLEIBT positiv!! Hilfe. - # Liegt das daran, dass dieser Wert aus einem SQL-Statement stammt? - do_statement($form, $sthTransferPartSQL, $transferPartSQL, $currentPart_ID, $currentPart_WH_ID, - $temppart_bin_id, $temppart_chargenumber, $temppart_bestbefore, 'Verbraucht für ' . - $self->get_part_description(parts_id => $params{assembly_id}), $params{login}, $temppart_qty); - - # hier ist noch ein fehler am besten mit definierten erzeugnissen debuggen 02/2009 jb - # idee: ausbuch algorithmus mit rekursion lösen und an- und abschaltbar machen - # das problem könnte sein, dass strict nicht an war und sth global eine andere zuweisung bekam - # auf jeden fall war der internal-server-error nach aktivierung von strict und warnings plus ein paar my-definitionen weg - } else { # okay, wir haben weniger oder gleich Waren die wir wegbuchen müssen, wir können also aufhören - $tmpPartsQTY *=-1; do_statement($form, $sthTransferPartSQL, $transferPartSQL, $currentPart_ID, $currentPart_WH_ID, - $temppart_bin_id, $temppart_chargenumber, $temppart_bestbefore, 'Verbraucht für ' . - $self->get_part_description(parts_id => $params{assembly_id}), $params{login}, $tmpPartsQTY); - last; # beendet die schleife (springt zum letzten element) + $temppart_bin_id, $temppart_chargenumber, $temppart_bestbefore, 'Verbraucht für ' . + $self->get_part_description(parts_id => $params{assembly_id}), $params{login}, $temppart_qty); + next; + } + # Überprüfen, ob diese Anzahl gefertigt werden kann + my $max_parts = $self->get_max_qty_parts(parts_id => $currentPart_ID, # $self->method() == this.method() + warehouse_id => $currentPart_WH_ID); + + if ($partsQTY > $max_parts){ + # Gibt es hier ein Problem mit nicht "escapten" Zeichen? + # 25.4.09 Antwort: Ja. Aber erst wenn im Frontend die locales-Funktion aufgerufen wird + + $kannNichtFertigen .= "Zum Fertigen fehlen: " . abs($partsQTY - $max_parts) . + " Einheiten der Ware: " . $self->get_part_description(parts_id => $currentPart_ID) . + " im Lager: " . $warehouse_desc . + ", um das Erzeugnis herzustellen.
"; # Konnte die Menge nicht mit der aktuellen Anzahl der Waren fertigen + next; # die weiteren Überprüfungen sind unnötig, daher das nächste elemente prüfen (genaue Ausgabe, was noch fehlt) } - } # ende while SELECT SUM(qty), bin_id, chargenumber, bestbefore FROM inventory WHERE warehouse_id - } #ende while select parts_id,qty from assembly where id = ? - - if ($schleife_durchlaufen==0){ # falls die schleife nicht durchlaufen wurde, wurden auch - # keine einzelteile definiert - $kannNichtFertigen ="Für dieses Erzeugnis sind keine Einzelteile definiert. - Dementsprechend kann auch nichts hergestellt werden"; - } - # gibt die Fehlermeldung zurück. A.) Keine Teile definiert - # B.) Artikel und Anzahl der fehlenden Teile/Dienstleistungen - if ($kannNichtFertigen) { - return $kannNichtFertigen; - } - # soweit alles gut. Jetzt noch die wirkliche Lagerbewegung für das Erzeugnis ausführen ... - my $transferAssemblySQL = qq|INSERT INTO inventory (parts_id, warehouse_id, bin_id, chargenumber, bestbefore, - comment, employee_id, qty, trans_id, trans_type_id, shippingdate) - VALUES (?, ?, ?, ?, ?, ?, (SELECT id FROM employee WHERE login = ?), ?, nextval('id'), - (SELECT id FROM transfer_type WHERE direction = 'in' AND description = 'assembled'), - (select current_date))|; - my $sthTransferAssemblySQL = prepare_query($form, $dbh, $transferAssemblySQL); - do_statement($form, $sthTransferAssemblySQL, $transferAssemblySQL, $params{assembly_id}, $params{dst_warehouse_id}, - $params{dst_bin_id}, $params{chargenumber}, conv_date($params{bestbefore}), $params{comment}, $params{login}, $params{qty}); - $dbh->commit(); + # Eine kurze Vorabfrage, um den Lagerplatz, Chargennummer und die Mindesthaltbarkeit zu bestimmen + # Offen: Die Summe über alle Lagerplätze wird noch nicht gebildet + # Gelöst: Wir haben vorher schon die Abfrage durchgeführt, ob wir fertigen können. + # Noch besser gelöst: Wir laufen durch alle benötigten Waren zum Fertigen und geben eine Rückmeldung an den Benutzer was noch fehlt + # und lösen den Rest dann so wie bei xplace im Barcode-Programm + # S.a. Kommentar im bin/mozilla-Code mb übernimmt und macht das in ordentlich + + my $tempquery = qq|SELECT SUM(qty), bin_id, chargenumber, bestbefore FROM inventory + WHERE warehouse_id = ? AND parts_id = ? GROUP BY bin_id, chargenumber, bestbefore having SUM(qty)>0|; + my $tempsth = prepare_execute_query($form, $dbh, $tempquery, $currentPart_WH_ID, $currentPart_ID); + + # Alle Werte zu dem einzelnen Artikel, die wir später auslagern + my $tmpPartsQTY = $partsQTY; + + while (my $temphash_ref = $tempsth->fetchrow_hashref()) { + my $temppart_bin_id = $temphash_ref->{bin_id}; # kann man hier den quelllagerplatz beim verbauen angeben? + my $temppart_chargenumber = $temphash_ref->{chargenumber}; + my $temppart_bestbefore = conv_date($temphash_ref->{bestbefore}); + my $temppart_qty = $temphash_ref->{sum}; + + if ($tmpPartsQTY > $temppart_qty) { # wir haben noch mehr waren zum wegbuchen. + # Wir buchen den kompletten Lagerplatzbestand und zählen die Hilfsvariable runter + $tmpPartsQTY = $tmpPartsQTY - $temppart_qty; + $temppart_qty = $temppart_qty * -1; # TODO beim analyiseren des sql-trace, war dieser wert positiv, + # wenn * -1 als berechnung in der parameter-übergabe angegeben wird. + # Dieser Wert IST und BLEIBT positiv!! Hilfe. + # Liegt das daran, dass dieser Wert aus einem SQL-Statement stammt? + do_statement($form, $sthTransferPartSQL, $transferPartSQL, $currentPart_ID, $currentPart_WH_ID, + $temppart_bin_id, $temppart_chargenumber, $temppart_bestbefore, 'Verbraucht für ' . + $self->get_part_description(parts_id => $params{assembly_id}), $params{login}, $temppart_qty); + + # hier ist noch ein fehler am besten mit definierten erzeugnissen debuggen 02/2009 jb + # idee: ausbuch algorithmus mit rekursion lösen und an- und abschaltbar machen + # das problem könnte sein, dass strict nicht an war und sth global eine andere zuweisung bekam + # auf jeden fall war der internal-server-error nach aktivierung von strict und warnings plus ein paar my-definitionen weg + } else { # okay, wir haben weniger oder gleich Waren die wir wegbuchen müssen, wir können also aufhören + $tmpPartsQTY *=-1; + do_statement($form, $sthTransferPartSQL, $transferPartSQL, $currentPart_ID, $currentPart_WH_ID, + $temppart_bin_id, $temppart_chargenumber, $temppart_bestbefore, 'Verbraucht für ' . + $self->get_part_description(parts_id => $params{assembly_id}), $params{login}, $tmpPartsQTY); + last; # beendet die schleife (springt zum letzten element) + } + } # ende while SELECT SUM(qty), bin_id, chargenumber, bestbefore FROM inventory WHERE warehouse_id + } #ende while select parts_id,qty from assembly where id = ? + + if ($schleife_durchlaufen==0){ # falls die schleife nicht durchlaufen wurde, wurden auch + # keine einzelteile definiert + $kannNichtFertigen ="Für dieses Erzeugnis sind keine Einzelteile definiert. + Dementsprechend kann auch nichts hergestellt werden"; + } + # gibt die Fehlermeldung zurück. A.) Keine Teile definiert + # B.) Artikel und Anzahl der fehlenden Teile/Dienstleistungen + die "

" . $kannNichtFertigen if ($kannNichtFertigen); + + # soweit alles gut. Jetzt noch die wirkliche Lagerbewegung für das Erzeugnis ausführen ... + my $transferAssemblySQL = qq|INSERT INTO inventory (parts_id, warehouse_id, bin_id, chargenumber, bestbefore, + comment, employee_id, qty, trans_id, trans_type_id, shippingdate) + VALUES (?, ?, ?, ?, ?, ?, (SELECT id FROM employee WHERE login = ?), ?, nextval('id'), + (SELECT id FROM transfer_type WHERE direction = 'in' AND description = 'assembled'), + (select current_date))|; + my $sthTransferAssemblySQL = prepare_query($form, $dbh, $transferAssemblySQL); + do_statement($form, $sthTransferAssemblySQL, $transferAssemblySQL, $params{assembly_id}, $params{dst_warehouse_id}, + $params{dst_bin_id}, $params{chargenumber}, conv_date($params{bestbefore}), $params{comment}, $params{login}, $params{qty}); + 1; + }) or do { return $kannNichtFertigen }; $main::lxdebug->leave_sub(); return 1; # Alles erfolgreich @@ -331,6 +367,11 @@ sub get_warehouse_journal { push @filter_vars, like($filter{description}); } + if ($filter{classification_id}) { + push @filter_ary, "p.classification_id = ?"; + push @filter_vars, $filter{classification_id}; + } + if ($filter{chargenumber}) { push @filter_ary, "i1.chargenumber ILIKE ?"; push @filter_vars, like($filter{chargenumber}); @@ -377,10 +418,30 @@ sub get_warehouse_journal { my $sort_order = $form->{order}; $sort_col = $filter{sort} unless $sort_col; + $sort_col = 'shippingdate' if $sort_col eq 'date'; $sort_order = ($sort_col = 'shippingdate') unless $sort_col; - $sort_col = 'shippingdate' if $sort_col eq 'date'; - $sort_order = $filter{order} unless $sort_order; - my $sort_spec = "${sort_col} " . ($sort_order ? " DESC" : " ASC"); + + my %orderspecs = ( + 'shippingdate' => ['shippingdate', 'r_itime', 'r_parts_id'], + 'bin_to' => ['bin_to', 'r_itime', 'r_parts_id'], + 'bin_from' => ['bin_from', 'r_itime', 'r_parts_id'], + 'warehouse_to' => ['warehouse_to, r_itime, r_parts_id'], + 'warehouse_from' => ['warehouse_from, r_itime, r_parts_id'], + 'partnumber' => ['partnumber'], + 'partdescription'=> ['partdescription'], + 'partunit' => ['partunit, r_itime, r_parts_id'], + 'qty' => ['qty, r_itime, r_parts_id'], + 'oe_id' => ['oe_id'], + 'comment' => ['comment'], + 'trans_type' => ['trans_type'], + 'employee' => ['employee'], + 'projectnumber' => ['projectnumber'], + 'chargenumber' => ['chargenumber'], + ); + + $sort_order = $filter{order} unless $sort_order; + my $ASC = ($sort_order ? " DESC" : " ASC"); + my $sort_spec = join("$ASC , ", @{$orderspecs{$sort_col}}). " $ASC"; my $where_clause = @filter_ary ? join(" AND ", @filter_ary) . " AND " : ''; @@ -389,6 +450,8 @@ sub get_warehouse_journal { "qty" => "ABS(SUM(i1.qty))", "partnumber" => "p.partnumber", "partdescription" => "p.description", + "classification_id" => "p.classification_id", + "part_type" => "p.part_type", "bindescription" => "b.description", "chargenumber" => "i1.chargenumber", "bestbefore" => "i1.bestbefore", @@ -420,6 +483,9 @@ sub get_warehouse_journal { "warehouse_from" => "'$filter{na}'", }; + $form->{l_classification_id} = 'Y'; + $form->{l_part_type} = 'Y'; + $form->{l_itime} = 'Y'; $form->{l_invoice_id} = $form->{l_oe_id} if $form->{l_oe_id}; # build the select clauses. @@ -430,12 +496,12 @@ sub get_warehouse_journal { } my $group_clause = join ", ", map { +/^l_/; "r_$'" } - ( grep( { !/qty$/ and /^l_/ and $form->{$_} eq 'Y' } keys %$form), qw(l_parts_id l_partunit l_shippingdate) ); + ( grep( { !/qty$/ and /^l_/ and $form->{$_} eq 'Y' } keys %$form), qw(l_parts_id l_partunit l_shippingdate l_itime) ); $where_clause = defined($where_clause) ? $where_clause : ''; my $query = - qq|SELECT DISTINCT $select{trans} + qq|SELECT * FROM (SELECT DISTINCT $select{trans} FROM inventory i1 LEFT JOIN inventory i2 ON i1.trans_id = i2.trans_id LEFT JOIN parts p ON i1.parts_id = p.id @@ -483,9 +549,20 @@ sub get_warehouse_journal { WHERE $where_clause i1.qty > 0 AND i1.trans_id IN ( SELECT i.trans_id FROM inventory i GROUP BY i.trans_id HAVING COUNT(i.trans_id) = 1 ) GROUP BY $group_clause - ORDER BY r_${sort_spec}|; + ORDER BY r_${sort_spec}) AS lines WHERE r_qty>0|; + + my @all_vars = (@filter_vars,@filter_vars,@filter_vars); + + if ($filter{limit}) { + $query .= " LIMIT ?"; + push @all_vars,$filter{limit}; + } + if ($filter{offset}) { + $query .= " OFFSET ?"; + push @all_vars, $filter{offset}; + } - my $sth = prepare_execute_query($form, $dbh, $query, @filter_vars, @filter_vars, @filter_vars); + my $sth = prepare_execute_query($form, $dbh, $query, @all_vars); my ($h_oe_id, $q_oe_id); if ($form->{l_oe_id}) { @@ -577,6 +654,7 @@ SQL # - warehouse_id - will return matches with this warehouse_id only # - partnumber - will return only matches where the given string is a substring of the partnumber # - partsid - will return matches with this parts_id only +# - classification_id - will return matches with this parts with this classification only # - description - will return only matches where the given string is a substring of the description # - chargenumber - will return only matches where the given string is a substring of the chargenumber # - bestbefore - will return only matches with this bestbefore date @@ -628,6 +706,11 @@ sub get_warehouse_report { push @filter_vars, like($filter{partnumber}); } + if ($filter{classification_id}) { + push @filter_ary, "p.classification_id = ?"; + push @filter_vars, $filter{classification_id}; + } + if ($filter{description}) { push @filter_ary, "p.description ILIKE ?"; push @filter_vars, like($filter{description}); @@ -648,6 +731,11 @@ sub get_warehouse_report { push @filter_vars, trim($form->{bestbefore}); } + if ($filter{classification_id}) { + push @filter_ary, "p.classification_id = ?"; + push @filter_vars, $filter{classification_id}; + } + if ($filter{ean}) { push @filter_ary, "p.ean ILIKE ?"; push @filter_vars, like($filter{ean}); @@ -700,6 +788,8 @@ sub get_warehouse_report { "warehouseid" => "i.warehouse_id", "partnumber" => "p.partnumber", "partdescription" => "p.description", + "classification_id" => "p.classification_id", + "part_type" => "p.part_type", "bindescription" => "b.description", "binid" => "b.id", "chargenumber" => "i.chargenumber", @@ -709,7 +799,11 @@ sub get_warehouse_report { "warehousedescription" => "w.description", "partunit" => "p.unit", "stock_value" => "p.lastcost / COALESCE(pfac.factor, 1)", + "purchase_price" => "p.lastcost", ); + $form->{l_classification_id} = 'Y'; + $form->{l_part_type} = 'Y'; + my $select_clause = join ', ', map { +/^l_/; "$select_tokens{$'} AS $'" } ( grep( { !/qty/ and /^l_/ and $form->{$_} eq 'Y' } keys %$form), qw(l_parts_id l_qty l_partunit) ); @@ -727,7 +821,7 @@ sub get_warehouse_report { qw(l_parts_id l_qty l_partunit) ); my $query = - qq|SELECT $select_clause + qq|SELECT * FROM ( SELECT $select_clause FROM inventory i LEFT JOIN parts p ON i.parts_id = p.id LEFT JOIN bin b ON i.bin_id = b.id @@ -735,9 +829,17 @@ sub get_warehouse_report { $joins WHERE $where_clause GROUP BY $group_clause - ORDER BY $sort_spec|; + ORDER BY $sort_spec ) AS lines WHERE qty<>0|; - my $sth = prepare_execute_query($form, $dbh, $query, @filter_vars); + if ($filter{limit}) { + $query .= " LIMIT ?"; + push @filter_vars,$filter{limit}; + } + if ($filter{offset}) { + $query .= " OFFSET ?"; + push @filter_vars, $filter{offset}; + } + my $sth = prepare_execute_query($form, $dbh, $query, @filter_vars ); my (%non_empty_bins, @all_fields, @contents); @@ -881,6 +983,40 @@ sub get_basic_bin_info { return map { $_->{bin_id} => $_ } @{ $result }; } + +sub get_basic_warehouse_info { + $main::lxdebug->enter_sub(); + + my $self = shift; + my %params = @_; + + Common::check_params(\%params, qw(id)); + + my $myconfig = \%main::myconfig; + my $form = $main::form; + + my $dbh = $params{dbh} || $form->get_standard_dbh(); + + my @ids = 'ARRAY' eq ref $params{id} ? @{ $params{id} } : ($params{id}); + + my $query = + qq|SELECT w.id AS warehouse_id, w.description AS warehouse_description + FROM warehouse w + WHERE w.id IN (| . join(', ', ('?') x scalar(@ids)) . qq|)|; + + my $result = selectall_hashref_query($form, $dbh, $query, map { conv_i($_) } @ids); + + if ('' eq ref $params{id}) { + $result = $result->[0] || { }; + $main::lxdebug->leave_sub(); + + return $result; + } + + $main::lxdebug->leave_sub(); + + return map { $_->{warehouse_id} => $_ } @{ $result }; +} # # Eingabe: Teilenummer, Lagernummer (warehouse) # Ausgabe: Die maximale Anzahl der Teile in diesem Lager @@ -1091,6 +1227,106 @@ An expiration date. Note that this is not by default used by C =back +=head2 create_assembly \%PARAMS, [ \%PARAMS, ... ] + +Creates an assembly if all defined items are available. + +Assembly item(s) will be stocked out and the assembly will be stocked in, +taking into account the qty and units which can be defined for each +assembly item separately. + +The calling params originate from C but only parts_id with the +attribute assembly are processed. + +The typical params would be: + + my %TRANSFER = ( + 'login' => $::myconfig{login}, + 'dst_warehouse_id' => $form->{warehouse_id}, + 'dst_bin_id' => $form->{bin_id}, + 'chargenumber' => $form->{chargenumber}, + 'bestbefore' => $form->{bestbefore}, + 'assembly_id' => $form->{parts_id}, + 'qty' => $form->{qty}, + 'comment' => $form->{comment} + ); + +=head3 Prerequisites + +All of these prerequisites have to be trueish, otherwise the function will exit +unsuccessfully with a return value of undef. + +=over 4 + +=item Mandantory params + + assembly_id, qty, login, dst_warehouse_id and dst_bin_id are mandatory. + +=item Subset named 'Assembly' of data set 'Part' + + assembly_id has to be an id in the table parts with the valid subset assembly. + +=item Assembly is composed of assembly item(s) + + There has to be at least one data set in the table assembly referenced to this assembly_id. + +=item Assembly cannot be destroyed or disassembled + + Assemblies are like cakes. You cannot disassemble it. NEVER. + No negative nor zero qty's are valid inputs. + +=item The assembly item(s) have to be in the same warehouse + + inventory.warehouse_id equals dst_warehouse_id (client configurable). + +=item The assembly item(s) have to be in stock with the qty needed + + I can only make a cake by receipt if I have ALL ingredients and + in the needed stock amount. + The qty of stocked in assembly item(s) has to fit into the + number of the qty of the assemblies, which are going to be created (client configurable). + +=item assembly item(s) with the parts set 'service' are ignored + + The subset 'Services' of part will not transferred for assembly item(s). + +=back + +Client configurable prerequisites can be changed with different +prerequisites as described in client_config (s.a. next chapter). + + +=head2 default creation of assembly + +The valid state of the assembly item(s) used for the assembly process are +'out' for the general direction and 'used' as the specific reason. +The valid state of the assembly is 'in' for the direction and 'assembled' +as the specific reason. + +The method is transaction safe, in case of errors not a single entry will be made +in inventory. + +Two prerequisites can be changed with this global parameters + +=over 2 + +=item $::instance_conf->get_transfer_default_warehouse_for_assembly + + If trueish we try to get all the items form the default bins defined in parts + and do not try to find them in the destination warehouse. Returns an + error if not all items have set a default bin in parts. + +=item $::instance_conf->get_bin_id_ignore_onhand + + If trueish we can create assemblies even if we do not have enough items in stock. + The needed qty will be booked in a special bin, which has to be configured in + the client config. + +=back + + + + =head1 BUGS None yet.