__PACKAGE__->make_sorted(
ONLY => [ qw(list) ],
- package_name => $::locale->text('Package name'),
- type => $::locale->text('Execution type'),
- active => $::locale->text('Active'),
- cron_spec => $::locale->text('Execution schedule'),
- last_run_at => $::locale->text('Last run at'),
- next_run_at => $::locale->text('Next run at'),
+ package_name => 'Package name',
+ type => 'Execution type',
+ active => 'Active',
+ cron_spec => 'Execution schedule',
+ last_run_at => 'Last run at',
+ next_run_at => 'Next run at',
);
#
__PACKAGE__->make_sorted(
ONLY => [ qw(list) ],
- package_name => $::locale->text('Package name'),
- run_at => $::locale->text('Run at'),
- status => $::locale->text('Execution status'),
- result => $::locale->text('Result'),
- error => $::locale->text('Error'),
+ package_name => 'Package name',
+ run_at => 'Run at',
+ status => 'Execution status',
+ result => 'Result',
+ error => 'Error',
);
#
use SL::Request qw(flatten);
use SL::MoreCommon qw(uri_encode);
+use Rose::Object::MakeMethods::Generic
+(
+ scalar => [ qw(action_name) ],
+);
+
#
# public/helper functions
#
return $_[0] if (scalar(@_) == 1) && !ref($_[0]);
my %params = ref($_[0]) eq 'HASH' ? %{ $_[0] } : @_;
- my $controller = delete($params{controller}) || $self->_controller_name;
+ my $controller = delete($params{controller}) || $self->controller_name;
my $action = $params{action} || 'dispatch';
my $script;
$file->close;
}
+sub controller_name {
+ my $class = ref($_[0]) || $_[0];
+ $class =~ s/^SL::Controller:://;
+ return $class;
+}
+
#
# Before/after run hooks
#
$::form->error("Invalid action '${action}' for controller " . ref($self)) if !$self->can($sub);
+ $self->action_name($action);
$self->_run_hooks('before', $action);
$self->$sub(@_);
$self->_run_hooks('after', $action);
}
-sub _controller_name {
- my $class = ref($_[0]) || $_[0];
- $class =~ s/^SL::Controller:://;
- return $class;
-}
-
sub _dispatch {
my $self = shift;
my $sub = "action_${action}";
if ($self->can($sub)) {
+ $self->action_name($action);
$self->_run_hooks('before', $action);
$self->$sub(@_);
$self->_run_hooks('after', $action);
The controller to call is given by C<$params{controller}>. It defaults
to the current controller as returned by
-L</_controller_name>.
+L</controller_name>.
The action to call is given by C<$params{action}>. It defaults to
C<dispatch>.
request is routed. Only controllers that handle login requests
themselves should return trueish for this function.
+=item C<controller_name>
+
+Returns the name of the curernt controller package without the
+C<SL::Controller::> prefix. This method can be called both as a class
+method and an instance method.
+
+=item C<action_name>
+
+Returns the name of the currently executing action. If the dispatcher
+mechanism was used then this is not C<dispatch> but the actual method
+name the dispatching resolved to.
+
=back
=head2 PRIVATE FUNCTIONS
=over 4
-=item C<_controller_name>
-
-Returns the name of the curernt controller package without the
-C<SL::Controller::> prefix.
-
=item C<_dispatch>
Implements the method lookup for indirect dispatching mentioned in the
use Clone qw(clone);
use SL::DB::OrderItem;
+use SL::Controller::Helper::GetModels;
+use SL::Controller::Helper::Paginated;
+use SL::Controller::Helper::Sorted;
use SL::Controller::Helper::ParseFilter;
use SL::Controller::Helper::ReportGenerator;
+use Rose::Object::MakeMethods::Generic (
+ scalar => [ qw(db_args flat_filter) ],
+);
+
__PACKAGE__->run_before(sub { $::auth->assert('sales_order_edit'); });
+__PACKAGE__->get_models_url_params('flat_filter');
+__PACKAGE__->make_paginated(
+ MODEL => 'OrderItem',
+ PAGINATE_ARGS => 'db_args',
+ ONLY => [ qw(list) ],
+);
+
+__PACKAGE__->make_sorted(
+ MODEL => 'OrderItem',
+ ONLY => [ qw(list) ],
+
+ DEFAULT_BY => 'reqdate',
+ DEFAULT_DIR => 1,
+
+ reqdate => 'Reqdate',
+ description => 'Description',
+ partnumber => 'Part Number',
+ qty => 'Qty',
+ missing => 'Missing qty',
+ shipped_qty => 'shipped',
+ ordnumber => 'Order',
+ customer => 'Customer',
+);
+
sub action_list {
my ($self) = @_;
my %list_params = (
- sort_by => $::form->{sort_by} || 'reqdate',
- sort_dir => $::form->{sort_dir},
filter => $::form->{filter},
- page => $::form->{page},
);
- my $db_args = $self->setup_for_list(%list_params);
- $self->{pages} = SL::DB::Manager::OrderItem->paginate(%list_params, args => $db_args);
- $self->{flat_filter} = { map { $_->{key} => $_->{value} } $::form->flatten_variables('filter') };
+ $self->db_args($self->setup_for_list(%list_params));
+ $self->flat_filter({ map { $_->{key} => $_->{value} } $::form->flatten_variables('filter') });
$self->make_filter_summary;
my $top = $::form->parse_html_template('delivery_plan/report_top', { FORM => $::form, SELF => $self });
raw_bottom_info_text => $bottom,
controller_class => 'DeliveryPlan',
},
- report_generator_export_options => [
- 'list', qw(filter sort_by sort_dir),
- ],
- db_args => $db_args,
+ report_generator_export_options => [ qw(list filter) ],
);
- $self->{orderitems} = SL::DB::Manager::OrderItem->get_all(%$db_args);
+ $self->{orderitems} = $self->get_models(%{ $self->db_args });
$self->list_objects;
}
with_objects => [ 'order', 'order.customer', 'part' ],
launder_to => $self->{filter},
),
- sort_by => $self->set_sort_params(%params),
- page => $params{page},
);
$args{query} = [ @{ $args{query} || [] },
return \%args;
}
-sub set_sort_params {
- my ($self, %params) = @_;
- my $sort_str;
- ($self->{sort_by}, $self->{sort_dir}, $sort_str) =
- SL::DB::Manager::OrderItem->make_sort_string(%params);
- return $sort_str;
-}
-
sub prepare_report {
my ($self, %params) = @_;
my @sortable = qw(reqdate partnumber description ordnumber customer);
my %column_defs = (
- reqdate => { text => $::locale->text('Reqdate'),
- sub => sub { $_[0]->reqdate_as_date || $_[0]->order->reqdate_as_date }},
- description => { text => $::locale->text('Description'),
- sub => sub { $_[0]->description },
- obj_link => sub { $self->link_to($_[0]->part) }},
- partnumber => { text => $::locale->text('Part Number'),
- sub => sub { $_[0]->part->partnumber },
- obj_link => sub { $self->link_to($_[0]->part) }},
- qty => { text => $::locale->text('Qty'),
- sub => sub { $_[0]->qty_as_number . ' ' . $_[0]->unit }},
- missing => { text => $::locale->text('Missing qty'),
- sub => sub { $::form->format_amount(\%::myconfig, $_[0]->qty - $_[0]->shipped_qty, 2) . ' ' . $_[0]->unit }},
- shipped_qty => { text => $::locale->text('shipped'),
- sub => sub { $::form->format_amount(\%::myconfig, $_[0]->shipped_qty, 2) . ' ' . $_[0]->unit }},
- ordnumber => { text => $::locale->text('Order'),
- sub => sub { $_[0]->order->ordnumber },
- obj_link => sub { $self->link_to($_[0]->order) }},
- customer => { text => $::locale->text('Customer'),
- sub => sub { $_[0]->order->customer->name },
- obj_link => sub { $self->link_to($_[0]->order->customer) }},
+ reqdate => { sub => sub { $_[0]->reqdate_as_date || $_[0]->order->reqdate_as_date }},
+ description => { sub => sub { $_[0]->description },
+ obj_link => sub { $self->link_to($_[0]->part) }},
+ partnumber => { sub => sub { $_[0]->part->partnumber },
+ obj_link => sub { $self->link_to($_[0]->part) }},
+ qty => { sub => sub { $_[0]->qty_as_number . ' ' . $_[0]->unit }},
+ missing => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]->qty - $_[0]->shipped_qty, 2) . ' ' . $_[0]->unit }},
+ shipped_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]->shipped_qty, 2) . ' ' . $_[0]->unit }},
+ ordnumber => { sub => sub { $_[0]->order->ordnumber },
+ obj_link => sub { $self->link_to($_[0]->order) }},
+ customer => { sub => sub { $_[0]->order->customer->name },
+ obj_link => sub { $self->link_to($_[0]->order->customer) }},
);
-
- for my $col (@sortable) {
- $column_defs{$col}{link} = $self->url_for(
- action => 'list',
- sort_by => $col,
- sort_dir => ($self->{sort_by} eq $col ? 1 - $self->{sort_dir} : $self->{sort_dir}),
- page => $self->{pages}{cur},
- %{ $self->{flat_filter} },
- );
- }
+ map { $column_defs{$_}->{text} = $::locale->text( $self->get_sort_spec->{$_}->{title} ) } keys %column_defs;
map { $column_defs{$_}->{visible} = 1 } @visible;
$report->set_columns(%column_defs);
$report->set_column_order(@columns);
$report->set_options(allow_pdf_export => 1, allow_csv_export => 1);
- $report->set_sort_indicator(%params);
$report->set_export_options(@{ $params{report_generator_export_options} || [] });
$report->set_options(
%{ $params{report_generator_options} || {} },
title => $::locale->text('Delivery Plan'),
);
$report->set_options_from_form;
+ $self->set_report_generator_sort_options(report => $report, sortable_columns => \@sortable);
- SL::DB::Manager::OrderItem->disable_paginating(args => $params{db_args}) if $report->{options}{output_format} =~ /^(pdf|csv)$/i;
+ $self->disable_pagination if $report->{options}{output_format} =~ /^(pdf|csv)$/i;
$self->{report_data} = {
column_defs => \%column_defs,
use strict;
use Exporter qw(import);
-our @EXPORT = qw(get_callback get_models);
+our @EXPORT = qw(get_models_url_params get_callback get_models);
use constant PRIV => '__getmodelshelperpriv';
map { push @{ $registered_handlers{$_} }, $additional_handlers{$_} if $additional_handlers{$_} } keys %registered_handlers;
}
+sub get_models_url_params {
+ my ($class, $sub_name_or_code) = @_;
+
+ my $code = (ref($sub_name_or_code) || '') eq 'CODE' ? $sub_name_or_code : sub { shift->$sub_name_or_code(@_) };
+ my $callback = sub {
+ my ($self, %params) = @_;
+ my @additional_params = $code->($self);
+ return (
+ %params,
+ (scalar(@additional_params) == 1) && (ref($additional_params[0]) eq 'HASH') ? %{ $additional_params[0] } : @additional_params,
+ );
+ };
+
+ push @{ $registered_handlers{callback} }, $callback;
+}
+
sub get_callback {
my ($self, %override_params) = @_;
=over 4
+=item C<get_models_url_params $class, $sub>
+
+Register one of the controller's subs to be called whenever an URL has
+to be generated (e.g. for sort and pagination links). This is a way
+for the controller to add additional parameters to the URL (e.g. for
+filter parameters).
+
+The C<$sub> parameter can be either a code reference or the name of
+one of the controller's functions.
+
+The value returned by this C<$sub> must be either a single hash
+reference or a hash of key/value pairs to add to the URL.
+
=item C<register_get_models_handlers $class, %handlers>
This function should only be called from other controller helpers like
use strict;
use Exporter qw(import);
-our @EXPORT = qw(make_paginated get_paginate_spec get_current_paginate_params _save_current_paginate_params _get_models_handler_for_paginated _callback_handler_for_paginated);
+our @EXPORT = qw(make_paginated get_paginate_spec get_current_paginate_params _save_current_paginate_params _get_models_handler_for_paginated _callback_handler_for_paginated disable_pagination);
use constant PRIV => '__paginatedhelper_priv';
sub make_paginated {
my ($class, %specs) = @_;
- $specs{MODEL} ||= $class->_controller_name;
+ $specs{MODEL} ||= $class->controller_name;
$specs{MODEL} =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
$specs{PER_PAGE} ||= "SL::DB::Manager::$specs{MODEL}"->default_objects_per_page;
$specs{FORM_PARAMS} ||= [ qw(page per_page) ];
$specs{ONLY} ||= [];
$specs{ONLY} = [ $specs{ONLY} ] if !ref $specs{ONLY};
+ $specs{ONLY_MAP} = @{ $specs{ONLY} } ? { map { ($_ => 1) } @{ $specs{ONLY} } } : { '__ALL__' => 1 };
$controller_paginate_spec = \%specs;
my $spec = $self->get_paginate_spec;
- my $priv = $self->{PRIV()} || {};
+ my $priv = _priv($self);
$params{page} = $priv->{page} unless defined $params{page};
$params{per_page} = $priv->{per_page} unless defined $params{per_page};
per_page => ($params{per_page} * 1) || $spec->{PER_PAGE},
);
- my $calculated_params = "SL::DB::Manager::$spec->{MODEL}"->paginate(%paginate_params, args => {});
+ my %paginate_args = ref($spec->{PAGINATE_ARGS}) eq 'CODE' ? %{ $spec->{PAGINATE_ARGS}->($self) }
+ : $spec->{PAGINATE_ARGS} ? do { my $sub = $spec->{PAGINATE_ARGS}; %{ $self->$sub() } }
+ : ();
+ my $calculated_params = "SL::DB::Manager::$spec->{MODEL}"->paginate(%paginate_params, args => \%paginate_args);
%paginate_params = (
%paginate_params,
num_pages => $calculated_params->{max},
return %paginate_params;
}
+sub disable_pagination {
+ my ($self) = @_;
+ _priv($self)->{disabled} = 1;
+}
+
#
# private functions
#
sub _save_current_paginate_params {
my ($self) = @_;
+ return if !_is_enabled($self);
+
my $paginate_spec = $self->get_paginate_spec;
$self->{PRIV()} = {
page => $::form->{ $paginate_spec->{FORM_PARAMS}->[0] } || 1,
sub _callback_handler_for_paginated {
my ($self, %params) = @_;
- my $priv = $self->{PRIV()} || {};
+ my $priv = _priv($self);
- if ($priv->{page}) {
+ if (_is_enabled($self) && $priv->{page}) {
my $paginate_spec = $self->get_paginate_spec;
$params{ $paginate_spec->{FORM_PARAMS}->[0] } = $priv->{page};
$params{ $paginate_spec->{FORM_PARAMS}->[1] } = $priv->{per_page} if $priv->{per_page};
sub _get_models_handler_for_paginated {
my ($self, %params) = @_;
- $params{model} ||= $self->get_paginate_spec->{MODEL};
+ my $spec = $self->get_paginate_spec;
+ $params{model} ||= $spec->{MODEL};
- "SL::DB::Manager::$params{model}"->paginate($self->get_current_paginate_params, args => \%params);
+ "SL::DB::Manager::$params{model}"->paginate($self->get_current_paginate_params, args => \%params) if _is_enabled($self);
- # $::lxdebug->dump(0, "GM handler for paginated; params nach modif:", \%params);
+ # $::lxdebug->dump(0, "GM handler for paginated; params nach modif (is_enabled? " . _is_enabled($self) . ")", \%params);
return %params;
}
+sub _priv {
+ my ($self) = @_;
+ $self->{PRIV()} ||= {};
+ return $self->{PRIV()};
+}
+
+sub _is_enabled {
+ my ($self) = @_;
+ return !_priv($self)->{disabled} && ($self->get_paginate_spec->{ONLY_MAP}->{$self->action_name} || $self->get_paginate_spec->{ONLY_MAP}->{'__ALL__'});
+}
+
1;
__END__
C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
C<BackgroundJobHistory>).
+=item * C<PAGINATE_ARGS>
+
+Optional. Either a code reference or the name of function to be called
+on the controller importing this helper.
+
+If this funciton is given then the paginate helper calls it whenever
+it has to count the total number of models for calculating the number
+of pages to display. The function must return a hash reference with
+elements suitable for passing to a Rose model manager's C<get_all>
+function.
+
+This can be used e.g. when filtering is used.
+
=item * C<PER_PAGE>
Optional. An integer: the number of models to return per page.
to L<make_paginated> after normalization (hash reference construction,
applying default parameters etc).
+=item C<disable_pagination>
+
+Disable pagination for the duration of the current action. Can be used
+when using the attribute C<ONLY> to L<make_paginated> does not
+cover all cases.
+
=back
=head1 BUGS
use strict;
+use Carp;
+use List::MoreUtils qw(uniq);
+
use Exporter qw(import);
-our @EXPORT = qw(make_sorted get_sort_spec get_current_sort_params _save_current_sort_params _get_models_handler_for_sorted _callback_handler_for_sorted);
+our @EXPORT = qw(make_sorted get_sort_spec get_current_sort_params set_report_generator_sort_options
+ _save_current_sort_params _get_models_handler_for_sorted _callback_handler_for_sorted);
use constant PRIV => '__sortedhelperpriv';
sub make_sorted {
my ($class, %specs) = @_;
- $specs{MODEL} ||= $class->_controller_name;
+ $specs{MODEL} ||= $class->controller_name;
$specs{MODEL} =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
while (my ($column, $spec) = each %specs) {
return %sort_params;
}
+sub set_report_generator_sort_options {
+ my ($self, %params) = @_;
+
+ $params{$_} or croak("Missing parameter '$_'") for qw(report sortable_columns);
+
+ my %current_sort_params = $self->get_current_sort_params;
+
+ foreach my $col (@{ $params{sortable_columns} }) {
+ $params{report}->{columns}->{$col}->{link} = $self->get_callback(
+ sort_by => $col,
+ sort_dir => ($current_sort_params{by} eq $col ? 1 - $current_sort_params{dir} : $current_sort_params{dir}),
+ );
+ }
+
+ $params{report}->set_sort_indicator($current_sort_params{by}, 1 - $current_sort_params{dir});
+
+ if ($params{report}->{export}) {
+ $params{report}->{export}->{variable_list} = [ uniq(
+ @{ $params{report}->{export}->{variable_list} },
+ @{ $self->get_sort_spec->{FORM_PARAMS} }
+ )];
+ }
+}
+
#
# private functions
#
layout helper's C<sortable_table_header>. Does not have a default
value.
+Note that this string must be the untranslated English version of the
+string. The titles will be translated whenever they're requested.
+
=item * C<model>
Optional. The name of a Rose database model this sort index refers
to L<make_sorted> after normalization (hash reference construction,
applying default parameters etc).
+=item C<set_report_generator_sort_options %params>
+
+This function does three things with an instance of
+L<SL::ReportGenerator>:
+
+=over 4
+
+=item 1. it sets the sort indicator,
+
+=item 2. it sets the the links for those column headers that are
+sortable and
+
+=item 3. it adds the C<FORM_PARAMS> fields to the list of variables in
+the report generator's export options.
+
+=back
+
+The report generator instance must be passed as the parameter
+C<report>. The parameter C<sortable_columns> must be an array
+reference of column names that are sortable.
+
+The report generator instance must already have its columns and export
+options set via calls to its L<SL::ReportGenerator::set_columns> and
+L<SL::ReportGenerator::set_export_options> functions.
+
=back
=head1 BUGS
my $by_spec = $sort_spec->{$by};
my %current_sort_params = $controller->get_current_sort_params;
my ($image, $new_dir) = ('', $current_sort_params{dir});
- my $title = delete($params{title}) || $by_spec->{title};
+ my $title = delete($params{title}) || $::locale->text($by_spec->{title});
if ($current_sort_params{by} eq $by) {
my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
-[% SET report_bottom_url_args = {} %]
-[% report_bottom_url_args.import(SELF.flat_filter) %]
-[% report_bottom_url_args.import({action='list', sort_dir=SELF.sort_dir, sort_by=SELF.sort_by}) %]
-[% PROCESS 'common/paginate.html' pages=SELF.pages, base_url=SELF.url_for(report_bottom_url_args) %]
+[% USE L %]
+[%- L.paginate_controls %]