S:C:H:ReportGenerator: Interface und Impmentierung von Kontroll-Zeilen
authorBernd Bleßmann <bernd@kivitendo-premium.de>
Fri, 7 May 2021 19:34:11 +0000 (21:34 +0200)
committerBernd Bleßmann <bernd@kivitendo-premium.de>
Wed, 12 May 2021 15:56:30 +0000 (17:56 +0200)
Es wurde eine Schnittstelle geschaffen, um Kontroll-Zeilen an den
ReportGenerator-Helfer zu übergeben.
Umgesetzt sind Kontroll-Zeilen für einen Separator und für eigene Daten.

SL/Controller/Helper/ReportGenerator.pm
SL/Controller/Helper/ReportGenerator/ControlRow.pm [new file with mode: 0644]
SL/Controller/Helper/ReportGenerator/ControlRow/ALL.pm [new file with mode: 0644]
SL/Controller/Helper/ReportGenerator/ControlRow/Base.pm [new file with mode: 0644]
SL/Controller/Helper/ReportGenerator/ControlRow/Data.pm [new file with mode: 0644]
SL/Controller/Helper/ReportGenerator/ControlRow/Separator.pm [new file with mode: 0644]
SL/Controller/Helper/ReportGenerator/ControlRow/SimpleData.pm [new file with mode: 0644]

index 513eda8..be9b830 100644 (file)
@@ -4,6 +4,7 @@ use strict;
 
 use Carp;
 use List::Util qw(max);
+use Scalar::Util qw(blessed);
 
 use SL::Common;
 use SL::MoreCommon;
