Artikel aus Belegen anlegen - Artikelklassifizierung berücksichtigen
[kivitendo-erp.git] / SL / Presenter / Part.pm
index c3bbea2..8f13aee 100644 (file)
@@ -3,10 +3,11 @@ package SL::Presenter::Part;
 use strict;
 
 use SL::DB::Part;
-use SL::DB::Manager::PartsClassification;
+use SL::DB::PartClassification;
+use SL::Locale::String qw(t8);
 
 use Exporter qw(import);
-our @EXPORT = qw(part_picker part select_classification classification_abbreviation type_abbreviation type_and_classification);
+our @EXPORT = qw(part_picker part select_classification classification_abbreviation type_abbreviation separate_abbreviation typeclass_abbreviation);
 
 use Carp;
 
@@ -29,30 +30,28 @@ sub part_picker {
   my ($self, $name, $value, %params) = @_;
 
   $value = SL::DB::Manager::Part->find_by(id => $value) if $value && !ref $value;
-  my $id = delete($params{id}) || $self->name_to_id($name);
-  my $fat_set_item = delete $params{fat_set_item};
+  my $id = $params{id} || $self->name_to_id($name);
 
   my @classes = $params{class} ? ($params{class}) : ();
   push @classes, 'part_autocomplete';
-  push @classes, 'partpicker_fat_set_item' if $fat_set_item;
 
   my $ret =
-    $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id) .
-    join('', map { $params{$_} ? $self->input_tag("", delete $params{$_}, id => "${id}_${_}", type => 'hidden') : '' } qw(part_type unit convertible_unit)) .
+    $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id,
+      'data-part-picker-data' => JSON::to_json(\%params),
+    ) .
     $self->input_tag("", ref $value ? $value->displayable_name : '', id => "${id}_name", %params);
 
-  $::request->layout->add_javascripts('autocomplete_part.js');
+  $::request->layout->add_javascripts('kivi.Part.js');
   $::request->presenter->need_reinit_widgets($id);
 
   $self->html_tag('span', $ret, class => 'part_picker');
 }
 
 #
