X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=SL%2FIS.pm;h=30570c059e8e31007f7c9980851e1549cdac6af7;hb=fea73b796d2289979be90dfc808346ec10a008f6;hp=227b4428b68d82c4d9c136c7592ed8ae84540394;hpb=89b2668811eac6023ad58322e2f9970ddb6a27c9;p=kivitendo-erp.git diff --git a/SL/IS.pm b/SL/IS.pm index 227b4428b..30570c059 100644 --- a/SL/IS.pm +++ b/SL/IS.pm @@ -373,6 +373,12 @@ sub invoice_details { $sth->finish; } + CVar->get_non_editable_ic_cvars(form => $form, + dbh => $dbh, + row => $i, + sub_module => 'invoice', + may_converted_from => ['delivery_order_items', 'orderitems', 'invoice']); + push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} }, CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_) for @{ $ic_cvar_configs }; @@ -578,18 +584,15 @@ sub post_invoice { $form->{defaultcurrency} = $form->get_default_currency($myconfig); my $defaultcurrency = $form->{defaultcurrency}; - # Seit neuestem wird die department_id schon übergeben UND $form->department nicht mehr - # korrekt zusammengebaut. Sehr wahrscheinlich beim Umstieg auf T8 kaputt gegangen - # Ich lass den Code von 2005 erstmal noch stehen ;-) jb 03-2011 - if (!$form->{department_id}){ - ($null, $form->{department_id}) = split(/--/, $form->{department}); - } - my $all_units = AM->retrieve_units($myconfig, $form); if (!$payments_only) { + if ($form->{storno}) { + _delete_transfers($dbh, $form, $form->{storno_id}); + } if ($form->{id}) { &reverse_invoice($dbh, $form); + _delete_transfers($dbh, $form, $form->{id}); } else { my $trans_number = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{invnumber}, save => 1); @@ -633,6 +636,8 @@ sub post_invoice { $form->{amount} = {}; $form->{amount_cogs} = {}; + my @processed_invoice_ids; + foreach my $i (1 .. $form->{rowcount}) { if ($form->{type} eq "credit_note") { $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"}) * -1; @@ -653,6 +658,7 @@ sub post_invoice { if ($form->{"id_$i"}) { my $item_unit; + my $position = $i; if (defined($baseunits{$form->{"id_$i"}})) { $item_unit = $baseunits{$form->{"id_$i"}}; @@ -738,7 +744,7 @@ sub post_invoice { if ($form->{"assembly_$i"}) { # record assembly item as allocated - &process_assembly($dbh, $myconfig, $form, $form->{"id_$i"}, $baseqty); + &process_assembly($dbh, $myconfig, $form, $position, $form->{"id_$i"}, $baseqty); } else { $allocated = &cogs($dbh, $myconfig, $form, $form->{"id_$i"}, $baseqty, $basefactor, $i); @@ -753,21 +759,36 @@ sub post_invoice { $pricegroup_id *= 1; $pricegroup_id = undef if !$pricegroup_id; - my ($invoice_id) = selectfirst_array_query($form, $dbh, qq|SELECT nextval('invoiceid')|); + CVar->get_non_editable_ic_cvars(form => $form, + dbh => $dbh, + row => $i, + sub_module => 'invoice', + may_converted_from => ['delivery_order_items', 'orderitems', 'invoice']); + + if (!$form->{"invoice_id_$i"}) { + # there is no persistent id, therefore create one with all necessary constraints + my $q_invoice_id = qq|SELECT nextval('invoiceid')|; + my $h_invoice_id = prepare_query($form, $dbh, $q_invoice_id); + do_statement($form, $h_invoice_id, $q_invoice_id); + $form->{"invoice_id_$i"} = $h_invoice_id->fetchrow_array(); + my $q_create_invoice_id = qq|INSERT INTO invoice (id, trans_id, position, parts_id) values (?, ?, ?, ?)|; + do_query($form, $dbh, $q_create_invoice_id, conv_i($form->{"invoice_id_$i"}), + conv_i($form->{id}), conv_i($position), conv_i($form->{"id_$i"})); + $h_invoice_id->finish(); + } # save detail record in invoice table - $query = - qq|INSERT INTO invoice (id, trans_id, parts_id, description, longdescription, qty, - sellprice, fxsellprice, discount, allocated, assemblyitem, - unit, deliverydate, project_id, serialnumber, pricegroup_id, - ordnumber, donumber, transdate, cusordnumber, base_qty, subtotal, - marge_percent, marge_total, lastcost, active_price_source, active_discount_source, - - price_factor_id, price_factor, marge_price_factor) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - (SELECT factor FROM price_factors WHERE id = ?), ?)|; - - @values = ($invoice_id, conv_i($form->{id}), conv_i($form->{"id_$i"}), + $query = <{id}), conv_i($position), conv_i($form->{"id_$i"}), $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}), $form->{"qty_$i"}, $form->{"sellprice_$i"}, $fxsellprice, $form->{"discount_$i"}, $allocated, 'f', @@ -779,18 +800,33 @@ sub post_invoice { $form->{"lastcost_$i"}, $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"}, conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}), - conv_i($form->{"marge_price_factor_$i"})); + conv_i($form->{"marge_price_factor_$i"}), + conv_i($form->{"invoice_id_$i"})); do_query($form, $dbh, $query, @values); + push @processed_invoice_ids, $form->{"invoice_id_$i"}; CVar->save_custom_variables(module => 'IC', sub_module => 'invoice', - trans_id => $invoice_id, + trans_id => $form->{"invoice_id_$i"}, configs => $ic_cvar_configs, variables => $form, name_prefix => 'ic_', name_postfix => "_$i", dbh => $dbh); } + # link previous items with invoice items + foreach (qw(delivery_order_items orderitems invoice)) { + if (!$form->{useasnew} && $form->{"converted_from_${_}_id_$i"}) { + RecordLinks->create_links('dbh' => $dbh, + 'mode' => 'ids', + 'from_table' => $_, + 'from_ids' => $form->{"converted_from_${_}_id_$i"}, + 'to_table' => 'invoice', + 'to_id' => $form->{"invoice_id_$i"}, + ); + } + delete $form->{"converted_from_${_}_id_$i"}; + } } # total payments, don't move we need it here @@ -1139,7 +1175,7 @@ sub post_invoice { conv_date($form->{"invdate"}), conv_date($form->{"orddate"}), conv_date($form->{"quodate"}), conv_i($form->{"customer_id"}), $amount, $netamount, $form->{"paid"}, conv_date($form->{"duedate"}), conv_date($form->{"deliverydate"}), '1', $form->{"shippingpoint"}, - $form->{"shipvia"}, conv_i($form->{"terms"}), $form->{"notes"}, $form->{"intnotes"}, + $form->{"shipvia"}, conv_i($form->{"terms"}), $restricter->process($form->{"notes"}), $form->{"intnotes"}, $form->{"currency"}, conv_i($form->{"department_id"}), conv_i($form->{"payment_id"}), $form->{"taxincluded"} ? 't' : 'f', $form->{"type"}, conv_i($form->{"language_id"}), conv_i($form->{"taxzone_id"}), conv_i($form->{"shipto_id"}), conv_i($form->{"employee_id"}), conv_i($form->{"salesman_id"}), conv_i($form->{storno_id}), $form->{"storno"} ? 't' : 'f', @@ -1188,14 +1224,16 @@ sub post_invoice { } # Link this record to the records it was created from. - RecordLinks->create_links('dbh' => $dbh, - 'mode' => 'ids', - 'from_table' => 'oe', - 'from_ids' => $form->{convert_from_oe_ids}, - 'to_table' => 'ar', - 'to_id' => $form->{id}, - ); - delete $form->{convert_from_oe_ids}; + if ($form->{convert_from_oe_ids}) { + RecordLinks->create_links('dbh' => $dbh, + 'mode' => 'ids', + 'from_table' => 'oe', + 'from_ids' => $form->{convert_from_oe_ids}, + 'to_table' => 'ar', + 'to_id' => $form->{id}, + ); + delete $form->{convert_from_oe_ids}; + } my @convert_from_do_ids = map { $_ * 1 } grep { $_ } split m/\s+/, $form->{convert_from_do_ids}; @@ -1217,6 +1255,16 @@ sub post_invoice { 'arap_id' => $form->{id}, 'table' => 'ar',); + # search for orphaned invoice items + $query = sprintf 'SELECT id FROM invoice WHERE trans_id = ? AND NOT id IN (%s)', join ', ', ("?") x scalar @processed_invoice_ids; + @values = (conv_i($form->{id}), map { conv_i($_) } @processed_invoice_ids); + my @orphaned_ids = map { $_->{id} } selectall_hashref_query($form, $dbh, $query, @values); + if (scalar @orphaned_ids) { + # clean up invoice items + $query = sprintf 'DELETE FROM invoice WHERE id IN (%s)', join ', ', ("?") x scalar @orphaned_ids; + do_query($form, $dbh, $query, @orphaned_ids); + } + # safety check datev export if ($::instance_conf->get_datev_check_on_sales_invoice) { my $transdate = $::form->{invdate} ? DateTime->from_lxoffice($::form->{invdate}) : undef; @@ -1247,6 +1295,136 @@ sub post_invoice { return $rc; } +sub transfer_out { + $::lxdebug->enter_sub; + + my ($self, $form, $dbh) = @_; + + my (@errors, @transfers); + + # do nothing, if transfer default is not requeseted at all + if (!$::instance_conf->get_transfer_default) { + $::lxdebug->leave_sub; + return \@errors; + } + + require SL::WH; + + foreach my $i (1 .. $form->{rowcount}) { + next if !$form->{"id_$i"}; + my ($err, $wh_id, $bin_id) = _determine_wh_and_bin($dbh, $::instance_conf, + $form->{"id_$i"}, + $form->{"qty_$i"}, + $form->{"unit_$i"}); + next if ($err eq 'ignore service'); + if (!@{ $err } && $wh_id && $bin_id) { + push @transfers, { + 'parts_id' => $form->{"id_$i"}, + 'qty' => $form->{"qty_$i"}, + 'unit' => $form->{"unit_$i"}, + 'transfer_type' => 'shipped', + 'src_warehouse_id' => $wh_id, + 'src_bin_id' => $bin_id, + 'project_id' => $form->{"project_id_$i"}, + 'invoice_id' => $form->{"invoice_id_$i"}, + 'comment' => $::locale->text("Default transfer invoice"), + }; + } + + push @errors, @{ $err }; + } + + if (!@errors) { + WH->transfer(@transfers); + } + + $::lxdebug->leave_sub; + return \@errors; +} + +sub _determine_wh_and_bin { + $::lxdebug->enter_sub(2); + + my ($dbh, $conf, $part_id, $qty, $unit) = @_; + my @errors; + + my $part = SL::DB::Part->new(id => $part_id)->load; + + # ignore service if they are not configured to be transfered + if ($part->is_service && !$conf->get_transfer_default_services) { + $::lxdebug->leave_sub(2); + return 'ignore service'; + } + + # test negative qty + if ($qty < 0) { + push @errors, $::locale->text("Cannot transfer negative quantities."); + return (\@errors); + } + + # get/test default bin + my ($default_wh_id, $default_bin_id); + if ($conf->get_transfer_default_use_master_default_bin) { + $default_wh_id = $conf->get_warehouse_id if $conf->get_warehouse_id; + $default_bin_id = $conf->get_bin_id if $conf->get_bin_id; + } + my $wh_id = $part->warehouse_id || $default_wh_id; + my $bin_id = $part->bin_id || $default_bin_id; + + # check qty and bin + if ($bin_id) { + my ($max_qty, $error) = WH->get_max_qty_parts_bin(dbh => $dbh, + parts_id => $part->id, + bin_id => $bin_id); + if ($error == 1) { + push @errors, $::locale->text("Part \"#1\" has chargenumber or best before date set. So it cannot be transfered automaticaly.", + $part->description); + } + my $form_unit_obj = SL::DB::Unit->new(name => $unit)->load; + my $part_unit_qty = $form_unit_obj->convert_to($qty, $part->unit_obj); + my $diff_qty = $max_qty - $part_unit_qty; + if (!@errors && $diff_qty < 0) { + push @errors, $::locale->text("For part \"#1\" there are missing #2 #3 in the default warehouse/bin \"#4/#5\"", + $part->description, + $::form->format_amount(\%::myconfig, -1*$diff_qty), + $part->unit_obj->name, + SL::DB::Warehouse->new(id => $wh_id)->load->description, + SL::DB::Bin->new( id => $bin_id)->load->description); + } + } else { + push @errors, $::locale->text("For part \"#1\" there is no default warehouse and bin defined.", + $part->description); + } + + # transfer to special "ignore onhand" bin if requested and default bin does not work + if (@errors && $conf->get_transfer_default_ignore_onhand && $conf->get_bin_id_ignore_onhand) { + $wh_id = $conf->get_warehouse_id_ignore_onhand; + $bin_id = $conf->get_bin_id_ignore_onhand; + if ($wh_id && $bin_id) { + @errors = (); + } else { + push @errors, $::locale->text("For part \"#1\" there is no default warehouse and bin for ignoring onhand defined.", + $part->description); + } + } + + $::lxdebug->leave_sub(2); + return (\@errors, $wh_id, $bin_id); +} + +sub _delete_transfers { + $::lxdebug->enter_sub; + + my ($dbh, $form, $id) = @_; + + my $query = qq|DELETE FROM inventory WHERE invoice_id + IN (SELECT id FROM invoice WHERE trans_id = ?)|; + + do_query($form, $dbh, $query, $id); + + $::lxdebug->leave_sub; +} + sub _delete_payments { $main::lxdebug->enter_sub(); @@ -1358,7 +1536,7 @@ sub post_payment { sub process_assembly { $main::lxdebug->enter_sub(); - my ($dbh, $myconfig, $form, $id, $totalqty) = @_; + my ($dbh, $myconfig, $form, $position, $id, $totalqty) = @_; my $query = qq|SELECT a.parts_id, a.qty, p.assembly, p.partnumber, p.description, p.unit, @@ -1379,7 +1557,7 @@ sub process_assembly { $ref->{qty} *= $totalqty; if ($ref->{assembly}) { - &process_assembly($dbh, $myconfig, $form, $ref->{parts_id}, $ref->{qty}); + &process_assembly($dbh, $myconfig, $form, $position, $ref->{parts_id}, $ref->{qty}); next; } else { if ($ref->{inventory_accno_id}) { @@ -1389,9 +1567,10 @@ sub process_assembly { # save detail record for individual assembly item in invoice table $query = - qq|INSERT INTO invoice (trans_id, description, parts_id, qty, sellprice, fxsellprice, allocated, assemblyitem, unit) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)|; - my @values = (conv_i($form->{id}), $ref->{description}, conv_i($ref->{parts_id}), $ref->{qty}, 0, 0, $allocated, 't', $ref->{unit}); + qq|INSERT INTO invoice (trans_id, position, description, parts_id, qty, sellprice, fxsellprice, allocated, assemblyitem, unit) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)|; + my @values = (conv_i($form->{id}), conv_i($position), $ref->{description}, + conv_i($ref->{parts_id}), $ref->{qty}, 0, 0, $allocated, 't', $ref->{unit}); do_query($form, $dbh, $query, @values); } @@ -1538,7 +1717,6 @@ sub reverse_invoice { # delete acc_trans my @values = (conv_i($form->{id})); do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE trans_id = ?|, @values); - do_query($form, $dbh, qq|DELETE FROM invoice WHERE trans_id = ?|, @values); do_query($form, $dbh, qq|DELETE FROM shipto WHERE (trans_id = ?) AND (module = 'AR')|, @values); $main::lxdebug->leave_sub(); @@ -1553,6 +1731,7 @@ sub delete_invoice { my $dbh = $form->get_standard_dbh; &reverse_invoice($dbh, $form); + _delete_transfers($dbh, $form, $form->{id}); my @values = (conv_i($form->{id})); @@ -1576,6 +1755,7 @@ sub delete_invoice { my @queries = ( qq|DELETE FROM status WHERE trans_id = ?|, qq|DELETE FROM periodic_invoices WHERE ar_id = ?|, + qq|DELETE FROM invoice WHERE trans_id = ?|, qq|DELETE FROM ar WHERE id = ?|, ); @@ -1678,7 +1858,7 @@ sub retrieve_invoice { i.id AS invoice_id, i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.discount, i.parts_id AS id, i.unit, i.deliverydate AS reqdate, - i.project_id, i.serialnumber, i.id AS invoice_pos, i.pricegroup_id, i.ordnumber, i.donumber, i.transdate, i.cusordnumber, i.subtotal, i.lastcost, + i.project_id, i.serialnumber, i.pricegroup_id, i.ordnumber, i.donumber, i.transdate, i.cusordnumber, i.subtotal, i.lastcost, i.price_factor_id, i.price_factor, i.marge_price_factor, i.active_price_source, i.active_discount_source, p.partnumber, p.assembly, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, p.formel, p.listprice, pr.projectnumber, pg.partsgroup, prg.pricegroup @@ -1693,7 +1873,7 @@ sub retrieve_invoice { LEFT JOIN chart c2 ON ((SELECT tc.income_accno_id FROM taxzone_charts tc WHERE tc.taxzone_id = '$taxzone_id' and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c2.id) LEFT JOIN chart c3 ON ((SELECT tc.expense_accno_id FROM taxzone_charts tc WHERE tc.taxzone_id = '$taxzone_id' and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c3.id) - WHERE (i.trans_id = ?) AND NOT (i.assemblyitem = '1') ORDER BY i.id|; + WHERE (i.trans_id = ?) AND NOT (i.assemblyitem = '1') ORDER BY i.position|; $sth = prepare_execute_query($form, $dbh, $query, $id); @@ -1705,7 +1885,6 @@ sub retrieve_invoice { trans_id => $ref->{invoice_id}, ); map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars }; - delete $ref->{invoice_id}; map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid)) if !$ref->{"part_inventory_accno_id"}; delete($ref->{"part_inventory_accno_id"});