1 package SL::Presenter::Chart;
 
   7 use Exporter qw(import);
 
   8 our @EXPORT_OK = qw(chart_picker chart);
 
  12 use SL::Presenter::EscapedText qw(escape is_escaped);
 
  13 use SL::Presenter::Tag qw(input_tag name_to_id html_tag);
 
  16   my ($chart, %params) = @_;
 
  18   $params{display} ||= 'inline';
 
  20   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
 
  23     $params{no_link} ? '' : '<a href="am.pl?action=edit_account&id=' . escape($chart->id) . '">',
 
  24     escape($chart->accno),
 
  25     $params{no_link} ? '' : '</a>',
 
  31   my ($name, $value, %params) = @_;
 
  33   $value = SL::DB::Manager::Chart->find_by(id => $value) if $value && !ref $value;
 
  34   my $id = delete($params{id}) || name_to_id($name);
 
  35   my $fat_set_item = delete $params{fat_set_item};
 
  37   my @classes = $params{class} ? ($params{class}) : ();
 
  38   push @classes, 'chart_autocomplete';
 
  39   push @classes, 'chartpicker_fat_set_item' if $fat_set_item;
 
  42     input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id) .
 
  43     join('', map { $params{$_} ? input_tag("", delete $params{$_}, id => "${id}_${_}", type => 'hidden') : '' } qw(type category choose booked)) .
 
  44     input_tag("", (ref $value && $value->can('displayable_name')) ? $value->displayable_name : '', id => "${id}_name", %params);
 
  46   $::request->layout->add_javascripts('autocomplete_chart.js');
 
  47   $::request->presenter->need_reinit_widgets($id);
 
  49   html_tag('span', $ret, class => 'chart_picker');
 
  52 sub picker { goto &chart_picker }
 
  62 SL::Presenter::Chart - Chart related presenter stuff
 
  66   # Create an html link for editing/opening a chart
 
  67   my $object = SL::DB::Manager::Chart->get_first;
 
  68   my $html   = SL::Presenter::Chart::chart($object, display => 'inline');
 
  70 see also L<SL::Presenter>
 
  80 =item C<chart, $object, %params>
 
  82 Returns a rendered version (actually an instance of
 
  83 L<SL::Presenter::EscapedText>) of the chart object C<$object>
 
  85 C<%params> can include:
 
  91 Either C<inline> (the default) or C<table-cell>. At the moment both
 
  92 representations are identical and produce the chart's name linked
 
  93 to the corresponding 'edit' action.
 
 101 =item C<chart_picker $name, $value, %params>
 
 103 All-in-one picker widget for charts. The code was originally copied and adapted
 
 104 from the part picker. The name will be both id and name of the resulting hidden
 
 105 C<id> input field (but the ID can be overwritten with C<$params{id}>).
 
 107 An additional dummy input will be generated which is used to find
 
 108 chart. For a detailed description of its behaviour, see section
 
 109 C<CHART PICKER SPECIFICATION>.
 
 111 For some examples of usage see the test page at controller.pl?action=Chart/test_page
 
 113 C<$value> can be a chart id or a C<Rose::DB:Object> instance.
 
 115 C<%params> can include:
 
 121 If C<%params> contains C<category> only charts of this category will be
 
 122 available for selection (in the autocompletion and in the popup).
 
 124 You may comma separate multiple categories, e.g C<A,Q,L>.
 
 126 In SL::DB::Manager::Chart there is also a filter called C<selected_category>,
 
 127 which filters the possible charts according to the category checkboxes the user
 
 128 selects in the popup window. This filter may further restrict the results of
 
 129 the filter category, but the user is not able to "break out of" the limits
 
 130 defined by C<category>. In fact if the categories are restricted by C<category>
 
 131 the popup template should only show checkboxes for those categories.
 
 135 If C<%params> contains C<type> only charts of this type will be used for
 
 136 autocompletion, i.e. the selection is filtered. You may comma separate multiple
 
 139 Type is usually a filter for link: C<AR,AR_paid>
 
 141 Type can also be a specially defined type: C<guv>, C<balance>, C<bank>
 
 143 See the type filter in SL::DB::Manager::Chart.
 
 147 If C<%params> is passed with choose=1 the input of the filter field in the
 
 148 popup window is cleared. This is useful if a chart was already selected and we
 
 149 want to choose a new chart and immediately see all options.
 
 153 If C<%params> is passed with fat_set_item=1 the contents of the selected chart
 
 154 object (the elements of the database chart table) are passed back via JSON into
 
 155 the item object. There is an example on the test page.
 
 157 Without fat_set_item only the variables id and name (displayable name) are
 
 162 C<chart_picker> will register its javascript for inclusion in the next header
 
 163 rendering. If you write a standard controller that only calls C<render> once, it
 
 164 will just work.  In case the header is generated in a different render call
 
 165 (multiple blocks, ajax, old C<bin/mozilla> style controllers) you need to
 
 166 include C<js/autocomplete_chart.js> yourself.
 
 172 For users that don't regularly do bookkeeping and haven't memorised all the
 
 173 account numbers and names there are some filter options inside the popup window
 
 174 to quickly narrow down the possible matches. You can filter by
 
 178 =item * chart accno or description, inside the input field
 
 180 =item * accounts that have already been booked
 
 182 =item * by category (AIELQC)
 
 184 By selecting category checkboxes the list of available charts can be
 
 185 restricted. If all checkboxes are unchecked all possible charts are shown.
 
 189 There are two views in the list of accounts. By default all possible accounts are shown as a long list.
 
 191 But you can also show more information, in this case the resulting list is automatically paginated:
 
 195 =item * the balance of the account (as determined by SL::DB::Chart get_balance, which checks for user rights)
 
 199 =item * the invoice date of the last transaction (may lie in the future)
 
 203 The partpicker also has two views, but whereas the compact block view of the
 
 204 partpicker allows part boxes to be aligned in two columns, the chartpicker
 
 205 block view still only shows one chart per row, because there is more
 
 206 information and the account names can be quite long. This behaviour is
 
 207 determined by css, however, and could be changed (div.cpc_block).  The downside
 
 208 of this is that you have to scroll the window to reach the pagination controls.
 
 210 The default view of the display logic is to use block view, so we don't have to
 
 211 pass any parameters in the pagination GET. However, the default view for the
 
 212 user is the list view, so the popup window is opened with the "Hide additional
 
 213 information" select box already ticked.
 
 215 =head1 CHART PICKER SPECIFICATION
 
 217 The following list of design goals were applied:
 
 223 Charts should not be perceived by the user as distinct inputs of chart number and
 
 224 description but as a single object
 
 228 Easy to use without documentation for novice users
 
 232 Fast to use with keyboard for experienced users
 
 236 Possible to use without any keyboard interaction for mouse (or touchscreen)
 
 241 Must not leave the current page in event of ambiguity (cf. current select_item
 
 246 Should not require a feedback/check loop in the common case
 
 250 Should not be constrained to exact matches
 
 254 The implementation consists of the following parts which will be referenced later:
 
 260 A hidden input (id input), used to hold the id of the selected part. The only
 
 261 input that gets submitted
 
 265 An input (dummy input) containing a description of the currently selected chart,
 
 266 also used by the user to search for charts
 
 270 A jquery.autocomplete mechanism attached to the dummy field
 
 274 A popup layer for both feedback and input of additional data in case of
 
 279 An internal status of the chart picker, indicating whether id input and dummy
 
 280 input are consistent. After leaving the dummy input the chart picker must
 
 281 place itself in a consistent status.
 
 285 A clickable icon (popup trigger) attached to the dummy input, which triggers the popup layer.
 
 295 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
 
 297 G. Richardson E<lt>information@kivitendo-premium.deE<gt>