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::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
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
)->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;
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) = @_;
}
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 $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));
+ 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;
# 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,
}
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 {
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 } ),
$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"
$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}),
$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}),
}
sub build_bin_select {
- $_[0]->p->select_tag('part.bin_id', [ $_[0]->warehouse->bins ],
+ select_tag('part.bin_id', [ $_[0]->warehouse->bins ],
title_key => 'description',
default => $_[0]->bin->id,
);
}
+
# get_set_inits for partpicker
sub init_parts {
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});
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 ]);
}
}
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/^<p>( )+\s+/<p>/s;
+ $self->part->{notes} =~ s/( )+<\/p>$/<\/p>/s;
+
+}
+
sub render_assortment_items_to_html {
my ($self, $assortment_items, $number_of_items) = @_;
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(
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,
],