X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=SL%2FDO.pm;h=7659ffe607737c4f52d4552b13030d1721ba7e8e;hb=38f974a2140303118c2241ba95bde1703be5a237;hp=3d4cb23bf6b99e7c5cd79b2adb81abcf3a9d837f;hpb=65e5f16ab7ec7bb55ef7130665f96112e0f92f77;p=kivitendo-erp.git diff --git a/SL/DO.pm b/SL/DO.pm index 3d4cb23bf..7659ffe60 100644 --- a/SL/DO.pm +++ b/SL/DO.pm @@ -42,8 +42,10 @@ use SL::CVar; use SL::DB::DeliveryOrder; use SL::DB::Status; use SL::DBUtils; +use SL::HTML::Restrict; use SL::RecordLinks; use SL::IC; +use SL::TransNumber; use strict; @@ -74,6 +76,7 @@ sub transactions { sm.name AS salesman FROM delivery_orders dord LEFT JOIN $vc ct ON (dord.${vc}_id = ct.id) + LEFT JOIN contacts cp ON (dord.cp_id = cp.cp_id) LEFT JOIN employee e ON (dord.employee_id = e.id) LEFT JOIN employee sm ON (dord.salesman_id = sm.id) LEFT JOIN project pr ON (dord.globalproject_id = pr.id) @@ -104,6 +107,11 @@ sub transactions { push @values, '%' . $form->{$vc} . '%'; } + if ($form->{"cp_name"}) { + push @where, "(cp.cp_name ILIKE ? OR cp.cp_givenname ILIKE ?)"; + push @values, ('%' . $form->{"cp_name"} . '%')x2; + } + foreach my $item (qw(employee_id salesman_id)) { next unless ($form->{$item}); push @where, "dord.$item = ?"; @@ -215,6 +223,7 @@ sub save { # connect to database, turn off autocommit my $dbh = $form->get_standard_dbh($myconfig); + my $restricter = SL::HTML::Restrict->create; my ($query, @values, $sth, $null); @@ -224,20 +233,18 @@ sub save { my $ic_cvar_configs = CVar->get_configs(module => 'IC', dbh => $dbh); - $form->{donumber} = $form->update_defaults($myconfig, $form->{type} eq 'sales_delivery_order' ? 'sdonumber' : 'pdonumber', $dbh) unless $form->{donumber}; + my $trans_number = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{donumber}, id => $form->{id}); + $form->{donumber} ||= $trans_number->create_unique; $form->{employee_id} = (split /--/, $form->{employee})[1] if !$form->{employee_id}; $form->get_employee($dbh) unless ($form->{employee_id}); my $ml = ($form->{type} eq 'sales_delivery_order') ? 1 : -1; - if ($form->{id}) { + my (@processed_doi, @processed_dois); - $query = qq|DELETE FROM delivery_order_items_stock WHERE delivery_order_item_id IN (SELECT id FROM delivery_order_items WHERE delivery_order_id = ?)|; - do_query($form, $dbh, $query, conv_i($form->{id})); - - $query = qq|DELETE FROM delivery_order_items WHERE delivery_order_id = ?|; - do_query($form, $dbh, $query, conv_i($form->{id})); + if ($form->{id}) { + # only delete shipto complete $query = qq|DELETE FROM shipto WHERE trans_id = ? AND module = 'DO'|; do_query($form, $dbh, $query, conv_i($form->{id})); @@ -246,8 +253,8 @@ sub save { $query = qq|SELECT nextval('id')|; ($form->{id}) = selectrow_query($form, $dbh, $query); - $query = qq|INSERT INTO delivery_orders (id, donumber, employee_id, currency_id) VALUES (?, '', ?, (SELECT currency_id FROM defaults LIMIT 1))|; - do_query($form, $dbh, $query, $form->{id}, conv_i($form->{employee_id})); + $query = qq|INSERT INTO delivery_orders (id, donumber, employee_id, currency_id, taxzone_id) VALUES (?, '', ?, (SELECT currency_id FROM defaults LIMIT 1), ?)|; + do_query($form, $dbh, $query, $form->{id}, conv_i($form->{employee_id}), $form->{taxzone_id}); } my $project_id; @@ -265,23 +272,23 @@ sub save { $query = qq|SELECT id, unit FROM parts WHERE id IN (| . join(', ', map { '?' } @part_ids) . qq|)|; %part_unit_map = selectall_as_map($form, $dbh, $query, 'id', 'unit', @part_ids); } - - my $q_item_id = qq|SELECT nextval('delivery_order_items_id')|; - my $h_item_id = prepare_query($form, $dbh, $q_item_id); - - my $q_item = - qq|INSERT INTO delivery_order_items ( - id, delivery_order_id, parts_id, description, longdescription, qty, base_qty, - sellprice, discount, unit, reqdate, project_id, serialnumber, - ordnumber, transdate, cusordnumber, - lastcost, price_factor_id, price_factor, marge_price_factor, pricegroup_id) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - (SELECT factor FROM price_factors WHERE id = ?), ?, ?)|; + my $q_item = <{type} =~ /^sales/ ? 'out' : 'in'; @@ -289,6 +296,36 @@ sub save { for my $i (1 .. $form->{rowcount}) { next if (!$form->{"id_$i"}); + my $position = $i; + + my $cvars; + if (!$form->{"delivery_order_items_id_$i"}) { + # there is no persistent id, therefore create one with all necessary constraints + my $q_item_id = qq|SELECT nextval('delivery_order_items_id')|; + my $h_item_id = prepare_query($form, $dbh, $q_item_id); + do_statement($form, $h_item_id, $q_item_id); + $form->{"delivery_order_items_id_$i"} = $h_item_id->fetchrow_array(); + $query = qq|INSERT INTO delivery_order_items (id, delivery_order_id, position, parts_id) VALUES (?, ?, ?, ?)|; + do_query($form, $dbh, $query, conv_i($form->{"delivery_order_items_id_$i"}), + conv_i($form->{"id"}), conv_i($position), conv_i($form->{"id_$i"})); + $h_item_id->finish(); + + # get values for CVars from master data for new items + $cvars = CVar->get_custom_variables(dbh => $dbh, + module => 'IC', + trans_id => $form->{"id_$i"}, + ); + } else { + # get values for CVars from custom_variables for existing items + $cvars = CVar->get_custom_variables(dbh => $dbh, + module => 'IC', + sub_module => 'delivery_order_items', + trans_id => $form->{"delivery_order_items_id_$i"}, + ); + } + # map only non-editable CVars to form (editable ones are already there) + map { $form->{"ic_cvar_$_->{name}_$i"} = $_->{value} unless $_->{flag_editable} } @{ $cvars }; + $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"}); my $item_unit = $part_unit_map{$form->{"id_$i"}}; @@ -309,8 +346,6 @@ sub save { $items_reqdate = ($form->{"reqdate_$i"}) ? $form->{"reqdate_$i"} : undef; - do_statement($form, $h_item_id, $q_item_id); - my ($item_id) = $h_item_id->fetchrow_array(); # Get pricegroup_id and save it. Unfortunately the interface # also uses ID "0" for signalling that none is selected, but "0" @@ -320,8 +355,8 @@ sub save { $pricegroup_id = undef if !$pricegroup_id; # save detail record in delivery_order_items table - @values = (conv_i($item_id), conv_i($form->{id}), conv_i($form->{"id_$i"}), - $form->{"description_$i"}, $form->{"longdescription_$i"}, + @values = (conv_i($form->{id}), conv_i($position), conv_i($form->{"id_$i"}), + $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}), $form->{"qty_$i"}, $baseqty, $form->{"sellprice_$i"}, $form->{"discount_$i"} / 100, $form->{"unit_$i"}, conv_date($items_reqdate), conv_i($form->{"project_id_$i"}), @@ -331,28 +366,76 @@ sub save { $form->{"lastcost_$i"}, conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}), - $pricegroup_id); + $pricegroup_id, + $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"}, + conv_i($form->{"delivery_order_items_id_$i"})); do_statement($form, $h_item, $q_item, @values); + push @processed_doi, $form->{"delivery_order_items_id_$i"}; # transaction safe? my $stock_info = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_$i"}); foreach my $sinfo (@{ $stock_info }) { - @values = ($item_id, $sinfo->{qty}, $sinfo->{unit}, conv_i($sinfo->{warehouse_id}), - conv_i($sinfo->{bin_id}), $sinfo->{chargenumber}, conv_date($sinfo->{bestbefore})); + # if we have stock_info, we have to check for persistents entries + if (!$sinfo->{"delivery_order_items_stock_id"}) { + my $q_item_stock_id = qq|SELECT nextval('id')|; + my $h_item_stock_id = prepare_query($form, $dbh, $q_item_stock_id); + do_statement($form, $h_item_stock_id, $q_item_stock_id); + $sinfo->{"delivery_order_items_stock_id"} = $h_item_stock_id->fetchrow_array(); + $query = qq|INSERT INTO delivery_order_items_stock (id, delivery_order_item_id, qty, unit, warehouse_id, bin_id) + VALUES (?, ?, ?, ?, ?, ?)|; + do_query($form, $dbh, $query, conv_i($sinfo->{"delivery_order_items_stock_id"}), + conv_i($form->{"delivery_order_items_id_$i"}), $sinfo->{qty}, $sinfo->{unit}, conv_i($sinfo->{warehouse_id}), + conv_i($sinfo->{bin_id})); + $h_item_stock_id->finish(); + } + @values = ($form->{"delivery_order_items_id_$i"}, $sinfo->{qty}, $sinfo->{unit}, conv_i($sinfo->{warehouse_id}), + conv_i($sinfo->{bin_id}), $sinfo->{chargenumber}, conv_date($sinfo->{bestbefore}), + conv_i($sinfo->{"delivery_order_items_stock_id"})); do_statement($form, $h_item_stock, $q_item_stock, @values); + push @processed_dois, $sinfo->{"delivery_order_items_stock_id"}; } CVar->save_custom_variables(module => 'IC', sub_module => 'delivery_order_items', - trans_id => $item_id, + trans_id => $form->{"delivery_order_items_id_$i"}, configs => $ic_cvar_configs, variables => $form, name_prefix => 'ic_', name_postfix => "_$i", dbh => $dbh); + # link order items with doi + if ($form->{"converted_from_orderitems_id_$i"}) { + RecordLinks->create_links('dbh' => $dbh, + 'mode' => 'ids', + 'from_table' => 'orderitems', + 'from_ids' => $form->{"converted_from_orderitems_id_$i"}, + 'to_table' => 'delivery_order_items', + 'to_id' => $form->{"delivery_order_items_id_$i"}, + ); + delete $form->{"converted_from_orderitems_id_$i"}; + } } - $h_item_id->finish(); + # 1. search for orphaned dois; processed_dois may be empty (no transfer) TODO: be supersafe and alter same statement for doi and oi + $query = sprintf 'SELECT id FROM delivery_order_items_stock WHERE delivery_order_item_id in + (select id from delivery_order_items where delivery_order_id = ?)'; + $query .= sprintf ' AND NOT id IN (%s)', join ', ', ("?") x scalar @processed_dois if (scalar @processed_dois); + @values = (conv_i($form->{id}), map { conv_i($_) } @processed_dois); + my @orphaned_dois_ids = map { $_->{id} } selectall_hashref_query($form, $dbh, $query, @values); + if (scalar @orphaned_dois_ids) { + # clean up delivery_order_items_stock + $query = sprintf 'DELETE FROM delivery_order_items_stock WHERE id IN (%s)', join ', ', ("?") x scalar @orphaned_dois_ids; + do_query($form, $dbh, $query, @orphaned_dois_ids); + } + # 2. search for orphaned doi + $query = sprintf 'SELECT id FROM delivery_order_items WHERE delivery_order_id = ? AND NOT id IN (%s)', join ', ', ("?") x scalar @processed_doi; + @values = (conv_i($form->{id}), map { conv_i($_) } @processed_doi); + my @orphaned_ids = map { $_->{id} } selectall_hashref_query($form, $dbh, $query, @values); + if (scalar @orphaned_ids) { + # clean up delivery_order_items + $query = sprintf 'DELETE FROM delivery_order_items WHERE id IN (%s)', join ', ', ("?") x scalar @orphaned_ids; + do_query($form, $dbh, $query, @orphaned_ids); + } $h_item->finish(); $h_item_stock->finish(); @@ -387,10 +470,10 @@ sub save { conv_i($form->{id})); do_query($form, $dbh, $query, @values); - # add shipto $form->{name} = $form->{ $form->{vc} }; $form->{name} =~ s/--$form->{"$form->{vc}_id"}//; + # add shipto if (!$form->{shipto_id}) { $form->add_shipto($dbh, $form->{id}, "DO"); } @@ -417,7 +500,7 @@ sub save { $form->{saved_donumber} = $form->{donumber}; $form->{saved_ordnumber} = $form->{ordnumber}; $form->{saved_cusordnumber} = $form->{cusordnumber}; - + Common::webdav_folder($form); $main::lxdebug->leave_sub(); @@ -678,7 +761,8 @@ sub retrieve { doi.reqdate, doi.project_id, doi.serialnumber, doi.lastcost, doi.ordnumber, doi.transdate, doi.cusordnumber, doi.longdescription, doi.price_factor_id, doi.price_factor, doi.marge_price_factor, doi.pricegroup_id, - pr.projectnumber, dord.transdate AS dord_transdate, + doi.active_price_source, doi.active_discount_source, + pr.projectnumber, dord.transdate AS dord_transdate, dord.donumber, pg.partsgroup FROM delivery_order_items doi JOIN parts p ON (doi.parts_id = p.id) @@ -686,7 +770,7 @@ sub retrieve { LEFT JOIN project pr ON (doi.project_id = pr.id) LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) WHERE doi.delivery_order_id IN ($do_ids_placeholders) - ORDER BY doi.oid|; + ORDER BY doi.delivery_order_id, doi.position|; $form->{form_details} = selectall_hashref_query($form, $dbh, $query, @do_ids); @@ -704,7 +788,8 @@ sub retrieve { my $in_out = $form->{type} =~ /^sales/ ? 'out' : 'in'; $query = - qq|SELECT qty, unit, bin_id, warehouse_id, chargenumber, bestbefore + qq|SELECT id as delivery_order_items_stock_id, qty, unit, bin_id, + warehouse_id, chargenumber, bestbefore FROM delivery_order_items_stock WHERE delivery_order_item_id = ?|; my $sth = prepare_query($form, $dbh, $query); @@ -746,6 +831,7 @@ sub order_details { my $position = 0; my $subtotal_header = 0; my $subposition = 0; + my $si_position = 0; my (@project_ids); @@ -802,8 +888,10 @@ sub order_details { my @arrays = qw(runningnumber number description longdescription qty unit partnotes serialnumber reqdate projectnumber projectdescription + weight lineweight si_runningnumber si_number si_description - si_warehouse si_bin si_chargenumber si_bestbefore si_qty si_unit weight lineweight); + si_warehouse si_bin si_chargenumber si_bestbefore + si_qty si_qty_nofmt si_unit); map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays); @@ -821,10 +909,12 @@ sub order_details { next if (!$form->{"id_$i"}); if ($item->[1] ne $sameitem) { - push(@{ $form->{description} }, qq|$item->[1]|); + push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|); $sameitem = $item->[1]; - map({ push(@{ $form->{$_} }, "") } grep({ $_ ne "description" } @arrays)); + map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" && $_ !~ /^si_/} @arrays)); + map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, []) } grep({ $_ =~ /^si_/} @arrays)); + $si_position++; } $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"}); @@ -844,6 +934,8 @@ sub order_details { $position++; } + $si_position++; + my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 }; my $project = $projects_by_id{$form->{"project_id_$i"}} || SL::DB::Project->new; @@ -871,6 +963,26 @@ sub order_details { push @{ $form->{TEMPLATE_ARRAYS}->{lineweight} }, $form->format_amount($myconfig, $lineweight, 3); push @{ $form->{TEMPLATE_ARRAYS}->{lineweight_nofmt} }, $lineweight; + my $stock_info = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_$i"}); + + foreach my $si (@{ $stock_info }) { + $num_si++; + + do_statement($form, $h_bin_wh, $q_bin_wh, conv_i($si->{bin_id}), conv_i($si->{warehouse_id})); + my $bin_wh = $h_bin_wh->fetchrow_hashref(); + + push @{ $form->{TEMPLATE_ARRAYS}{si_runningnumber}[$si_position-1] }, $num_si; + push @{ $form->{TEMPLATE_ARRAYS}{si_number}[$si_position-1] }, $form->{"partnumber_$i"}; + push @{ $form->{TEMPLATE_ARRAYS}{si_description}[$si_position-1] }, $form->{"description_$i"}; + push @{ $form->{TEMPLATE_ARRAYS}{si_warehouse}[$si_position-1] }, $bin_wh->{warehouse}; + push @{ $form->{TEMPLATE_ARRAYS}{si_bin}[$si_position-1] }, $bin_wh->{bin}; + push @{ $form->{TEMPLATE_ARRAYS}{si_chargenumber}[$si_position-1] }, $si->{chargenumber}; + push @{ $form->{TEMPLATE_ARRAYS}{si_bestbefore}[$si_position-1] }, $si->{bestbefore}; + push @{ $form->{TEMPLATE_ARRAYS}{si_qty}[$si_position-1] }, $form->format_amount($myconfig, $si->{qty} * 1); + push @{ $form->{TEMPLATE_ARRAYS}{si_qty_nofmt}[$si_position-1] }, $si->{qty} * 1; + push @{ $form->{TEMPLATE_ARRAYS}{si_unit}[$si_position-1] }, $si->{unit}; + } + if ($form->{"assembly_$i"}) { $sameitem = ""; @@ -887,37 +999,37 @@ sub order_details { while (my $ref = $h_pg->fetchrow_hashref("NAME_lc")) { if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) { - map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays)); + map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" && $_ !~ /^si_/} @arrays)); + map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, []) } grep({ $_ =~ /^si_/} @arrays)); $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--"; push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem); + $si_position++; } - push(@{ $form->{TEMPLATE_ARRAYS}->{"description"} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq| -- $ref->{partnumber}, $ref->{description}|); - map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays)); + push(@{ $form->{TEMPLATE_ARRAYS}->{"description"} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq| -- $ref->{partnumber}, $ref->{description}|); + map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" && $_ !~ /^si_/} @arrays)); + map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, []) } grep({ $_ =~ /^si_/} @arrays)); + $si_position++; } } - if ($form->{"inventory_accno_$i"} && !$form->{"assembly_$i"}) { - my $stock_info = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_$i"}); - - foreach my $si (@{ $stock_info }) { - $num_si++; - - do_statement($form, $h_bin_wh, $q_bin_wh, conv_i($si->{bin_id}), conv_i($si->{warehouse_id})); - my $bin_wh = $h_bin_wh->fetchrow_hashref(); - - push @{ $form->{TEMPLATE_ARRAYS}{si_runningnumber}[$position-1] }, $num_si; - push @{ $form->{TEMPLATE_ARRAYS}{si_number}[$position-1] }, $form->{"partnumber_$i"}; - push @{ $form->{TEMPLATE_ARRAYS}{si_description}[$position-1] }, $form->{"description_$i"}; - push @{ $form->{TEMPLATE_ARRAYS}{si_warehouse}[$position-1] }, $bin_wh->{warehouse}; - push @{ $form->{TEMPLATE_ARRAYS}{si_bin}[$position-1] }, $bin_wh->{bin}; - push @{ $form->{TEMPLATE_ARRAYS}{si_chargenumber}[$position-1] }, $si->{chargenumber}; - push @{ $form->{TEMPLATE_ARRAYS}{si_bestbefore}[$position-1] }, $si->{bestbefore}; - push @{ $form->{TEMPLATE_ARRAYS}{si_qty}[$position-1] }, $form->format_amount($myconfig, $si->{qty} * 1); - push @{ $form->{TEMPLATE_ARRAYS}{si_qty_nofmt}[$position-1] }, $si->{qty} * 1; - push @{ $form->{TEMPLATE_ARRAYS}{si_unit}[$position-1] }, $si->{unit}; - } + my $cvars; + if (! $form->{"delivery_order_items_id_$i"}) { + # get values for CVars from master data for new items + $cvars = CVar->get_custom_variables(dbh => $dbh, + module => 'IC', + trans_id => $form->{"id_$i"}, + ); + } else { + # get values for CVars from custom_variables for existing items + $cvars = CVar->get_custom_variables(dbh => $dbh, + module => 'IC', + sub_module => 'delivery_order_items', + trans_id => $form->{"delivery_order_items_id_$i"}, + ); } + # map only non-editable CVars to form (editable ones are already there) + map { $form->{"ic_cvar_$_->{name}_$i"} = $_->{value} unless $_->{flag_editable} } @{ $cvars }; push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} }, CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_)