Presenter-Module für Listen von Verkaufs-/Einkaufsobjekte
authorMoritz Bunkus <m.bunkus@linet-services.de>
Tue, 29 Jan 2013 10:27:43 +0000 (11:27 +0100)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Fri, 1 Feb 2013 12:35:39 +0000 (13:35 +0100)
SL/Presenter/CustomerVendor.pm [new file with mode: 0644]
SL/Presenter/DeliveryOrder.pm [new file with mode: 0644]
SL/Presenter/Invoice.pm [new file with mode: 0644]
SL/Presenter/Order.pm [new file with mode: 0644]
SL/Presenter/Project.pm [new file with mode: 0644]
SL/Presenter/Record.pm [new file with mode: 0644]
locale/de/all
templates/webpages/presenter/record/empty_record_list.html [new file with mode: 0644]
templates/webpages/presenter/record/record_list.html [new file with mode: 0644]

diff --git a/SL/Presenter/CustomerVendor.pm b/SL/Presenter/CustomerVendor.pm
new file mode 100644 (file)
index 0000000..077c376
--- /dev/null
@@ -0,0 +1,118 @@
+package SL::Presenter::CustomerVendor;
+
+use strict;
+
+use parent qw(Exporter);
+
+use Exporter qw(import);
+our @EXPORT = qw(customer vendor);
+
+use Carp;
+
+sub customer {
+  my ($self, $customer, $type, %params) = @_;
+  return _customer_vendor($self, $customer, 'customer', %params);
+}
+
+sub vendor {
+  my ($self, $vendor, $type, %params) = @_;
+  return _customer_vendor($self, $vendor, 'vendor', %params);
+}
+
+sub _customer_vendor {
+  my ($self, $cv, $type, %params) = @_;
+
+  $params{display} ||= 'inline';
+
+  croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
+
+  my $text = join '', (
+    $params{no_link} ? '' : '<a href="ct.pl?action=edit&amp;db=' . $type . '&amp;id=' . $self->escape($cv->id) . '">',
+    $self->escape($cv->name),
+    $params{no_link} ? '' : '</a>',
+  );
+  return $self->escaped_text($text);
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Presenter::CustomerVendor - Presenter module for customer and
+vendor Rose::DB objects
+
+=head1 SYNOPSIS
+
+  # Customers:
+  my $customer = SL::DB::Manager::Customer->get_first;
+  my $html     = SL::Presenter->get->customer($customer, display => 'inline');
+
+  # Vendors:
+  my $vendor = SL::DB::Manager::Vendor->get_first;
+  my $html   = SL::Presenter->get->vendor($customer, display => 'inline');
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<customer $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the customer object C<$object>.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the customer's name linked
+to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the customer's name will be linked to
+the "edit customer" dialog from the master data menu.
+
+=back
+
+=item C<vendor $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the vendor object C<$object>.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the vendor's name linked
+to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the vendor's name will be linked to
+the "edit vendor" dialog from the master data menu.
+
+=back
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
diff --git a/SL/Presenter/DeliveryOrder.pm b/SL/Presenter/DeliveryOrder.pm
new file mode 100644 (file)
index 0000000..1abc835
--- /dev/null
@@ -0,0 +1,122 @@
+package SL::Presenter::DeliveryOrder;
+
+use strict;
+
+use parent qw(Exporter);
+
+use Exporter qw(import);
+our @EXPORT = qw(sales_delivery_order purchase_delivery_order);
+
+use Carp;
+
+sub sales_delivery_order {
+  my ($self, $delivery_order, %params) = @_;
+
+  return _do_record($self, $delivery_order, 'sales_delivery_order', %params);
+}
+
+sub purchase_delivery_order {
+  my ($self, $delivery_order, %params) = @_;
+
+  return _do_record($self, $delivery_order, 'purchase_delivery_order', %params);
+}
+
+sub _do_record {
+  my ($self, $delivery_order, $type, %params) = @_;
+
+  $params{display} ||= 'inline';
+
+  croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
+
+  my $text = join '', (
+    $params{no_link} ? '' : '<a href="do.pl?action=edit&amp;type=' . $type . '&amp;id=' . $self->escape($delivery_order->id) . '">',
+    $self->escape($delivery_order->donumber),
+    $params{no_link} ? '' : '</a>',
+  );
+  return $self->escaped_text($text);
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Presenter::DeliveryOrder - Presenter module for Rose::DB objects
+for sales and purchase delivery orders
+
+=head1 SYNOPSIS
+
+  # Sales delivery orders:
+  my $object = SL::DB::Manager::DeliveryOrder->get_first(where => [ is_sales => 1 ]);
+  my $html   = SL::Presenter->get->sales_delivery_order($object, display => 'inline');
+
+  # Purchase delivery orders:
+  my $object = SL::DB::Manager::DeliveryOrder->get_first(where => [ or => [ is_sales => undef, is_sales => 0 ]]);
+  my $html   = SL::Presenter->get->purchase_delivery_order($object, display => 'inline');
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<sales_delivery_order $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the sales delivery order object
+C<$object>.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the objects's delivery
+order number linked to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the delivery order number will be linked
+to the "edit delivery order" dialog from the sales menu.
+
+=back
+
+=item C<purchase_delivery_order $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the purchase delivery order object
+C<$object>.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the objects's delivery
+order number linked to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the delivery order number will be linked
+to the "edit delivery order" dialog from the purchase menu.
+
+=back
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
diff --git a/SL/Presenter/Invoice.pm b/SL/Presenter/Invoice.pm
new file mode 100644 (file)
index 0000000..f1aaf7f
--- /dev/null
@@ -0,0 +1,188 @@
+package SL::Presenter::Invoice;
+
+use strict;
+
+use parent qw(Exporter);
+
+use Exporter qw(import);
+our @EXPORT = qw(sales_invoice ar_transaction purchase_invoice ap_transaction);
+
+use Carp;
+
+sub sales_invoice {
+  my ($self, $invoice, %params) = @_;
+
+  return _is_ir_record($self, $invoice, 'is', %params);
+}
+
+sub ar_transaction {
+  my ($self, $invoice, %params) = @_;
+
+  return _is_ir_record($self, $invoice, 'ar', %params);
+}
+
+sub purchase_invoice {
+  my ($self, $invoice, %params) = @_;
+
+  return _is_ir_record($self, $invoice, 'is', %params);
+}
+
+sub ap_transaction {
+  my ($self, $invoice, %params) = @_;
+
+  return _is_ir_record($self, $invoice, 'ap', %params);
+}
+
+sub _is_ir_record {
+  my ($self, $invoice, $controller, %params) = @_;
+
+  $params{display} ||= 'inline';
+
+  croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
+
+  my $text = join '', (
+    $params{no_link} ? '' : '<a href="' . $controller . '.pl?action=edit&amp;type=invoice&amp;id=' . $self->escape($invoice->id) . '">',
+    $self->escape($invoice->invnumber),
+    $params{no_link} ? '' : '</a>',
+  );
+  return $self->escaped_text($text);
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Presenter::Invoice - Presenter module for sales invoice, AR
+transaction, purchase invoice and AP transaction Rose::DB objects
+
+=head1 SYNOPSIS
+
+  # Sales invoices:
+  my $object = SL::DB::Manager::Invoice->get_first(where => [ invoice => 1 ]);
+  my $html   = SL::Presenter->get->sales_invoice($object, display => 'inline');
+
+  # AR transactions:
+  my $object = SL::DB::Manager::Invoice->get_first(where => [ or => [ invoice => undef, invoice => 0 ]]);
+  my $html   = SL::Presenter->get->ar_transaction($object, display => 'inline');
+
+  # Purchase invoices:
+  my $object = SL::DB::Manager::PurchaseInvoice->get_first(where => [ invoice => 1 ]);
+  my $html   = SL::Presenter->get->purchase_invoice($object, display => 'inline');
+
+  # AP transactions:
+  my $object = SL::DB::Manager::PurchaseInvoice->get_first(where => [ or => [ invoice => undef, invoice => 0 ]]);
+  my $html   = SL::Presenter->get->ar_transaction($object, display => 'inline');
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<sales_invoice $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the sales invoice object C<$object>
+.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the invoice number linked
+to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the invoice number will be linked to the
+"edit invoice" dialog from the sales menu.
+
+=back
+
+=item C<ar_transaction $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the AR transaction object C<$object>
+.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the invoice number linked
+to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the invoice number will be linked to the
+"edit invoice" dialog from the general ledger menu.
+
+=back
+
+=item C<purchase_invoice $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the purchase invoice object
+C<$object>.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the invoice number name
+linked to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the invoice number will be linked to
+the "edit invoice" dialog from the purchase menu.
+
+=back
+
+=item C<ap_transaction $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the AP transaction object C<$object>
+.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the invoice number linked
+to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the invoice number will be linked to the
+"edit invoice" dialog from the general ledger menu.
+
+=back
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
diff --git a/SL/Presenter/Order.pm b/SL/Presenter/Order.pm
new file mode 100644 (file)
index 0000000..19d8483
--- /dev/null
@@ -0,0 +1,190 @@
+package SL::Presenter::Order;
+
+use strict;
+
+use parent qw(Exporter);
+
+use Exporter qw(import);
+our @EXPORT = qw(sales_quotation sales_order request_quotation purchase_order);
+
+use Carp;
+
+sub sales_quotation {
+  my ($self, $order, %params) = @_;
+
+  return _oe_record($self, $order, 'sales_quotation', %params);
+}
+
+sub sales_order {
+  my ($self, $order, %params) = @_;
+
+  return _oe_record($self, $order, 'sales_order', %params);
+}
+
+sub request_quotation {
+  my ($self, $order, %params) = @_;
+
+  return _oe_record($self, $order, 'request_quotation', %params);
+}
+
+sub purchase_order {
+  my ($self, $order, %params) = @_;
+
+  return _oe_record($self, $order, 'purchase_order', %params);
+}
+
+sub _oe_record {
+  my ($self, $order, $type, %params) = @_;
+
+  $params{display} ||= 'inline';
+
+  croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
+
+  my $number_method = $order->quotation ? 'quonumber' : 'ordnumber';
+
+  my $text = join '', (
+    $params{no_link} ? '' : '<a href="oe.pl?action=edit&amp;type=' . $type . '&amp;id=' . $self->escape($order->id) . '">',
+    $self->escape($order->$number_method),
+    $params{no_link} ? '' : '</a>',
+  );
+  return $self->escaped_text($text);
+}
+
+1;
+
+
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Presenter::Order - Presenter module for Rose::DB objects for sales
+quotations, sales orders, requests for quotations and purchase orders
+
+=head1 SYNOPSIS
+
+  # Sales quotations:
+  my $object = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('sales_quotation') ]);
+  my $html   = SL::Presenter->get->sales_quotation($object, display => 'inline');
+
+  # Sales orders:
+  my $object = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('sales_order') ]);
+  my $html   = SL::Presenter->get->sales_order($object, display => 'inline');
+
+  # Requests for quotations:
+  my $object = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('request_quotation') ]);
+  my $html   = SL::Presenter->get->request_quotation($object, display => 'inline');
+
+  # Purchase orders:
+  my $object = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('purchase_order') ]);
+  my $html   = SL::Presenter->get->purchase_order($object, display => 'inline');
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<sales_quotation $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the sales quotation object
+C<$object>.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the objects's
+quotation number linked to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the order number will be linked to the
+"edit quotation" dialog from the sales menu.
+
+=back
+
+=item C<sales_order $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the sales order object C<$object>.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the objects's
+order number linked to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the  order number will be linked
+to the "edit order" dialog from the sales menu.
+
+=back
+
+=item C<request_quotation $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the request for quotation object
+C<$object>.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the objects's
+quotation number linked to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the order number will be linked to the
+"edit request for quotation" dialog from the purchase menu.
+
+=back
+
+=item C<purchase_order $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the purchase order object
+C<$object>.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the objects's
+order number linked to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the  order number will be linked
+to the "edit order" dialog from the purchase menu.
+
+=back
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
diff --git a/SL/Presenter/Project.pm b/SL/Presenter/Project.pm
new file mode 100644 (file)
index 0000000..650995c
--- /dev/null
@@ -0,0 +1,123 @@
+package SL::Presenter::Project;
+
+use strict;
+
+use parent qw(Exporter);
+
+use Exporter qw(import);
+our @EXPORT = qw(project);
+
+use Carp;
+
+sub project {
+  my ($self, $project, %params) = @_;
+
+  return '' unless $project;
+
+  $params{display} ||= 'inline';
+
+  croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
+
+  $params{style} ||= 'both';
+  my $description;
+
+  if ($params{style} =~ m/number/) {
+    $description = $project->projectnumber;
+
+  } elsif ($params{style} =~ m/description/) {
+    $description = $project->description;
+
+  } else {
+    $description = $project->projectnumber;
+    if ($project->description && do { my $desc = quotemeta $project->description; $project->projectnumber !~ m/$desc/ }) {
+      $description .= ' (' . $project->description . ')';
+    }
+  }
+
+  my $text = join '', (
+    $params{no_link} ? '' : '<a href="controller.pl?action=Project/edit&amp;id=' . $self->escape($project->id) . '">',
+    $self->escape($description),
+    $params{no_link} ? '' : '</a>',
+  );
+  return $self->escaped_text($text);
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Presenter::Project - Presenter module for project Rose::DB objects
+
+=head1 SYNOPSIS
+
+  my $project = SL::DB::Manager::Project->get_first;
+  my $html    = SL::Presenter->get->project($project, display => 'inline');
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<project $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the project object C<$customer>.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the project's description
+(controlled by the C<style> parameter) linked to the corresponding
+'edit' action.
+
+=item * style
+
+Determines what exactly will be output. Can be one of the values with
+C<both> being the default if it is missing:
+
+=over 2
+
+=item C<projectnumber> (or simply C<number>)
+
+Outputs only the project's number.
+
+=item C<projectdescription> (or simply C<description>)
+
+Outputs only the project's description.
+
+=item C<both>
+
+Outputs the project's number followed by its description in
+parenthesis (e.g. "12345 (Secret Combinations)"). If the project's
+description is already part of the project's number then it will not
+be appended.
+
+=back
+
+=item * no_link
+
+If falsish (the default) then the project's description will be linked to
+the "edit project" dialog from the master data menu.
+
+=back
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
diff --git a/SL/Presenter/Record.pm b/SL/Presenter/Record.pm
new file mode 100644 (file)
index 0000000..a9c6dcd
--- /dev/null
@@ -0,0 +1,456 @@
+package SL::Presenter::Record;
+
+use strict;
+
+use parent qw(Exporter);
+
+use Exporter qw(import);
+our @EXPORT = qw(grouped_record_list empty_record_list record_list);
+
+use Carp;
+use List::Util qw(first);
+
+sub grouped_record_list {
+  my ($self, $list, %params) = @_;
+
+  my %groups = _group_records($list);
+  my $output = '';
+
+  $output .= _sales_quotation_list(        $self, $groups{sales_quotations})         if $groups{sales_quotations};
+  $output .= _sales_order_list(            $self, $groups{sales_orders})             if $groups{sales_orders};
+  $output .= _sales_delivery_order_list(   $self, $groups{sales_delivery_orders})    if $groups{sales_delivery_orders};
+  $output .= _sales_invoice_list(          $self, $groups{sales_invoices})           if $groups{sales_invoices};
+  $output .= _ar_transaction_list(         $self, $groups{ar_transactions})          if $groups{ar_transactions};
+
+  $output .= _request_quotation_list(      $self, $groups{purchase_quotations})      if $groups{purchase_quotations};
+  $output .= _purchase_order_list(         $self, $groups{purchase_orders})          if $groups{purchase_orders};
+  $output .= _purchase_delivery_order_list($self, $groups{purchase_delivery_orders}) if $groups{purchase_delivery_orders};
+  $output .= _purchase_invoice_list(       $self, $groups{purchase_invoices})        if $groups{purchase_invoices};
+  $output .= _ar_transaction_list(         $self, $groups{ar_transactions})          if $groups{ar_transactions};
+
+  return $output || $self->empty_record_list;
+}
+
+sub empty_record_list {
+  my ($self) = @_;
+  return $self->render('presenter/record/empty_record_list');
+}
+
+sub record_list {
+  my ($self, $list, %params) = @_;
+
+  my @columns;
+
+  if (ref($params{columns}) eq 'ARRAY') {
+    @columns = map {
+      if (ref($_) eq 'ARRAY') {
+        { title => $_->[0], data => $_->[1], link => $_->[2] }
+      } else {
+        $_;
+      }
+    } @{ delete $params{columns} };
+
+  } else {
+    croak "Wrong type for 'columns' argument: not an array reference";
+  }
+
+  my %column_meta   = map { $_->name => $_ } @{ $list->[0]->meta->columns       };
+  my %relationships = map { $_->name => $_ } @{ $list->[0]->meta->relationships };
+
+  my $call = sub {
+    my ($obj, $method, @args) = @_;
+    $obj->$method(@args);
+  };
+
+  my @data;
+  foreach my $obj (@{ $list }) {
+    my @row;
+
+    foreach my $spec (@columns) {
+      my %cell;
+
+      my $method       =  $spec->{column} || $spec->{data};
+      my $meta         =  $column_meta{ $spec->{data} };
+      my $type         =  lc ref $meta;
+      $type            =~ s/.*:://;
+      my $relationship =  $relationships{ $spec->{data} };
+      my $rel_type     =  !$relationship ? '' : lc $relationship->class;
+      $rel_type        =~ s/.*:://;
+
+      if (ref($spec->{data}) eq 'CODE') {
+        $cell{value} = $spec->{data}->($obj);
+
+      } else {
+        $cell{value} = $rel_type eq 'customer'        ? $self->customer($obj->$method, display => 'table-cell')
+                     : $rel_type eq 'vendor'          ? $self->vendor(  $obj->$method, display => 'table-cell')
+                     : $rel_type eq 'project'         ? $self->project( $obj->$method, display => 'table-cell')
+                     : $type eq 'date'                ? $call->($obj, $method . '_as_date')
+                     : $type =~ m/float|numeric|real/ ? $::form->format_amount(\%::myconfig, $call->($obj, $method), 2)
+                     : $type eq 'boolean'             ? $call->($obj, $method . '_as_bool_yn')
+                     : $type =~ m/int|serial/         ? $spec->{data} * 1
+                     :                                  $call->($obj, $method);
+      }
+
+      $cell{alignment} = 'right' if $type =~ m/int|serial|float|real|numeric/;
+
+      push @row, \%cell;
+    }
+
+    push @data, \@row;
+  }
+
+  my @header =
+    map +{ value     => $columns[$_]->{title},
+           alignment => $data[0]->[$_]->{alignment},
+         }, (0..scalar(@columns) - 1);
+
+  return $self->render(
+    'presenter/record/record_list',
+    %params,
+    TABLE_HEADER => \@header,
+    TABLE_ROWS   => \@data,
+  );
+}
+
+#
+# private methods
+#
+
+sub _group_records {
+  my ($list) = @_;
+
+  my %matchers = (
+    sales_quotations         => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('sales_quotation')   },
+    sales_orders             => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('sales_order')       },
+    sales_delivery_orders    => sub { (ref($_[0]) eq 'SL::DB::DeliveryOrder')   &&  $_[0]->is_sales                     },
+    sales_invoices           => sub { (ref($_[0]) eq 'SL::DB::Invoice')         &&  $_[0]->invoice                      },
+    ar_transactions          => sub { (ref($_[0]) eq 'SL::DB::Invoice')         && !$_[0]->invoice                      },
+    purchase_quotations      => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('request_quotation') },
+    purchase_orders          => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('purchase_order')    },
+    purchase_delivery_orders => sub { (ref($_[0]) eq 'SL::DB::DeliveryOrder')   && !$_[0]->is_sales                     },
+    purchase_invoices        => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') &&  $_[0]->invoice                      },
+    ap_transactions          => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') && !$_[0]->invoice                      },
+  );
+
+  my %groups;
+
+  foreach my $record (@{ $list || [] }) {
+    my $type         = (first { $matchers{$_}->($record) } keys %matchers) || 'other';
+    $groups{$type} ||= [];
+    push @{ $groups{$type} }, $record;
+  }
+
+  return %groups;
+}
+
+sub _sales_quotation_list {
+  my ($self, $list) = @_;
+
+  return $self->record_list(
+    $list,
+    title   => $::locale->text('Sales Quotations'),
+    columns => [
+      [ $::locale->text('Quotation Date'),          'transdate'                                                                ],
+      [ $::locale->text('Quotation Number'),        sub { $self->sales_quotation($_[0], display => 'table-cell') }   ],
+      [ $::locale->text('Customer'),                'customer'                                                                 ],
+      [ $::locale->text('Net amount'),              'netamount'                                                                ],
+      [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
+      [ $::locale->text('Project'),                 'globalproject', ],
+      [ $::locale->text('Closed'),                  'closed'                                                                   ],
+    ],
+  );
+}
+
+sub _request_quotation_list {
+  my ($self, $list) = @_;
+
+  return $self->record_list(
+    $list,
+    title   => $::locale->text('Request Quotations'),
+    columns => [
+      [ $::locale->text('Quotation Date'),          'transdate'                                                                ],
+      [ $::locale->text('Quotation Number'),        sub { $self->sales_quotation($_[0], display => 'table-cell') }   ],
+      [ $::locale->text('Vendor'),                  'vendor'                                                                   ],
+      [ $::locale->text('Net amount'),              'netamount'                                                                ],
+      [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
+      [ $::locale->text('Project'),                 'globalproject', ],
+      [ $::locale->text('Closed'),                  'closed'                                                                   ],
+    ],
+  );
+}
+
+sub _sales_order_list {
+  my ($self, $list) = @_;
+
+  return $self->record_list(
+    $list,
+    title   => $::locale->text('Sales Orders'),
+    columns => [
+      [ $::locale->text('Order Date'),              'transdate'                                                                ],
+      [ $::locale->text('Order Number'),            sub { $self->sales_order($_[0], display => 'table-cell') }   ],
+      [ $::locale->text('Quotation'),               'quonumber' ],
+      [ $::locale->text('Customer'),                'customer'                                                                 ],
+      [ $::locale->text('Net amount'),              'netamount'                                                                ],
+      [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
+      [ $::locale->text('Project'),                 'globalproject', ],
+      [ $::locale->text('Closed'),                  'closed'                                                                   ],
+    ],
+  );
+}
+
+sub _purchase_order_list {
+  my ($self, $list) = @_;
+
+  return $self->record_list(
+    $list,
+    title   => $::locale->text('Purchase Orders'),
+    columns => [
+      [ $::locale->text('Order Date'),              'transdate'                                                                ],
+      [ $::locale->text('Order Number'),            sub { $self->sales_order($_[0], display => 'table-cell') }   ],
+      [ $::locale->text('Request for Quotation'),   'quonumber' ],
+      [ $::locale->text('Vendor'),                  'vendor'                                                                 ],
+      [ $::locale->text('Net amount'),              'netamount'                                                                ],
+      [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
+      [ $::locale->text('Project'),                 'globalproject', ],
+      [ $::locale->text('Closed'),                  'closed'                                                                   ],
+    ],
+  );
+}
+
+sub _sales_delivery_order_list {
+  my ($self, $list) = @_;
+
+  return $self->record_list(
+    $list,
+    title   => $::locale->text('Sales Delivery Orders'),
+    columns => [
+      [ $::locale->text('Delivery Order Date'),     'transdate'                                                                ],
+      [ $::locale->text('Delivery Order Number'),   sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Order Number'),            'ordnumber' ],
+      [ $::locale->text('Customer'),                'customer'                                                                 ],
+      [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
+      [ $::locale->text('Project'),                 'globalproject', ],
+      [ $::locale->text('Delivered'),               'delivered'                                                                ],
+      [ $::locale->text('Closed'),                  'closed'                                                                   ],
+    ],
+  );
+}
+
+sub _purchase_delivery_order_list {
+  my ($self, $list) = @_;
+
+  return $self->record_list(
+    $list,
+    title   => $::locale->text('Purchase Delivery Orders'),
+    columns => [
+      [ $::locale->text('Delivery Order Date'),     'transdate'                                                                ],
+      [ $::locale->text('Delivery Order Number'),   sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Order Number'),            'ordnumber' ],
+      [ $::locale->text('Vendor'),                  'vendor'                                                                 ],
+      [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
+      [ $::locale->text('Project'),                 'globalproject', ],
+      [ $::locale->text('Delivered'),               'delivered'                                                                ],
+      [ $::locale->text('Closed'),                  'closed'                                                                   ],
+    ],
+  );
+}
+
+sub _sales_invoice_list {
+  my ($self, $list) = @_;
+
+  return $self->record_list(
+    $list,
+    title   => $::locale->text('Sales Invoices'),
+    columns => [
+      [ $::locale->text('Invoice Date'),            'transdate'               ],
+      [ $::locale->text('Invoice Number'),          sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Quotation Number'),        'quonumber' ],
+      [ $::locale->text('Order Number'),            'ordnumber' ],
+      [ $::locale->text('Customer'),                'customer'                ],
+      [ $::locale->text('Net amount'),              'netamount'               ],
+      [ $::locale->text('Paid'),                    'paid'                    ],
+      [ $::locale->text('Transaction description'), 'transaction_description' ],
+    ],
+  );
+}
+
+sub _purchase_invoice_list {
+  my ($self, $list) = @_;
+
+  return $self->record_list(
+    $list,
+    title   => $::locale->text('Purchase Invoices'),
+    columns => [
+      [ $::locale->text('Invoice Date'),                 'transdate'               ],
+      [ $::locale->text('Invoice Number'),               sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Request for Quotation Number'), 'quonumber' ],
+      [ $::locale->text('Order Number'),                 'ordnumber' ],
+      [ $::locale->text('Vendor'),                       'vendor'                 ],
+      [ $::locale->text('Net amount'),                   'netamount'               ],
+      [ $::locale->text('Paid'),                         'paid'                    ],
+      [ $::locale->text('Transaction description'),      'transaction_description' ],
+    ],
+  );
+}
+
+sub _ar_transaction_list {
+  my ($self, $list) = @_;
+
+  return $self->record_list(
+    $list,
+    title   => $::locale->text('AR Transactions'),
+    columns => [
+      [ $::locale->text('Invoice Date'),            'transdate'               ],
+      [ $::locale->text('Invoice Number'),          sub { $self->ar_transaction($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Customer'),                'customer'                ],
+      [ $::locale->text('Net amount'),              'netamount'               ],
+      [ $::locale->text('Paid'),                    'paid'                    ],
+      [ $::locale->text('Transaction description'), 'transaction_description' ],
+    ],
+  );
+}
+
+sub _ap_transaction_list {
+  my ($self, $list) = @_;
+
+  return $self->record_list(
+    $list,
+    title   => $::locale->text('AP Transactions'),
+    columns => [
+      [ $::locale->text('Invoice Date'),            'transdate'                      ],
+      [ $::locale->text('Invoice Number'),          sub { $self->ar_transaction($_[0 ], display => 'table-cell') } ],
+      [ $::locale->text('Vendor'),                  'vendor'                         ],
+      [ $::locale->text('Net amount'),              'netamount'                      ],
+      [ $::locale->text('Paid'),                    'paid'                           ],
+      [ $::locale->text('Transaction description'), 'transaction_description'        ],
+    ],
+  );
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Presenter::Record - Presenter module for lists of
+sales/purchase/general ledger record Rose::DB objects
+
+=head1 SYNOPSIS
+
+  # Retrieve a number of documents from somewhere, e.g.
+  my $order   = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('sales_order') ]);
+  my $records = $order->linked_records(destination => 'to');
+
+  # Give HTML representation:
+  my $html = SL::Presenter->get->grouped_record_list($records);
+
+=head1 OVERVIEW
+
+TODO
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<empty_record_list>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of an empty list of records. Is usually
+only called by L<grouped_record_list> if its list is empty.
+
+=item C<grouped_record_list $list, %params>
+
+Given a number of Rose::DB objects in the array reference C<$list>
+this function first groups them by type. Then it calls L<record_list>
+with each non-empty type-specific sub-list and the appropriate
+parameters for outputting a list of those records.
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of all the lists.
+
+The order in which the records are grouped is:
+
+=over 2
+
+=item * sales quotations
+
+=item * sales orders
+
+=item * sales delivery orders
+
+=item * sales invoices
+
+=item * AR transactions
+
+=item * requests for quotations
+
+=item * purchase orders
+
+=item * purchase delivery orders
+
+=item * purchase invoices
+
+=item * AP transactions
+
+=back
+
+Objects of unknown types are skipped.
+
+=item C<record_list $list, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of a list of records. This list
+consists of a heading and a tabular representation of the list.
+
+The parameters include:
+
+=over 2
+
+=item C<title>
+
+Mandatory. The title to use in the heading. Must already be
+translated.
+
+=item C<columns>
+
+Mandatory. An array reference of column specs to output. Each column
+spec can be either an array reference or a hash reference.
+
+If a column spec is an array reference then the first element is the
+column's name shown in the table header. It must already be translated.
+
+The second element can be either a string or a code reference. A
+string is taken as the name of a function to call on the Rose::DB
+object for the current row. Its return value is formatted depending on
+the column's type (e.g. dates are output as the user expects them,
+floating point numbers are rounded to two decimal places and
+right-aligned etc). If it is a code reference then that code is called
+with the object as the first argument. Its return value should be an
+instance of L<SL::Presenter::EscapedText> and contain the rendered
+representation of the content to output.
+
+The third element, if present, can be a link to which the column will
+be linked.
+
+If the column spec is a hash reference then the same arguments are
+expected. The corresponding hash keys are C<title>, C<data> and
+C<link>.
+
+=back
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
index 61e7977..7a9f653 100644 (file)
@@ -1115,6 +1115,7 @@ $self->{texts} = {
   'Line Total'                  => 'Zeilensumme',
   'Line and column'             => 'Zeile und Spalte',
   'Line endings'                => 'Zeilenumbr&uuml;che',
+  'Linked Records'              => 'Verknüpfte Belege',
   'List Accounts'               => 'Konten anzeigen',
   'List Languages'              => 'Sprachen anzeigen',
   'List Price'                  => 'Listenpreis',
@@ -1497,8 +1498,10 @@ $self->{texts} = {
   'Projects'                    => 'Projekte',
   'Projecttransactions'         => 'Projektbuchungen',
   'Prozentual/Absolut'          => 'Prozentual/Absolut',
+  'Purchase Delivery Orders'    => 'Einkaufslieferscheine',
   'Purchase Delivery Orders deleteable' => 'Einkaufslieferscheine löschbar',
   'Purchase Invoice'            => 'Einkaufsrechnung',
+  'Purchase Invoices'           => 'Einkaufsrechnungen',
   'Purchase Order'              => 'Lieferantenauftrag',
   'Purchase Orders'             => 'Lieferantenaufträge',
   'Purchase Orders deleteable'  => 'Lieferantenaufträge löschbar',
@@ -1576,7 +1579,9 @@ $self->{texts} = {
   'Reports'                     => 'Berichte',
   'Representative'              => 'Vertreter',
   'Reqdate'                     => 'Liefertermin',
+  'Request Quotations'          => 'Preisanfragen',
   'Request for Quotation'       => 'Anfrage',
+  'Request for Quotation Number' => 'Anfragenummer',
   'Request for Quotations'      => 'Anfragen',
   'Request quotation'           => 'Preisanfrage',
   'Requested execution date'    => 'Gewünschtes Ausführungsdatum',
@@ -1607,13 +1612,15 @@ $self->{texts} = {
   'Saldo neu'                   => 'Saldo neu',
   'Saldo per'                   => 'Saldo per',
   'Sale Prices'                 => 'Verkaufspreise',
+  'Sales Delivery Orders'       => 'Verkaufslieferscheine',
   'Sales Delivery Orders deleteable' => 'Verkaufslieferscheine löschbar',
   'Sales Invoice'               => 'Rechnung',
-  'Sales Invoices'              => 'Kundenrechnung',
+  'Sales Invoices'              => 'Kundenrechnungen',
   'Sales Order'                 => 'Kundenauftrag',
   'Sales Orders'                => 'Aufträge',
   'Sales Orders deleteable'     => 'Kundenaufträge löschbar',
   'Sales Price information'     => 'Verkaufspreisinformation',
+  'Sales Quotations'            => 'Angebote',
   'Sales Report'                => 'Verkaufsbericht',
   'Sales and purchase invoices with inventory transactions with taxkeys' => 'Einkaufs- und Verkaufsrechnungen mit Warenbestandsbuchungen mit Steuerschlüsseln',
   'Sales delivery order'        => 'Lieferschein (Verkauf)',
diff --git a/templates/webpages/presenter/record/empty_record_list.html b/templates/webpages/presenter/record/empty_record_list.html
new file mode 100644 (file)
index 0000000..d082315
--- /dev/null
@@ -0,0 +1,2 @@
+[% USE LxERP %]
+<p class="message_hint">[% LxERP.t8('No data was found.') %]</p>
diff --git a/templates/webpages/presenter/record/record_list.html b/templates/webpages/presenter/record/record_list.html
new file mode 100644 (file)
index 0000000..67acb34
--- /dev/null
@@ -0,0 +1,28 @@
+[% USE L %][% USE LxERP %]
+<div class="listtop">[%- P.escape(title) %]</div>
+
+<div style="padding-bottom: 15px">
+ <table style="width: 100%">
+  <thead>
+   <tr>
+    [%- FOREACH column = TABLE_HEADER %]
+    <th class="listheading"[% IF column.alignment %] align="[% column.alignment %]"[% END %]>[%- P.escape(column.value) %]</th>
+    [%- END %]
+   </tr>
+  </thead>
+
+  <tbody>
+   [%- FOREACH row = TABLE_ROWS %]
+   <tr class="listrow[% loop.count % 2 %]">
+    [%- FOREACH column = row %]
+    <td[% IF column.alignment %] align="[% column.alignment %]"[% END %]>
+     [%- IF column.link %]<a href="[% column.link %]">[%- END %]
+      [%- P.escape(column.value) %]
+      [%- IF column.link %]</a>[%- END %]
+    </td>
+    [%- END %]
+   </tr>
+   [%- END %]
+  </tbody>
+ </table>
+</div>