X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=SL%2FController%2FPart.pm;h=0b476ea815ecb990c830e3a2f039288135af0c0a;hb=40df01939f53719f4f7c167ed2bf7fa67c509291;hp=a05252c363aa1b5d7de80f785847306698425a65;hpb=5aacb82be45ed25864bff2ba15a97fbff4de7e11;p=kivitendo-erp.git diff --git a/SL/Controller/Part.pm b/SL/Controller/Part.pm index a05252c36..0b476ea81 100644 --- a/SL/Controller/Part.pm +++ b/SL/Controller/Part.pm @@ -6,11 +6,13 @@ use parent qw(SL::Controller::Base); use Clone qw(clone); use SL::DB::Part; use SL::DB::PartsGroup; +use SL::DB::PriceRuleItem; use SL::DB::Shop; use SL::Controller::Helper::GetModels; use SL::Locale::String qw(t8); use SL::JSON; use List::Util qw(sum); +use List::UtilsBy qw(extract_by); use SL::Helper::Flash; use Data::Dumper; use DateTime; @@ -20,6 +22,7 @@ use SL::CVar; use SL::MoreCommon qw(save_form); use Carp; use SL::Presenter::EscapedText qw(escape is_escaped); +use SL::Presenter::Tag qw(select_tag); use Rose::Object::MakeMethods::Generic ( 'scalar --get_set_init' => [ qw(parts models part p warehouses multi_items_models @@ -31,7 +34,7 @@ use Rose::Object::MakeMethods::Generic ( all_buchungsgruppen all_payment_terms all_warehouses parts_classification_filter all_languages all_units all_price_factors) ], - 'scalar' => [ qw(warehouse bin) ], + 'scalar' => [ qw(warehouse bin stock_amounts journal) ], ); # safety @@ -138,11 +141,11 @@ sub action_save { )->save(); CVar->save_custom_variables( - dbh => $self->part->db->dbh, - module => 'IC', - trans_id => $self->part->id, - variables => $::form, # $::form->{cvar} would be nicer - always_valid => 1, + dbh => $self->part->db->dbh, + module => 'IC', + trans_id => $self->part->id, + variables => $::form, # $::form->{cvar} would be nicer + save_validity => 1, ); 1; @@ -228,8 +231,11 @@ sub render_form { $params{CUSTOM_VARIABLES} = CVar->get_custom_variables(module => 'IC', trans_id => $self->part->id); - CVar->render_inputs('variables' => $params{CUSTOM_VARIABLES}, show_disabled_message => 1, partsgroup_id => $self->part->partsgroup_id) - if (scalar @{ $params{CUSTOM_VARIABLES} }); + if (scalar @{ $params{CUSTOM_VARIABLES} }) { + CVar->render_inputs('variables' => $params{CUSTOM_VARIABLES}, show_disabled_message => 1, partsgroup_id => $self->part->partsgroup_id); + $params{CUSTOM_VARIABLES_FIRST_TAB} = []; + @{ $params{CUSTOM_VARIABLES_FIRST_TAB} } = extract_by { $_->{first_tab} == 1 } @{ $params{CUSTOM_VARIABLES} }; + } my %title_hash = ( part => t8('Edit Part'), assembly => t8('Edit Assembly'), @@ -261,6 +267,17 @@ sub action_history { history_entries => $history_entries); } +sub action_inventory { + my ($self) = @_; + + $::auth->assert('warehouse_contents'); + + $self->stock_amounts($self->part->get_simple_stock_sql); + $self->journal($self->part->get_mini_journal); + + $_[0]->render('part/_inventory_data', { layout => 0 }); +}; + sub action_update_item_totals { my ($self) = @_; @@ -397,23 +414,28 @@ sub action_add_assembly_item { } sub action_show_multi_items_dialog { + my ($self) = @_; + + my $search_term = $self->models->filtered->laundered->{all_substr_multi__ilike}; + $search_term ||= $self->models->filtered->laundered->{all_with_makemodel_substr_multi__ilike}; + $search_term ||= $self->models->filtered->laundered->{all_with_customer_partnumber_substr_multi__ilike}; + $_[0]->render('part/_multi_items_dialog', { layout => 0 }, - all_partsgroups => SL::DB::Manager::PartsGroup->get_all + all_partsgroups => SL::DB::Manager::PartsGroup->get_all, + search_term => $search_term ); } sub action_multi_items_update_result { - my $max_count = 100; - - $::form->{multi_items}->{filter}->{obsolete} = 0; + my $max_count = $::form->{limit}; my $count = $_[0]->multi_items_models->count; if ($count == 0) { my $text = escape($::locale->text('No results.')); $_[0]->render($text, { layout => 0 }); - } elsif ($count > $max_count) { - my $text = escpae($::locale->text('Too many results (#1 from #2).', $count, $max_count)); + } elsif ($max_count && $count > $max_count) { + my $text = escape($::locale->text('Too many results (#1 from #2).', $count, $max_count)); $_[0]->render($text, { layout => 0 }); } else { my $multi_items = $_[0]->multi_items_models->get; @@ -538,7 +560,7 @@ sub action_warehouse_changed { die unless ref($self->warehouse) eq 'SL::DB::Warehouse'; if ( $self->warehouse->id and @{$self->warehouse->bins} ) { - $self->bin($self->warehouse->bins->[0]); + $self->bin($self->warehouse->bins_sorted->[0]); $self->js ->html('#bin', $self->build_bin_select) ->focus('#part_bin_id'); @@ -562,7 +584,9 @@ sub action_ajax_autocomplete { # since we need a second get models instance with different filters for that, # we only modify the original filter temporarily in place if ($::form->{prefer_exact}) { - local $::form->{filter}{'all::ilike'} = delete local $::form->{filter}{'all:substr:multi::ilike'}; + local $::form->{filter}{'all::ilike'} = delete local $::form->{filter}{'all:substr:multi::ilike'}; + local $::form->{filter}{'all_with_makemodel::ilike'} = delete local $::form->{filter}{'all_with_makemodel:substr:multi::ilike'}; + local $::form->{filter}{'all_with_customer_partnumber::ilike'} = delete local $::form->{filter}{'all_with_customer_partnumber:substr:multi::ilike'}; my $exact_models = SL::Controller::Helper::GetModels->new( controller => $self, @@ -598,7 +622,13 @@ sub action_test_page { } sub action_part_picker_search { - $_[0]->render('part/part_picker_search', { layout => 0 }); + my ($self) = @_; + + my $search_term = $self->models->filtered->laundered->{all_substr_multi__ilike}; + $search_term ||= $self->models->filtered->laundered->{all_with_makemodel_substr_multi__ilike}; + $search_term ||= $self->models->filtered->laundered->{all_with_customer_partnumber_substr_multi__ilike}; + + $_[0]->render('part/part_picker_search', { layout => 0 }, search_term => $search_term); } sub action_part_picker_result { @@ -641,6 +671,8 @@ sub prepare_assortment_render_vars { sub prepare_assembly_render_vars { my ($self) = @_; + croak("Need assembly item(s) to create a 'save as new' assembly.") unless $self->part->items; + my %vars = ( items_sellprice_sum => $self->part->items_sellprice_sum, items_lastcost_sum => $self->part->items_lastcost_sum, assembly_html => $self->render_assembly_items_to_html( \@{ $self->part->items } ), @@ -728,6 +760,8 @@ sub parse_form { $self->part->assign_attributes(%{ $params}); $self->part->bin_id(undef) unless $self->part->warehouse_id; + $self->normalize_text_blocks; + # Only reset items ([]) and rewrite from form if $::form->{assortment_items} isn't empty. This # will be the case for used assortments when saving, or when a used assortment # is "used as new" @@ -794,8 +828,9 @@ sub parse_form_makemodels { $position++; my $vendor = SL::DB::Manager::Vendor->find_by(id => $makemodel->{make}) || die "Can't find vendor from make"; + my $id = $makemodels_map->{$makemodel->{id}} ? $makemodels_map->{$makemodel->{id}}->id : undef; my $mm = SL::DB::MakeModel->new( # parts_id => $self->part->id, # will be assigned by row add_makemodels - id => $makemodel->{id}, + id => $id, make => $makemodel->{make}, model => $makemodel->{model} || '', lastcost => $::form->parse_amount(\%::myconfig, $makemodel->{lastcost_as_number}), @@ -834,8 +869,9 @@ sub parse_form_customerprices { $position++; my $customer = SL::DB::Manager::Customer->find_by(id => $customerprice->{customer_id}) || die "Can't find customer from id"; + my $id = $customerprices_map->{$customerprice->{id}} ? $customerprices_map->{$customerprice->{id}}->id : undef; my $cu = SL::DB::PartCustomerPrice->new( # parts_id => $self->part->id, # will be assigned by row add_customerprices - id => $customerprice->{id}, + id => $id, customer_id => $customerprice->{customer_id}, customer_partnumber => $customerprice->{customer_partnumber} || '', price => $::form->parse_amount(\%::myconfig, $customerprice->{price_as_number}), @@ -857,12 +893,13 @@ sub parse_form_customerprices { } sub build_bin_select { - $_[0]->p->select_tag('part.bin_id', [ $_[0]->warehouse->bins ], + select_tag('part.bin_id', [ @{ $_[0]->warehouse->bins_sorted } ], title_key => 'description', default => $_[0]->bin->id, ); } + # get_set_inits for partpicker sub init_parts { @@ -881,6 +918,8 @@ sub init_part { if ( $::form->{part}{id} ) { return SL::DB::Part->new(id => $::form->{part}{id})->load(with => [ qw(makemodels customerprices prices translations partsgroup shop_parts shop_parts.shop) ]); + } elsif ( $::form->{id} ) { + return SL::DB::Part->new(id => $::form->{id})->load; # used by inventory tab } else { die "part_type missing" unless $::form->{part}{part_type}; return SL::DB::Part->new(part_type => $::form->{part}{part_type}); @@ -1020,7 +1059,7 @@ sub init_all_buchungsgruppen { if ( $self->part->orphaned ) { return SL::DB::Manager::Buchungsgruppe->get_all_sorted; } else { - return SL::DB::Manager::Buchungsgruppe->get_all(where => [ id => $self->part->buchungsgruppen_id ]); + return SL::DB::Manager::Buchungsgruppe->get_all_sorted(where => [ id => $self->part->buchungsgruppen_id ]); } } @@ -1187,6 +1226,25 @@ sub check_has_valid_part_type { die "invalid part_type" unless $_[0] =~ /^(part|service|assembly|assortment)$/; } + +sub normalize_text_blocks { + my ($self) = @_; + + # check if feature is enabled (select normalize_part_descriptions from defaults) + return unless ($::instance_conf->get_normalize_part_descriptions); + + # text block + foreach (qw(description)) { + $self->part->{$_} =~ s/\s+$//s; + $self->part->{$_} =~ s/^\s+//s; + $self->part->{$_} =~ s/ {2,}/ /g; + } + # html block (caveat: can be circumvented by using bold or italics) + $self->part->{notes} =~ s/^

( )+\s+/

/s; + $self->part->{notes} =~ s/( )+<\/p>$/<\/p>/s; + +} + sub render_assortment_items_to_html { my ($self, $assortment_items, $number_of_items) = @_; @@ -1261,7 +1319,8 @@ sub parse_add_items_to_objects { sub _setup_form_action_bar { my ($self) = @_; - my $may_edit = $::auth->assert('part_service_assembly_edit', 'may fail'); + my $may_edit = $::auth->assert('part_service_assembly_edit', 'may fail'); + my $used_in_pricerules = !!SL::DB::Manager::PriceRuleItem->get_all_count(where => [type => 'part', value_int => $self->part->id]); for my $bar ($::request->layout->get('actionbar')) { $bar->add( @@ -1287,6 +1346,7 @@ sub _setup_form_action_bar { disabled => !$self->part->id ? t8('This object has not been saved yet.') : !$may_edit ? t8('You do not have the permissions to access this function.') : !$self->part->orphaned ? t8('This object has already been used.') + : $used_in_pricerules ? t8('This object is used in price rules.') : undef, ],