-# Must be addapted to new type table
+# shortcut for article type
 #
 sub type_abbreviation {
   my ($self, $part_type) = @_;
-  $main::lxdebug->message(LXDebug->DEBUG2(),"parttype=".$part_type);
   return $::locale->text('Assembly (typeabbreviation)')   if $part_type eq 'assembly';
   return $::locale->text('Part (typeabbreviation)')       if $part_type eq 'part';
   return $::locale->text('Assortment (typeabbreviation)') if $part_type eq 'assortment';
@@ -74,17 +73,44 @@ sub type_abbreviation {
 # $::locale->text('Merchandise')
 # $::locale->text('Production')
 
+#
+# shortcut for article type
+#
 sub classification_abbreviation {
   my ($self, $id) = @_;
-  $main::lxdebug->message(LXDebug->DEBUG2(),"classif=".$id);
-  return $::locale->text(SL::DB::Manager::PartsClassification->get_abbreviation($id));
+  SL::DB::Manager::PartClassification->cache_all();
+  my $obj = SL::DB::PartClassification->load_cached($id);
+  $obj && $obj->abbreviation ? t8($obj->abbreviation) : '';
+}
+
+sub typeclass_abbreviation {
+  my ($self, $part) = @_;
+  return '' if !$part || !$part->isa('SL::DB::Part');
+  return $self->type_abbreviation($part->part_type).$self->classification_abbreviation($part->classification_id);
+}
+
+#
+# shortcut for article type
+#
+sub separate_abbreviation {
+  my ($self, $id) = @_;
+  SL::DB::Manager::PartClassification->cache_all();
+  my $obj = SL::DB::PartClassification->load_cached($id);
+  $obj && $obj->abbreviation && $obj->report_separate ? t8($obj->abbreviation) : '';
 }
 
+#
+# generate selection tag
+#
 sub select_classification {
   my ($self, $name, %attributes) = @_;
+
   $attributes{value_key} = 'id';
   $attributes{title_key} = 'description';
-  my $collection = SL::DB::Manager::PartsClassification->get_all_sorted();
+
+  my $classification_type_filter = delete $attributes{type} // [];
+
+  my $collection = SL::DB::Manager::PartClassification->get_all_sorted( where => $classification_type_filter );
   $_->description($::locale->text($_->description)) for @{ $collection };
   return $self->select_tag( $name, $collection, %attributes );
 }
@@ -102,7 +128,7 @@ SL::Presenter::Part - Part related presenter stuff
 =head1 SYNOPSIS
 
   # Create an html link for editing/opening a part/service/assembly
-  my $object = my $object = SL::DB::Manager::Part->get_first;
+  my $object = SL::DB::Manager::Part->get_first;
   my $html   = SL::Presenter->get->part($object, display => 'inline');
 
 see also L<SL::Presenter>
@@ -136,6 +162,40 @@ to the corresponding 'edit' action.
 
 =over 2
 
+=item C<classification_abbreviation $classification_id>
+
+Returns the shortcut of the classification
+
+=back
+
+=over 2
+
+=item C<separate_abbreviation $classification_id>
+
+Returns the shortcut of the classification if the classification has the separate flag set.
+
+=back
+
+=over 2
+
+=item C<select_classification $name,%params>
+
+Returns an HTML select tag with all available classifications.
+
+C<%params> can include:
+
+=over 4
+
+=item * default
+
+The id of the selected item.
+
+=back
+
+=back
+
+=over 2
+
 =item C<part_picker $name, $value, %params>
 
 All-in-one picker widget for parts. The name will be both id and name
@@ -164,10 +224,47 @@ accepted as default values and can persist during updates. As with other
 selectors though, they are not selectable once overridden.
 
 C<part_picker> will register it's javascript for inclusion in the next header
-rendering. If you write a standard controller that only call C<render> once, it
-will just work.  In case the header is generated in a different render call
+rendering. If you write a standard controller that only calls C<render> once, it
+will just work. In case the header is generated in a different render call
 (multiple blocks, ajax, old C<bin/mozilla> style controllers) you need to
-include C<js/autocomplete_part.js> yourself.
+include C<kivi.Part.js> yourself.
+
+On pressing <enter> the picker will try to commit the current selection,
+resulting in one of the following events, whose corresponding callbacks can be
+set in C<params.actions>:
+
+=over 4
+
+=item * C<commit_one>
+
+If exactly one element matches the input, the internal id will be set to this
+id, the internal state will be set to C<PICKED> and the C<change> event on the
+picker will be fired. Additionally, if C<params> contains C<fat_set_item> a
+special event C<set_item:PartPicker> will be fired which is guaranteed to
+contain a complete JSON representation of the part.
+
+After that the action C<commit_one> will be executed, which defaults to
+clicking a button with id C<update_button> for backward compatibility reasons.
+
+=item * C<commit_many>
+
+If more than one element matches the input, the internal state will be set to
+undefined.
+
+After that the action C<commit_one> will be executed, which defaults to
+opening a popup dialog for graphical interaction. If C<params> contains
+C<multiple> an alternative popup will be opened, allowing multiple items to be
+selected. Note however that this requires an additional callback
+C<set_multi_items> to work.
+
+=item * C<commit_none>
+
+If no element matches the input, the internal state will be set to undefined.
+
+If an action for C<commit_none> exists, it will be called with the picker
+object and current term. The caller can then implement creation of new parts.
+
+=back
 
 =back
 
@@ -212,6 +309,14 @@ Should not require a feedback/check loop in the common case
 
 Should not be constrained to exact matches
 
+=item *
+
+Must be atomic
+
+=item *
+
+Action should be overridable
+
 =back
 
 The implementation consists of the following parts which will be referenced later:
@@ -257,4 +362,6 @@ None atm :)
 
 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
 
+Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
+
 =cut