Neues Feature: Chartpicker
[kivitendo-erp.git] / SL / Presenter / Chart.pm
1 package SL::Presenter::Chart;
2
3 use strict;
4
5 use SL::DB::Chart;
6
7 use Exporter qw(import);
8 use Data::Dumper;
9 our @EXPORT = qw(chart_picker chart);
10
11 use Carp;
12
13 sub chart {
14   my ($self, $chart, %params) = @_;
15
16   $params{display} ||= 'inline';
17
18   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
19
20   my $text = join '', (
21     $params{no_link} ? '' : '<a href="am.pl?action=edit_account&id=' . $self->escape($chart->id) . '">',
22     $self->escape($chart->accno),
23     $params{no_link} ? '' : '</a>',
24   );
25   return $self->escaped_text($text);
26 }
27
28 sub chart_picker {
29   my ($self, $name, $value, %params) = @_;
30
31   $value = SL::DB::Manager::Chart->find_by(id => $value) if $value && !ref $value;
32   my $id = delete($params{id}) || $self->name_to_id($name);
33   my $fat_set_item = delete $params{fat_set_item};
34
35   my @classes = $params{class} ? ($params{class}) : ();
36   push @classes, 'chart_autocomplete';
37   push @classes, 'chartpicker_fat_set_item' if $fat_set_item;
38
39   my $ret =
40     $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id) .
41     join('', map { $params{$_} ? $self->input_tag("", delete $params{$_}, id => "${id}_${_}", type => 'hidden') : '' } qw(type category choose booked)) .
42     $self->input_tag("", (ref $value && $value->can('displayable_name')) ? $value->displayable_name : '', id => "${id}_name", %params);
43
44   $::request->layout->add_javascripts('autocomplete_chart.js');
45   $::request->presenter->need_reinit_widgets($id);
46
47   $self->html_tag('span', $ret, class => 'chart_picker');
48 }
49
50 1;
51
52 __END__
53
54 =encoding utf-8
55
56 =head1 NAME
57
58 SL::Presenter::Chart - Chart related presenter stuff
59
60 =head1 SYNOPSIS
61
62   # Create an html link for editing/opening a chart
63   my $object = SL::DB::Manager::Chart->get_first;
64   my $html   = SL::Presenter->get->chart($object, display => 'inline');
65
66 see also L<SL::Presenter>
67
68 =head1 DESCRIPTION
69
70 see L<SL::Presenter>
71
72 =head1 FUNCTIONS
73
74 =over 2
75
76 =item C<chart, $object, %params>
77
78 Returns a rendered version (actually an instance of
79 L<SL::Presenter::EscapedText>) of the chart object C<$object>
80
81 C<%params> can include:
82
83 =over 4
84
85 =item * display
86
87 Either C<inline> (the default) or C<table-cell>. At the moment both
88 representations are identical and produce the chart's name linked
89 to the corresponding 'edit' action.
90
91 =back
92
93 =back
94
95 =over 2
96
97 =item C<chart_picker $name, $value, %params>
98
99 All-in-one picker widget for charts. The code was originally copied and adapted
100 from the part picker. The name will be both id and name of the resulting hidden
101 C<id> input field (but the ID can be overwritten with C<$params{id}>).
102
103 An additional dummy input will be generated which is used to find
104 chart. For a detailed description of its behaviour, see section
105 C<CHART PICKER SPECIFICATION>.
106
107 For some examples of usage see the test page at controller.pl?action=Chart/test_page
108
109 C<$value> can be a chart id or a C<Rose::DB:Object> instance.
110
111 C<%params> can include:
112
113 =over 4
114
115 =item * category
116
117 If C<%params> contains C<category> only charts of this category will be
118 available for selection (in the autocompletion and in the popup).
119
120 You may comma separate multiple categories, e.g C<A,Q,L>.
121
122 In SL::DB::Manager::Chart there is also a filter called C<selected_category>,
123 which filters the possible charts according to the category checkboxes the user
124 selects in the popup window. This filter may further restrict the results of
125 the filter category, but the user is not able to "break out of" the limits
126 defined by C<category>. In fact if the categories are restricted by C<category>
127 the popup template should only show checkboxes for those categories.
128
129 =item * type
130
131 If C<%params> contains C<type> only charts of this type will be used for
132 autocompletion, i.e. the selection is filtered. You may comma separate multiple
133 types.
134
135 Type is usually a filter for link: C<AR,AR_paid>
136
137 Type can also be a specially defined type: C<guv>, C<balance>, C<bank>
138
139 See the type filter in SL::DB::Manager::Chart.
140
141 =item * choose
142
143 If C<%params> is passed with choose=1 the input of the filter field in the
144 popup window is cleared. This is useful if a chart was already selected and we
145 want to choose a new chart and immediately see all options.
146
147 =item * fat_set_item
148
149 If C<%params> is passed with fat_set_item=1 the contents of the selected chart
150 object (the elements of the database chart table) are passed back via JSON into
151 the item object. There is an example on the test page.
152
153 Without fat_set_item only the variables id and name (displayable name) are
154 available.
155
156 =back
157
158 C<chart_picker> will register its javascript for inclusion in the next header
159 rendering. If you write a standard controller that only calls C<render> once, it
160 will just work.  In case the header is generated in a different render call
161 (multiple blocks, ajax, old C<bin/mozilla> style controllers) you need to
162 include C<js/autocomplete_part.js> yourself.
163
164 =back
165
166 =head1 POPUP LAYER
167
168 For users that don't regularly do bookkeeping and haven't memorised all the
169 account numbers and names there are some filter options inside the popup window
170 to quickly narrow down the possible matches. You can filter by
171
172 =over 4
173
174 =item * chart accno or description, inside the input field
175
176 =item * accounts that have already been booked
177
178 =item * by category (AIELQC)
179
180 By selecting category checkboxes the list of available charts can be
181 restricted. If all checkboxes are unchecked all possible charts are shown.
182
183 =back
184
185 There are two views in the list of accounts. By default all possible accounts are shown as a long list.
186
187 But you can also show more information, in this case the resulting list is automatically paginated:
188
189 =over 4
190
191 =item * the balance of the account (as determined by SL::DB::Chart get_balance, which checks for user rights)
192
193 =item * the category
194
195 =item * the invoice date of the last transaction (may lie in the future)
196
197 =back
198
199 The partpicker also has two views, but whereas the compact block view of the
200 partpicker allows part boxes to be aligned in two columns, the chartpicker
201 block view still only shows one chart per row, because there is more
202 information and the account names can be quite long. This behaviour is
203 determined by css, however, and could be changed (div.cpc_block).  The downside
204 of this is that you have to scroll the window to reach the pagination controls.
205
206 The default view of the display logic is to use block view, so we don't have to
207 pass any parameters in the pagination GET. However, the default view for the
208 user is the list view, so the popup window is opened with the "Hide additional
209 information" select box already ticked.
210
211 =head1 CHART PICKER SPECIFICATION
212
213 The following list of design goals were applied:
214
215 =over 4
216
217 =item *
218
219 Charts should not be perceived by the user as distinct inputs of chart number and
220 description but as a single object
221
222 =item *
223
224 Easy to use without documentation for novice users
225
226 =item *
227
228 Fast to use with keyboard for experienced users
229
230 =item *
231
232 Possible to use without any keyboard interaction for mouse (or touchscreen)
233 users
234
235 =item *
236
237 Must not leave the current page in event of ambiguity (cf. current select_item
238 mechanism)
239
240 =item *
241
242 Should not require a feedback/check loop in the common case
243
244 =item *
245
246 Should not be constrained to exact matches
247
248 =back
249
250 The implementation consists of the following parts which will be referenced later:
251
252 =over 4
253
254 =item 1
255
256 A hidden input (id input), used to hold the id of the selected part. The only
257 input that gets submitted
258
259 =item 2
260
261 An input (dummy input) containing a description of the currently selected chart,
262 also used by the user to search for charts
263
264 =item 3
265
266 A jquery.autocomplete mechanism attached to the dummy field
267
268 =item 4
269
270 A popup layer for both feedback and input of additional data in case of
271 ambiguity.
272
273 =item 5
274
275 An internal status of the chart picker, indicating whether id input and dummy
276 input are consistent. After leaving the dummy input the chart picker must
277 place itself in a consistent status.
278
279 =item 6
280
281 A clickable icon (popup trigger) attached to the dummy input, which triggers the popup layer.
282
283 =back
284
285 =head1 BUGS
286
287 None atm :)
288
289 =head1 AUTHOR
290
291 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
292
293 G. Richardson E<lt>information@kivitendo-premium.deE<gt>
294
295 =cut