kivitendo/SL/Presenter/Part.pm @ 912e5eff
2504ebe1 | Sven Schöling | package SL::Presenter::Part;
|
||
use strict;
|
||||
2477c498 | Moritz Bunkus | use SL::DB::Part;
|
||
65d2537d | Martin Helmling | use SL::DB::PartClassification;
|
||
use SL::Locale::String qw(t8);
|
||||
0e5e3501 | Sven Schöling | use SL::Presenter::EscapedText qw(escape is_escaped);
|
||
f95d6823 | Bernd Bleßmann | use SL::Presenter::Tag qw(input_tag html_tag name_to_id select_tag link_tag);
|
||
2477c498 | Moritz Bunkus | |||
2504ebe1 | Sven Schöling | use Exporter qw(import);
|
||
0e5e3501 | Sven Schöling | our @EXPORT_OK = qw(
|
||
part_picker part select_classification classification_abbreviation
|
||||
type_abbreviation separate_abbreviation typeclass_abbreviation
|
||||
);
|
||||
our %EXPORT_TAGS = (ALL => \@EXPORT_OK);
|
||||
f33995ff | Geoffrey Richardson | |||
use Carp;
|
||||
sub part {
|
||||
0e5e3501 | Sven Schöling | my ($part, %params) = @_;
|
||
f33995ff | Geoffrey Richardson | |||
$params{display} ||= 'inline';
|
||||
croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
|
||||
ee51b82f | Tamino Steinert | my $text = escape($part->partnumber);
|
||
if (! delete $params{no_link}) {
|
||||
my $href = 'controller.pl?action=Part/edit'
|
||||
. '&part.id=' . escape($part->id);
|
||||
$text = link_tag($href, $text, %params);
|
||||
}
|
||||
0e5e3501 | Sven Schöling | |||
is_escaped($text);
|
||||
f33995ff | Geoffrey Richardson | }
|
||
2504ebe1 | Sven Schöling | |||
sub part_picker {
|
||||
0e5e3501 | Sven Schöling | my ($name, $value, %params) = @_;
|
||
2504ebe1 | Sven Schöling | |||
20576aac | Sven Schöling | $value = SL::DB::Manager::Part->find_by(id => $value) if $value && !ref $value;
|
||
0e5e3501 | Sven Schöling | my $id = $params{id} || name_to_id($name);
|
||
ee74ea82 | Sven Schöling | |||
my @classes = $params{class} ? ($params{class}) : ();
|
||||
push @classes, 'part_autocomplete';
|
||||
2504ebe1 | Sven Schöling | |||
my $ret =
|
||||
0e5e3501 | Sven Schöling | input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id,
|
||
426a5bf3 | Sven Schöling | 'data-part-picker-data' => JSON::to_json(\%params),
|
||
) .
|
||||
0e5e3501 | Sven Schöling | input_tag("", ref $value ? $value->displayable_name : '', id => "${id}_name", %params);
|
||
2504ebe1 | Sven Schöling | |||
5aec18fe | Sven Schöling | $::request->layout->add_javascripts('kivi.Part.js');
|
||
3ac83c61 | Moritz Bunkus | $::request->presenter->need_reinit_widgets($id);
|
||
0e5e3501 | Sven Schöling | html_tag('span', $ret, class => 'part_picker');
|
||
2504ebe1 | Sven Schöling | }
|
||
0e5e3501 | Sven Schöling | sub picker { goto &part_picker }
|
||
65d2537d | Martin Helmling | #
|
||
# shortcut for article type
|
||||
#
|
||||
sub type_abbreviation {
|
||||
0e5e3501 | Sven Schöling | my ($part_type) = @_;
|
||
0ca195cd | Bernd Bleßmann | |||
return '' if !$part_type;
|
||||
65d2537d | Martin Helmling | 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';
|
||||
return $::locale->text('Service (typeabbreviation)');
|
||||
}
|
||||
#
|
||||
# Translations for Abbreviations:
|
||||
#
|
||||
# $::locale->text('None (typeabbreviation)')
|
||||
# $::locale->text('Purchase (typeabbreviation)')
|
||||
# $::locale->text('Sales (typeabbreviation)')
|
||||
# $::locale->text('Merchandise (typeabbreviation)')
|
||||
# $::locale->text('Production (typeabbreviation)')
|
||||
#
|
||||
# and for descriptions
|
||||
# $::locale->text('Purchase')
|
||||
# $::locale->text('Sales')
|
||||
# $::locale->text('Merchandise')
|
||||
# $::locale->text('Production')
|
||||
#
|
||||
# shortcut for article type
|
||||
#
|
||||
sub classification_abbreviation {
|
||||
0e5e3501 | Sven Schöling | my ($id) = @_;
|
||
0ca195cd | Bernd Bleßmann | |||
return '' if !$id;
|
||||
65d2537d | Martin Helmling | SL::DB::Manager::PartClassification->cache_all();
|
||
my $obj = SL::DB::PartClassification->load_cached($id);
|
||||
$obj && $obj->abbreviation ? t8($obj->abbreviation) : '';
|
||||
}
|
||||
ea1c5dfb | Martin Helmling | sub typeclass_abbreviation {
|
||
0e5e3501 | Sven Schöling | my ($part) = @_;
|
||
ea1c5dfb | Martin Helmling | return '' if !$part || !$part->isa('SL::DB::Part');
|
||
0e5e3501 | Sven Schöling | return type_abbreviation($part->part_type) . classification_abbreviation($part->classification_id);
|
||
ea1c5dfb | Martin Helmling | }
|
||
219d88ab | Martin Helmling | #
|
||
# shortcut for article type
|
||||
#
|
||||
sub separate_abbreviation {
|
||||
0e5e3501 | Sven Schöling | my ($id) = @_;
|
||
0ca195cd | Bernd Bleßmann | |||
return '' if !$id;
|
||||
219d88ab | Martin Helmling | SL::DB::Manager::PartClassification->cache_all();
|
||
my $obj = SL::DB::PartClassification->load_cached($id);
|
||||
$obj && $obj->abbreviation && $obj->report_separate ? t8($obj->abbreviation) : '';
|
||||
}
|
||||
65d2537d | Martin Helmling | #
|
||
# generate selection tag
|
||||
#
|
||||
sub select_classification {
|
||||
0e5e3501 | Sven Schöling | my ($name, %attributes) = @_;
|
||
ce904bd6 | Geoffrey Richardson | |||
65d2537d | Martin Helmling | $attributes{value_key} = 'id';
|
||
$attributes{title_key} = 'description';
|
||||
ce904bd6 | Geoffrey Richardson | |||
my $classification_type_filter = delete $attributes{type} // [];
|
||||
my $collection = SL::DB::Manager::PartClassification->get_all_sorted( where => $classification_type_filter );
|
||||
65d2537d | Martin Helmling | $_->description($::locale->text($_->description)) for @{ $collection };
|
||
0e5e3501 | Sven Schöling | select_tag( $name, $collection, %attributes );
|
||
65d2537d | Martin Helmling | }
|
||
2504ebe1 | Sven Schöling | 1;
|
||
9aaadfdc | Sven Schöling | |||
__END__
|
||||
=encoding utf-8
|
||||
=head1 NAME
|
||||
f33995ff | Geoffrey Richardson | SL::Presenter::Part - Part related presenter stuff
|
||
9aaadfdc | Sven Schöling | |||
=head1 SYNOPSIS
|
||||
f33995ff | Geoffrey Richardson | # Create an html link for editing/opening a part/service/assembly
|
||
5eb16622 | Geoffrey Richardson | my $object = SL::DB::Manager::Part->get_first;
|
||
f33995ff | Geoffrey Richardson | my $html = SL::Presenter->get->part($object, display => 'inline');
|
||
see also L<SL::Presenter>
|
||||
9aaadfdc | Sven Schöling | |||
=head1 DESCRIPTION
|
||||
see L<SL::Presenter>
|
||||
=head1 FUNCTIONS
|
||||
f33995ff | Geoffrey Richardson | =over 2
|
||
=item C<part, $object, %params>
|
||||
Returns a rendered version (actually an instance of
|
||||
L<SL::Presenter::EscapedText>) of the part object C<$object>
|
||||
ee51b82f | Tamino Steinert | Remaining C<%params> are passed to the function
|
||
C<SL::Presenter::Tag::link_tag>. It can include:
|
||||
f33995ff | Geoffrey Richardson | |||
9aaadfdc | Sven Schöling | =over 4
|
||
f33995ff | Geoffrey Richardson | =item * display
|
||
ee51b82f | Tamino Steinert | Either C<inline> (the default) or C<table-cell>. Is passed to the function
|
||
C<SL::Presenter::Tag::link_tag>.
|
||||
=item * no_link
|
||||
If falsish (the default) then the part number will be linked to the "edit"
|
||||
dialog.
|
||||
f33995ff | Geoffrey Richardson | |||
=back
|
||||
2f6a11fb | Bernd Bleßmann | =back
|
||
f33995ff | Geoffrey Richardson | |||
=over 2
|
||||
65d2537d | Martin Helmling | =item C<classification_abbreviation $classification_id>
|
||
Returns the shortcut of the classification
|
||||
=back
|
||||
=over 2
|
||||
219d88ab | Martin Helmling | =item C<separate_abbreviation $classification_id>
|
||
5eb16622 | Geoffrey Richardson | Returns the shortcut of the classification if the classification has the separate flag set.
|
||
219d88ab | Martin Helmling | |||
=back
|
||||
=over 2
|
||||
65d2537d | Martin Helmling | =item C<select_classification $name,%params>
|
||
5eb16622 | Geoffrey Richardson | Returns an HTML select tag with all available classifications.
|
||
65d2537d | Martin Helmling | |||
C<%params> can include:
|
||||
=over 4
|
||||
=item * default
|
||||
5eb16622 | Geoffrey Richardson | The id of the selected item.
|
||
65d2537d | Martin Helmling | |||
=back
|
||||
=back
|
||||
=over 2
|
||||
9e278d06 | Moritz Bunkus | =item C<part_picker $name, $value, %params>
|
||
9aaadfdc | Sven Schöling | |||
9e278d06 | Moritz Bunkus | All-in-one picker widget for parts. The name will be both id and name
|
||
of the resulting hidden C<id> input field (but the ID can be
|
||||
overwritten with C<$params{id}>).
|
||||
9aaadfdc | Sven Schöling | |||
9e278d06 | Moritz Bunkus | An additional dummy input will be generated which is used to find
|
||
976b1ddf | Geoffrey Richardson | parts. For a detailed description of its behaviour, see section
|
||
9e278d06 | Moritz Bunkus | C<PART PICKER SPECIFICATION>.
|
||
9aaadfdc | Sven Schöling | |||
9e278d06 | Moritz Bunkus | C<$value> can be a parts id or a C<Rose::DB:Object> instance.
|
||
9aaadfdc | Sven Schöling | |||
862bc8ce | Geoffrey Richardson | If C<%params> contains C<part_type> only parts of this type will be used
|
||
9e278d06 | Moritz Bunkus | for autocompletion. You may comma separate multiple types as in
|
||
C<part,assembly>.
|
||||
9aaadfdc | Sven Schöling | |||
2de6b963 | Bernd Bleßmann | If C<%params> contains C<status> only parts of this status will be used
|
||
for autocompletion. C<status> can be one of the following strings:
|
||||
C<active>, C<obsolete> or C<all>. C<active> is the default if C<status> is
|
||||
not given.
|
||||
880a2e9f | Moritz Bunkus | If C<%params> contains C<unit> only parts with this unit will be used
|
||
for autocompletion. You may comma separate multiple units as in
|
||||
C<h,min>.
|
||||
9aaadfdc | Sven Schöling | |||
df33875f | Moritz Bunkus | If C<%params> contains C<convertible_unit> only parts with a unit
|
||
that's convertible to unit will be used for autocompletion.
|
||||
9aaadfdc | Sven Schöling | |||
0c73b206 | Bernd Bleßmann | If C<%params> contains C<with_makemodel> or C<with_customer_partnumber> even
|
||
parts will be used for autocompletion which partnumber is a vendor partnumber
|
||||
(makemodel) or a customer partnumber.
|
||||
65e51d8e | Bernd Bleßmann | 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.
|
||||
Also note that you can set C<multiple> to 0 (or not set C<multiple>) on
|
||||
creation of the picker, but can open the alternative multi select popup
|
||||
with js like this:
|
||||
C<$("#pp_id").data("part_picker").o.multiple=1; $("#pp_id").data("part_picker").open_dialog()'>
|
||||
where C<pp_id> is the dom id of the part picker.
|
||||
Or you can even do it the other way round setting C<multiple> to 1 on creation
|
||||
and open a single selection popup with js.
|
||||
4af0680d | Bernd Bleßmann | If C<%params> contains C<multiple_pos_input> an input field with the dom id
|
||
C<multi_items_position> will be rendered in the alternative popup.
|
||||
This can be used in the callback for C<set_multi_items> to controll the
|
||||
input postion for the items.
|
||||
8c30110b | Bernd Bleßmann | If C<%params> contains C<multiple_limit> the alternative popup will not
|
||
show any results if there are more than C<multiple_limit> results. A warning
|
||||
message is displayed in this case. Set C<multiple_limit> to 0 to disable
|
||||
the limitation. The limit defaults to 100.
|
||||
976b1ddf | Geoffrey Richardson | Obsolete parts will by default not be displayed for selection. However they are
|
||
58e12eca | Sven Schöling | accepted as default values and can persist during updates. As with other
|
||
5c48c74b | Sven Schöling | selectors though, they are not selectable once overridden.
|
||
58e12eca | Sven Schöling | |||
c89986ac | Sven Schöling | C<part_picker> will register it's javascript for inclusion in the next header
|
||
5162c8cc | Sven Schöling | 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
|
||||
5aa4c453 | Bernd Bleßmann | (multiple blocks, ajax, old C<bin/mozilla> style controllers) you need to
|
||
5aec18fe | Sven Schöling | include C<kivi.Part.js> yourself.
|
||
9aaadfdc | Sven Schöling | |||
5162c8cc | Sven Schöling | 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>
|
||||
5eb16622 | Geoffrey Richardson | 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
|
||||
5162c8cc | Sven Schöling | 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
|
||||
65e51d8e | Bernd Bleßmann | opening a popup dialog for graphical interaction.
|
||
5162c8cc | Sven Schöling | |||
=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
|
||||
9aaadfdc | Sven Schöling | =back
|
||
81e4602d | Sven Schöling | =head1 PART PICKER SPECIFICATION
|
||
The following list of design goals were applied:
|
||||
=over 4
|
||||
=item *
|
||||
Parts should not be perceived by the user as distinct inputs of partnumber and
|
||||
description but as a single object
|
||||
=item *
|
||||
Easy to use without documentation for novice users
|
||||
=item *
|
||||
Fast to use with keyboard for experienced users
|
||||
=item *
|
||||
Possible to use without any keyboard interaction for mouse (or touchscreen)
|
||||
users
|
||||
=item *
|
||||
Must not leave the current page in event of ambiguity (cf. current select_item
|
||||
mechanism)
|
||||
=item *
|
||||
Should be useable with hand scanners or similar alternative keyboard devices
|
||||
=item *
|
||||
Should not require a feedback/check loop in the common case
|
||||
=item *
|
||||
976b1ddf | Geoffrey Richardson | Should not be constrained to exact matches
|
||
81e4602d | Sven Schöling | |||
5162c8cc | Sven Schöling | =item *
|
||
Must be atomic
|
||||
=item *
|
||||
Action should be overridable
|
||||
81e4602d | Sven Schöling | =back
|
||
The implementation consists of the following parts which will be referenced later:
|
||||
=over 4
|
||||
=item 1
|
||||
A hidden input (id input), used to hold the id of the selected part. The only
|
||||
input that gets submitted
|
||||
=item 2
|
||||
An input (dummy input) containing a description of the currently selected part,
|
||||
also used by the user to search for parts
|
||||
=item 3
|
||||
A jquery.autocomplete mechanism attached to the dummy field
|
||||
=item 4
|
||||
A popup layer for both feedback and input of additional data in case of
|
||||
ambiguity.
|
||||
=item 5
|
||||
976b1ddf | Geoffrey Richardson | An internal status of the part picker, indicating whether id input and dummy
|
||
81e4602d | Sven Schöling | input are consistent. After leaving the dummy input the part picker must
|
||
place itself in a consistent status.
|
||||
=item 6
|
||||
A clickable icon (popup trigger) attached to the dummy input, which triggers the popup layer.
|
||||
=back
|
||||
9aaadfdc | Sven Schöling | =head1 BUGS
|
||
686cf1ec | Sven Schöling | None atm :)
|
||
9aaadfdc | Sven Schöling | |||
=head1 AUTHOR
|
||||
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
|
||||
65d2537d | Martin Helmling | Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
|
||
9aaadfdc | Sven Schöling | =cut
|