d8491ecb525c905f88a7a421411d5c9277b175c9
[kivitendo-erp.git] / SL / Presenter / Part.pm
1 package SL::Presenter::Part;
2
3 use strict;
4
5 use SL::DB::Part;
6 use SL::DB::PartClassification;
7 use SL::Locale::String qw(t8);
8
9 use Exporter qw(import);
10 our @EXPORT = qw(part_picker part select_classification classification_abbreviation type_abbreviation separate_abbreviation);
11
12 use Carp;
13
14 sub part {
15   my ($self, $part, %params) = @_;
16
17   $params{display} ||= 'inline';
18
19   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
20
21   my $text = join '', (
22     $params{no_link} ? '' : '<a href="controller.pl?action=Part/edit&part.id=' . $self->escape($part->id) . '">',
23     $self->escape($part->partnumber),
24     $params{no_link} ? '' : '</a>',
25   );
26   return $self->escaped_text($text);
27 }
28
29 sub part_picker {
30   my ($self, $name, $value, %params) = @_;
31
32   $value = SL::DB::Manager::Part->find_by(id => $value) if $value && !ref $value;
33   my $id = delete($params{id}) || $self->name_to_id($name);
34   my $fat_set_item = delete $params{fat_set_item};
35
36   my @classes = $params{class} ? ($params{class}) : ();
37   push @classes, 'part_autocomplete';
38   push @classes, 'partpicker_fat_set_item' if $fat_set_item;
39
40   my $ret =
41     $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id) .
42     join('', map { $params{$_} ? $self->input_tag("", delete $params{$_}, id => "${id}_${_}", type => 'hidden') : '' } qw(part_type unit convertible_unit)) .
43     $self->input_tag("", ref $value ? $value->displayable_name : '', id => "${id}_name", %params);
44
45   $::request->layout->add_javascripts('autocomplete_part.js');
46   $::request->presenter->need_reinit_widgets($id);
47
48   $self->html_tag('span', $ret, class => 'part_picker');
49 }
50
51 #
52 # shortcut for article type
53 #
54 sub type_abbreviation {
55   my ($self, $part_type) = @_;
56   return $::locale->text('Assembly (typeabbreviation)')   if $part_type eq 'assembly';
57   return $::locale->text('Part (typeabbreviation)')       if $part_type eq 'part';
58   return $::locale->text('Assortment (typeabbreviation)') if $part_type eq 'assortment';
59   return $::locale->text('Service (typeabbreviation)');
60 }
61
62 #
63 # Translations for Abbreviations:
64 #
65 # $::locale->text('None (typeabbreviation)')
66 # $::locale->text('Purchase (typeabbreviation)')
67 # $::locale->text('Sales (typeabbreviation)')
68 # $::locale->text('Merchandise (typeabbreviation)')
69 # $::locale->text('Production (typeabbreviation)')
70 #
71 # and for descriptions
72 # $::locale->text('Purchase')
73 # $::locale->text('Sales')
74 # $::locale->text('Merchandise')
75 # $::locale->text('Production')
76
77 #
78 # shortcut for article type
79 #
80 sub classification_abbreviation {
81   my ($self, $id) = @_;
82   SL::DB::Manager::PartClassification->cache_all();
83   my $obj = SL::DB::PartClassification->load_cached($id);
84   $obj && $obj->abbreviation ? t8($obj->abbreviation) : '';
85 }
86
87 #
88 # shortcut for article type
89 #
90 sub separate_abbreviation {
91   my ($self, $id) = @_;
92   SL::DB::Manager::PartClassification->cache_all();
93   my $obj = SL::DB::PartClassification->load_cached($id);
94   $obj && $obj->abbreviation && $obj->report_separate ? t8($obj->abbreviation) : '';
95 }
96
97 #
98 # generate selection tag
99 #
100 sub select_classification {
101   my ($self, $name, %attributes) = @_;
102   $attributes{value_key} = 'id';
103   $attributes{title_key} = 'description';
104   my $collection = SL::DB::Manager::PartClassification->get_all_sorted();
105   $_->description($::locale->text($_->description)) for @{ $collection };
106   return $self->select_tag( $name, $collection, %attributes );
107 }
108
109 1;
110
111 __END__
112
113 =encoding utf-8
114
115 =head1 NAME
116
117 SL::Presenter::Part - Part related presenter stuff
118
119 =head1 SYNOPSIS
120
121   # Create an html link for editing/opening a part/service/assembly
122   my $object = my $object = SL::DB::Manager::Part->get_first;
123   my $html   = SL::Presenter->get->part($object, display => 'inline');
124
125 see also L<SL::Presenter>
126
127 =head1 DESCRIPTION
128
129 see L<SL::Presenter>
130
131 =head1 FUNCTIONS
132
133 =over 2
134
135 =item C<part, $object, %params>
136
137 Returns a rendered version (actually an instance of
138 L<SL::Presenter::EscapedText>) of the part object C<$object>
139
140 C<%params> can include:
141
142 =over 4
143
144 =item * display
145
146 Either C<inline> (the default) or C<table-cell>. At the moment both
147 representations are identical and produce the part's name linked
148 to the corresponding 'edit' action.
149
150 =back
151
152 =back
153
154 =over 2
155
156 =item C<classification_abbreviation $classification_id>
157
158 Returns the shortcut of the classification
159
160 =back
161
162 =over 2
163
164 =item C<separate_abbreviation $classification_id>
165
166 Returns the shortcut of the classification if the classifiaction has the separate flag set.
167
168 =back
169
170 =over 2
171
172 =item C<select_classification $name,%params>
173
174 Returns a HTML Select Tag with all available Classifications
175
176 C<%params> can include:
177
178 =over 4
179
180 =item * default
181
182 The Id of the selected item .
183
184 =back
185
186 =back
187
188 =over 2
189
190 =item C<part_picker $name, $value, %params>
191
192 All-in-one picker widget for parts. The name will be both id and name
193 of the resulting hidden C<id> input field (but the ID can be
194 overwritten with C<$params{id}>).
195
196 An additional dummy input will be generated which is used to find
197 parts. For a detailed description of its behaviour, see section
198 C<PART PICKER SPECIFICATION>.
199
200 C<$value> can be a parts id or a C<Rose::DB:Object> instance.
201
202 If C<%params> contains C<part_type> only parts of this type will be used
203 for autocompletion. You may comma separate multiple types as in
204 C<part,assembly>.
205
206 If C<%params> contains C<unit> only parts with this unit will be used
207 for autocompletion. You may comma separate multiple units as in
208 C<h,min>.
209
210 If C<%params> contains C<convertible_unit> only parts with a unit
211 that's convertible to unit will be used for autocompletion.
212
213 Obsolete parts will by default not be displayed for selection. However they are
214 accepted as default values and can persist during updates. As with other
215 selectors though, they are not selectable once overridden.
216
217 C<part_picker> will register it's javascript for inclusion in the next header
218 rendering. If you write a standard controller that only call C<render> once, it
219 will just work.  In case the header is generated in a different render call
220 (multiple blocks, ajax, old C<bin/mozilla> style controllers) you need to
221 include C<js/autocomplete_part.js> yourself.
222
223 =back
224
225 =head1 PART PICKER SPECIFICATION
226
227 The following list of design goals were applied:
228
229 =over 4
230
231 =item *
232
233 Parts should not be perceived by the user as distinct inputs of partnumber and
234 description but as a single object
235
236 =item *
237
238 Easy to use without documentation for novice users
239
240 =item *
241
242 Fast to use with keyboard for experienced users
243
244 =item *
245
246 Possible to use without any keyboard interaction for mouse (or touchscreen)
247 users
248
249 =item *
250
251 Must not leave the current page in event of ambiguity (cf. current select_item
252 mechanism)
253
254 =item *
255
256 Should be useable with hand scanners or similar alternative keyboard devices
257
258 =item *
259
260 Should not require a feedback/check loop in the common case
261
262 =item *
263
264 Should not be constrained to exact matches
265
266 =back
267
268 The implementation consists of the following parts which will be referenced later:
269
270 =over 4
271
272 =item 1
273
274 A hidden input (id input), used to hold the id of the selected part. The only
275 input that gets submitted
276
277 =item 2
278
279 An input (dummy input) containing a description of the currently selected part,
280 also used by the user to search for parts
281
282 =item 3
283
284 A jquery.autocomplete mechanism attached to the dummy field
285
286 =item 4
287
288 A popup layer for both feedback and input of additional data in case of
289 ambiguity.
290
291 =item 5
292
293 An internal status of the part picker, indicating whether id input and dummy
294 input are consistent. After leaving the dummy input the part picker must
295 place itself in a consistent status.
296
297 =item 6
298
299 A clickable icon (popup trigger) attached to the dummy input, which triggers the popup layer.
300
301 =back
302
303 =head1 BUGS
304
305 None atm :)
306
307 =head1 AUTHOR
308
309 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
310
311 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
312
313 =cut