@@ -126,16 +127,24 @@ sub report_generator_list_objects {
   my @columns     = $params{report}->get_visible_columns('HTML');
 
   for my $obj (@{ $params{objects} || [] }) {
-    my %data = map {
-      my $def = $column_defs->{$_};
-      my $tmp;
-      $tmp->{raw_data} = $def->{raw_data} ? $def->{raw_data}->($obj) : '';
-      $tmp->{data}     = $def->{sub}      ? $def->{sub}->($obj)
-                       : $obj->can($_)    ? $obj->$_
-                       :                    $obj->{$_};
-      $tmp->{link}     = $def->{obj_link} ? $def->{obj_link}->($obj) : '';
-      $_ => $tmp;
-    } @columns;
+    my %data;
+
+    if (blessed($obj) && $obj->isa('SL::Controller::Helper::ReportGenerator::ControlRow::Base')) {
+      $obj->set_data($params{report});
+      next;
+
+    } else {
+      %data = map {
+        my $def = $column_defs->{$_};
+        my $tmp;
+        $tmp->{raw_data} = $def->{raw_data} ? $def->{raw_data}->($obj) : '';
+        $tmp->{data}     = $def->{sub}      ? $def->{sub}->($obj)
+                         : $obj->can($_)    ? $obj->$_
+                         :                    $obj->{$_};
+        $tmp->{link}     = $def->{obj_link} ? $def->{obj_link}->($obj) : '';
+        $_ => $tmp;
+      } @columns;
+    }
 
     $params{data_callback}->(\%data) if $params{data_callback};
 
@@ -251,6 +260,14 @@ already (column definitions, title, sort handling etc).
 
 Mandatory. An array reference of RDBO models to output.
 
+An element of the array can also be an instance of a control row, i.e.
+an instance of a class derived from
+C<SL::Controller::Helper::ReportGenerator::ControlRow::Base>.
+See also:
+
+L<SL::Controller::Helper::ReportGenerator::ControlRow>
+L<SL::Controller::Helper::ReportGenerator::ControlRow::*>
+
 =item C<data_callback>
 
 Optional. A callback handler (code reference) that gets called for
diff --git a/SL/Controller/Helper/ReportGenerator/ControlRow.pm b/SL/Controller/Helper/ReportGenerator/ControlRow.pm
new file mode 100644 (file)
index 0000000..536dd34
--- /dev/null
@@ -0,0 +1,103 @@
+package SL::Controller::Helper::ReportGenerator::ControlRow;
+
+use strict;
+use Carp;
+
+use SL::Controller::Helper::ReportGenerator::ControlRow::ALL;
+
+use Exporter 'import';
+our @EXPORT = qw(
+  make_control_row
+);
+
+
+sub make_control_row {
+  my ($type, %args) = @_;
+
+  my $class  = $SL::Controller::Helper::ReportGenerator::ControlRow::ALL::type_to_class{$type} // croak "unknown type $type";
+  my $obj    = $class->new(params => \%args);
+  my @errors = $obj->validate_params;
+  croak join("\n", @errors) if @errors;
+
+  return $obj;
+}
+
+
+1;
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Controller::Helper::ReportGenerator::ControlRow - an interface for
+report generator control rows
+
+=head1 DESCRIPTION
+
+ControlRow is an interface that allows generic control rows to be added
+to objects for the C<SL::Controller::Helper::ReportGenerator>.
+
+Each control row implementation can access the report and add data for a row.
+
+=head1 SYNOPSIS
+
+  package SL::Controller::TimeRecording;
+
+  use SL::Controller::Helper::ReportGenerator;
+  use SL::Controller::Helper::ReportGenerator::ControlRow qw(make_control_row);
+
+  sub action_list {
+    my ($self) = @_;
+
+    # Set up the report generator instance. In this example this is
+    # hidden in "prepare_report".
+    my $report = $self->prepare_report;
+
+    # Get objects from database.
+    my $objects = SL::DB::Manager::TimeRecording->get_all(...);
+
+    # Add a separator
+    push @$objects, make_control_row("separator");
+
+    # And a simple total
+    my $total = sum0 map { _round_total($_->duration_in_hours) } @$objects;
+    push @$objects, make_control_row("simple_data", data => {duration => $total});
+
+    # Let report generator create the output.
+    $self->report_generator_list_objects(
+      report  => $report,
+      objects => $objects,
+    );
+  }
+
+
+=head1 WRITING OWN CONTROL ROW CLASSES
+
+See C<SL::Controller::Helper::ReportGenerator::ControlRow::Base>.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<make_control_row TYPE %PARAMS>
+
+Returns an instance of the control row class for the given type. This
+object can be used as an element of objects to the report generator helper
+(see C<SL::Controller::Helper::ReportGenerator>).
+
+Available types are 'separator', 'data, 'simple_data' for now.
+
+C<%PARAMS> depends on the type. See also:
+
+L<SL::Controller::Helper::ReportGenerator::ControlRow::ALL>
+L<SL::Controller::Helper::ReportGenerator::ControlRow::*>
+
+=back
+
+=head1 AUTHOR
+
+Bernd Bleßmann E<lt>bernd@kivitendo-premium.deE<gt>
+
+=cut
diff --git a/SL/Controller/Helper/ReportGenerator/ControlRow/ALL.pm b/SL/Controller/Helper/ReportGenerator/ControlRow/ALL.pm
new file mode 100644 (file)
index 0000000..6ac367a
--- /dev/null
@@ -0,0 +1,15 @@
+package SL::Controller::Helper::ReportGenerator::ControlRow::ALL;
+
+use strict;
+
+use SL::Controller::Helper::ReportGenerator::ControlRow::Data;
+use SL::Controller::Helper::ReportGenerator::ControlRow::Separator;
+use SL::Controller::Helper::ReportGenerator::ControlRow::SimpleData;
+
+our %type_to_class = (
+  data        => 'SL::Controller::Helper::ReportGenerator::ControlRow::Data',
+  separator   => 'SL::Controller::Helper::ReportGenerator::ControlRow::Separator',
+  simple_data => 'SL::Controller::Helper::ReportGenerator::ControlRow::SimpleData',
+);
+
+1;
diff --git a/SL/Controller/Helper/ReportGenerator/ControlRow/Base.pm b/SL/Controller/Helper/ReportGenerator/ControlRow/Base.pm
new file mode 100644 (file)
index 0000000..ea11e97
--- /dev/null
@@ -0,0 +1,98 @@
+package SL::Controller::Helper::ReportGenerator::ControlRow::Base;
+
+use strict;
+
+use parent qw(SL::DB::Object);
+
+use Rose::Object::MakeMethods::Generic (
+  scalar => [ qw(params) ],
+);
+
+
+sub validate_params { die 'name needs to be implemented' }
+sub set_data        { die 'name needs to be implemented' }
+
+
+1;
+
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Controller::Helper::ReportGenerator::ControlRow::Base - a base class
+for report generator control row classes
+
+=head1 DESCRIPTION
+
+ControlRow is an interface that allows generic control rows to be added
+to objects for the C<SL::Controller::Helper::ReportGenerator>. This is a
+base class from which all control row classes are derived.
+
+=head1 SYNOPSIS
+
+Adding your own new control row of the type "only_dashes":
+
+  package SL::Controller::Helper::ReportGenerator::ControlRow::OnlyDashes;
+
+  use parent qw(SL::Controller::Helper::ReportGenerator::ControlRow::Base);
+
+  sub validate_params { return; } # no params
+
+  sub set_data {
+    my ($self, $report) = @_;
+
+    my %data = map { $_ => {data => '---'} } keys %{ $report->{columns} };
+
+    $report->add_data(\%data);
+  }
+
+After that, you have to register your new class in
+C<SL::Controller::Helper::ReportGenerator::ControlRow::ALL>:
+
+  use SL::Controller::Helper::ReportGenerator::ControlRow::OnlyDashes;
+
+  our %type_to_class = (
+    ...,
+    only_dashes => 'SL::Controller::Helper::ReportGenerator::ControlRow::OnlyDashes',
+  );
+
+
+=head1 WRITING OWN CONTROL ROW CLASSES
+
+You can use C<SL::Controller::Helper::ReportGenerator::ControlRow::Base>
+as parent of your module. You have to provide two methods:
+
+=over 4
+
+=item C<validate_params>
+
+This method is used to validate any params used for your module.
+You can access the params through the method C<params> which contains all
+remaining params after the type of the call to make_control_row (see
+C<SL::Controller::Helper::ReportGenerator::ControlRow>).
+
+The method should return an array of error messages if there are any
+errors. Otherwise it should return C<undef>.
+
+=item C<set_data REPORT>
+
+This method sould set the data for the report generator, which is handeled
+over as argument.
+
+=back
+
+=head1 REGISTERING OWN CONTROL ROW CLASSES
+
+See C<SL::Controller::Helper::ReportGenerator::ControlRow::ALL>. Here your
+class should be included with C<use> and entered in the map C<%type_to_class>
+with an appropiate name for it's type.
+
+
+=head1 AUTHOR
+
+Bernd Bleßmann E<lt>bernd@kivitendo-premium.deE<gt>
+
+=cut
diff --git a/SL/Controller/Helper/ReportGenerator/ControlRow/Data.pm b/SL/Controller/Helper/ReportGenerator/ControlRow/Data.pm
new file mode 100644 (file)
index 0000000..159249d
--- /dev/null
@@ -0,0 +1,94 @@
+package SL::Controller::Helper::ReportGenerator::ControlRow::Data;
+
+use strict;
+
+use parent qw(SL::Controller::Helper::ReportGenerator::ControlRow::Base);
+
+
+sub validate_params {
+  my ($self) = @_;
+
+  my @errors;
+  push @errors, 'type "data" needs a parameter "row" as hash ref' if !$self->params->{row} || ('HASH' ne ref $self->params->{row});
+
+  return @errors;;
+}
+
+sub set_data {
+  my ($self, $report) = @_;
+
+  my %data;
+  %data = map {
+    my $def = $self->params->{row}->{$_};
+    my $tmp;
+
+    foreach my $attr (qw(raw_data data link class align)) {
+      $tmp->{$attr} = $def->{$attr} if defined $def->{$attr};
+    }
+    $_ => $tmp;
+  } keys %{ $self->params->{row} };
+
+  $report->add_data(\%data);
+}
+
+
+1;
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Controller::Helper::ReportGenerator::ControlRow::Data - an
+implementaion of a control row class to display data
+
+=head1 DESCRIPTION
+
+This class implements a control row for the report generator helper to display
+data. You can configure the way the data is displayed.
+
+=head1 SYNOPSIS
+
+  use SL::Controller::Helper::ReportGenerator;
+  use SL::Controller::Helper::ReportGenerator::ControlRow qw(make_control_row);
+
+  sub action_list {
+    my ($self) = @_;
+
+    # Set up the report generator instance. In this example this is
+    # hidden in "prepare_report".
+    my $report = $self->prepare_report;
+
+    # Get objects from database.
+    my $objects = SL::DB::Manager::TimeRecording->get_all(...);
+
+    # Add a simple data
+    my $total = $self->get_total($objects);
+    push @$objects, make_control_row(
+      "data",
+      row => { duration => { data  => $total,
+                             class => 'listtotal',
+                             link  => '#info_for_total' } }
+    );
+
+    # Let report generator create the output.
+    $self->report_generator_list_objects(
+      report  => $report,
+      objects => $objects,
+    );
+  }
+
+=head1 PARAMETERS
+
+This control row gets the paramter C<row>, which must a hash ref.
+The keys are the column names for the fields you want to show your
+data. The values are hash refs itself and can contain the keys
+C<raw_data>, C<data>, C<link>, C<class> and C<align> which are passed
+in the data added to the report.
+
+=head1 AUTHOR
+
+Bernd Bleßmann E<lt>bernd@kivitendo-premium.deE<gt>
+
+=cut
diff --git a/SL/Controller/Helper/ReportGenerator/ControlRow/Separator.pm b/SL/Controller/Helper/ReportGenerator/ControlRow/Separator.pm
new file mode 100644 (file)
index 0000000..a7e7bf9
--- /dev/null
@@ -0,0 +1,69 @@
+package SL::Controller::Helper::ReportGenerator::ControlRow::Separator;
+
+use strict;
+
+use parent qw(SL::Controller::Helper::ReportGenerator::ControlRow::Base);
+
+
+sub validate_params {
+  return;
+}
+
+sub set_data {
+  my ($self, $report) = @_;
+
+  $report->add_separator();
+}
+
+
+1;
+
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Controller::Helper::ReportGenerator::ControlRow::Separator - an
+implementaion of a control row class to display a separator
+
+=head1 DESCRIPTION
+
+This class implements a control row for the report generator helper to display
+a separator.
+
+=head1 SYNOPSIS
+
+  use SL::Controller::Helper::ReportGenerator;
+  use SL::Controller::Helper::ReportGenerator::ControlRow qw(make_control_row);
+
+  sub action_list {
+    my ($self) = @_;
+
+    # Set up the report generator instance. In this example this is
+    # hidden in "prepare_report".
+    my $report = $self->prepare_report;
+
+    # Get objects from database.
+    my $objects = SL::DB::Manager::TimeRecording->get_all(...);
+
+    # Add a separator
+    push @$objects, make_control_row("separator");
+
+    # Let report generator create the output.
+    $self->report_generator_list_objects(
+      report  => $report,
+      objects => $objects,
+    );
+  }
+
+=head1 PARAMETERS
+
+This control row does not use any parameters.
+
+=head1 AUTHOR
+
+Bernd Bleßmann E<lt>bernd@kivitendo-premium.deE<gt>
+
+=cut
diff --git a/SL/Controller/Helper/ReportGenerator/ControlRow/SimpleData.pm b/SL/Controller/Helper/ReportGenerator/ControlRow/SimpleData.pm
new file mode 100644 (file)
index 0000000..7d625d3
--- /dev/null
@@ -0,0 +1,86 @@
+package SL::Controller::Helper::ReportGenerator::ControlRow::SimpleData;
+
+use strict;
+
+use parent qw(SL::Controller::Helper::ReportGenerator::ControlRow::Base);
+
+
+sub validate_params {
+  my ($self) = @_;
+
+  my @errors;
+  push @errors, 'type "simple_data" needs a parameter "data" as hash ref' if !$self->params->{data} || ('HASH' ne ref $self->params->{data});
+
+  return @errors;
+}
+
+sub set_data {
+  my ($self, $report) = @_;
+
+  my %data = map {
+    my $tmp;
+    $tmp->{data} = $self->params->{data}->{$_};
+    $_ => $tmp;
+  } keys %{ $self->params->{data} };
+
+  $report->add_data(\%data);
+}
+
+
+1;
+
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Controller::Helper::ReportGenerator::ControlRow::SimpleData - an
+implementaion of a control row class to display simple data
+
+=head1 DESCRIPTION
+
+This class implements a control row for the report generator helper to display
+simple data. C<Simple> because you only have to provide the column and your data
+as a string.
+
+=head1 SYNOPSIS
+
+  use SL::Controller::Helper::ReportGenerator;
+  use SL::Controller::Helper::ReportGenerator::ControlRow qw(make_control_row);
+
+  sub action_list {
+    my ($self) = @_;
+
+    # Set up the report generator instance. In this example this is
+    # hidden in "prepare_report".
+    my $report = $self->prepare_report;
+
+    # Get objects from database.
+    my $objects = SL::DB::Manager::TimeRecording->get_all(...);
+
+    # Add a simple data
+    push @$objects, make_control_row(
+      "simple_data",
+      data => { duration => 'Total sum of duration is not implemeted yet' }
+    );
+
+    # Let report generator create the output.
+    $self->report_generator_list_objects(
+      report  => $report,
+      objects => $objects,
+    );
+  }
+
+=head1 PARAMETERS
+
+This control row gets the paramter C<data>, which must a hash ref.
+The keys are the column names for the fields you want to show your
+data. The values are the data.
+
+=head1 AUTHOR
+
+Bernd Bleßmann E<lt>bernd@kivitendo-premium.deE<gt>
+
+=cut