1 package SL::Presenter::Record;
 
   5 use parent qw(Exporter);
 
   7 use Exporter qw(import);
 
   8 our @EXPORT = qw(grouped_record_list empty_record_list record_list);
 
  11 use List::Util qw(first);
 
  15   return []     if !defined $array;
 
  16   return $array if ref $array;
 
  20 sub grouped_record_list {
 
  21   my ($self, $list, %params) = @_;
 
  23   %params    = map { exists $params{$_} ? ($_ => $params{$_}) : () } qw(selectable with_columns);
 
  25   my %groups = _group_records($list);
 
  28   $output .= _sales_quotation_list(        $self, $groups{sales_quotations},         %params) if $groups{sales_quotations};
 
  29   $output .= _sales_order_list(            $self, $groups{sales_orders},             %params) if $groups{sales_orders};
 
  30   $output .= _sales_delivery_order_list(   $self, $groups{sales_delivery_orders},    %params) if $groups{sales_delivery_orders};
 
  31   $output .= _sales_invoice_list(          $self, $groups{sales_invoices},           %params) if $groups{sales_invoices};
 
  32   $output .= _ar_transaction_list(         $self, $groups{ar_transactions},          %params) if $groups{ar_transactions};
 
  34   $output .= _request_quotation_list(      $self, $groups{purchase_quotations},      %params) if $groups{purchase_quotations};
 
  35   $output .= _purchase_order_list(         $self, $groups{purchase_orders},          %params) if $groups{purchase_orders};
 
  36   $output .= _purchase_delivery_order_list($self, $groups{purchase_delivery_orders}, %params) if $groups{purchase_delivery_orders};
 
  37   $output .= _purchase_invoice_list(       $self, $groups{purchase_invoices},        %params) if $groups{purchase_invoices};
 
  38   $output .= _ar_transaction_list(         $self, $groups{ar_transactions},          %params) if $groups{ar_transactions};
 
  40   return $output || $self->empty_record_list;
 
  43 sub empty_record_list {
 
  45   return $self->render('presenter/record/empty_record_list');
 
  49   my ($self, $list, %params) = @_;
 
  53   if (ref($params{columns}) eq 'ARRAY') {
 
  55       if (ref($_) eq 'ARRAY') {
 
  56         { title => $_->[0], data => $_->[1], link => $_->[2] }
 
  60     } @{ delete $params{columns} };
 
  63     croak "Wrong type for 'columns' argument: not an array reference";
 
  66   my %with_columns = map { ($_ => 1) } @{ _arrayify($params{with_columns}) };
 
  67   if ($with_columns{record_link_direction}) {
 
  69       title => $::locale->text('Link direction'),
 
  70       data  => sub { $_[0]->{_record_link_direction} eq 'from' ? $::locale->text('Row was source for current record') : $::locale->text('Row was created from current record') },
 
  74   my %column_meta   = map { $_->name => $_ } @{ $list->[0]->meta->columns       };
 
  75   my %relationships = map { $_->name => $_ } @{ $list->[0]->meta->relationships };
 
  78     my ($obj, $method, @args) = @_;
 
  83   foreach my $obj (@{ $list }) {
 
  86     foreach my $spec (@columns) {
 
  89       my $method       =  $spec->{column} || $spec->{data};
 
  90       my $meta         =  $column_meta{ $spec->{data} };
 
  92       my $relationship =  $relationships{ $spec->{data} };
 
  93       my $rel_type     =  !$relationship ? '' : lc $relationship->class;
 
  94       $rel_type        =~ s/^sl::db:://;
 
  96       if (ref($spec->{data}) eq 'CODE') {
 
  97         $cell{value} = $spec->{data}->($obj);
 
 100         $cell{value} = $rel_type && $self->can($rel_type)                                       ? $self->$rel_type($obj->$method, display => 'table-cell')
 
 101                      : $type eq 'Rose::DB::Object::Metadata::Column::Date'                      ? $call->($obj, $method . '_as_date')
 
 102                      : $type =~ m/^Rose::DB::Object::Metadata::Column::(?:Float|Numeric|Real)$/ ? $::form->format_amount(\%::myconfig, $call->($obj, $method), 2)
 
 103                      : $type eq 'Rose::DB::Object::Metadata::Column::Boolean'                   ? $call->($obj, $method . '_as_bool_yn')
 
 104                      : $type =~ m/^Rose::DB::Object::Metadata::Column::(?:Integer|Serial)$/     ? $spec->{data} * 1
 
 105                      :                                                                            $call->($obj, $method);
 
 108       $cell{alignment} = 'right' if $type =~ m/int|serial|float|real|numeric/;
 
 117     map +{ value     => $columns[$_]->{title},
 
 118            alignment => $data[0]->[$_]->{alignment},
 
 119          }, (0..scalar(@columns) - 1);
 
 121   return $self->render(
 
 122     'presenter/record/record_list',
 
 124     TABLE_HEADER => \@header,
 
 125     TABLE_ROWS   => \@data,
 
 137     sales_quotations         => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('sales_quotation')   },
 
 138     sales_orders             => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('sales_order')       },
 
 139     sales_delivery_orders    => sub { (ref($_[0]) eq 'SL::DB::DeliveryOrder')   &&  $_[0]->is_sales                     },
 
 140     sales_invoices           => sub { (ref($_[0]) eq 'SL::DB::Invoice')         &&  $_[0]->invoice                      },
 
 141     ar_transactions          => sub { (ref($_[0]) eq 'SL::DB::Invoice')         && !$_[0]->invoice                      },
 
 142     purchase_quotations      => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('request_quotation') },
 
 143     purchase_orders          => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('purchase_order')    },
 
 144     purchase_delivery_orders => sub { (ref($_[0]) eq 'SL::DB::DeliveryOrder')   && !$_[0]->is_sales                     },
 
 145     purchase_invoices        => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') &&  $_[0]->invoice                      },
 
 146     ap_transactions          => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') && !$_[0]->invoice                      },
 
 151   foreach my $record (@{ $list || [] }) {
 
 152     my $type         = (first { $matchers{$_}->($record) } keys %matchers) || 'other';
 
 153     $groups{$type} ||= [];
 
 154     push @{ $groups{$type} }, $record;
 
 160 sub _sales_quotation_list {
 
 161   my ($self, $list, %params) = @_;
 
 163   return $self->record_list(
 
 165     title   => $::locale->text('Sales Quotations'),
 
 167       [ $::locale->text('Quotation Date'),          'transdate'                                                                ],
 
 168       [ $::locale->text('Quotation Number'),        sub { $self->sales_quotation($_[0], display => 'table-cell') }   ],
 
 169       [ $::locale->text('Customer'),                'customer'                                                                 ],
 
 170       [ $::locale->text('Net amount'),              'netamount'                                                                ],
 
 171       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
 
 172       [ $::locale->text('Project'),                 'globalproject', ],
 
 173       [ $::locale->text('Closed'),                  'closed'                                                                   ],
 
 179 sub _request_quotation_list {
 
 180   my ($self, $list, %params) = @_;
 
 182   return $self->record_list(
 
 184     title   => $::locale->text('Request Quotations'),
 
 186       [ $::locale->text('Quotation Date'),          'transdate'                                                                ],
 
 187       [ $::locale->text('Quotation Number'),        sub { $self->sales_quotation($_[0], display => 'table-cell') }   ],
 
 188       [ $::locale->text('Vendor'),                  'vendor'                                                                   ],
 
 189       [ $::locale->text('Net amount'),              'netamount'                                                                ],
 
 190       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
 
 191       [ $::locale->text('Project'),                 'globalproject', ],
 
 192       [ $::locale->text('Closed'),                  'closed'                                                                   ],
 
 198 sub _sales_order_list {
 
 199   my ($self, $list, %params) = @_;
 
 201   return $self->record_list(
 
 203     title   => $::locale->text('Sales Orders'),
 
 205       [ $::locale->text('Order Date'),              'transdate'                                                                ],
 
 206       [ $::locale->text('Order Number'),            sub { $self->sales_order($_[0], display => 'table-cell') }   ],
 
 207       [ $::locale->text('Quotation'),               'quonumber' ],
 
 208       [ $::locale->text('Customer'),                'customer'                                                                 ],
 
 209       [ $::locale->text('Net amount'),              'netamount'                                                                ],
 
 210       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
 
 211       [ $::locale->text('Project'),                 'globalproject', ],
 
 212       [ $::locale->text('Closed'),                  'closed'                                                                   ],
 
 218 sub _purchase_order_list {
 
 219   my ($self, $list, %params) = @_;
 
 221   return $self->record_list(
 
 223     title   => $::locale->text('Purchase Orders'),
 
 225       [ $::locale->text('Order Date'),              'transdate'                                                                ],
 
 226       [ $::locale->text('Order Number'),            sub { $self->sales_order($_[0], display => 'table-cell') }   ],
 
 227       [ $::locale->text('Request for Quotation'),   'quonumber' ],
 
 228       [ $::locale->text('Vendor'),                  'vendor'                                                                 ],
 
 229       [ $::locale->text('Net amount'),              'netamount'                                                                ],
 
 230       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
 
 231       [ $::locale->text('Project'),                 'globalproject', ],
 
 232       [ $::locale->text('Closed'),                  'closed'                                                                   ],
 
 238 sub _sales_delivery_order_list {
 
 239   my ($self, $list, %params) = @_;
 
 241   return $self->record_list(
 
 243     title   => $::locale->text('Sales Delivery Orders'),
 
 245       [ $::locale->text('Delivery Order Date'),     'transdate'                                                                ],
 
 246       [ $::locale->text('Delivery Order Number'),   sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
 
 247       [ $::locale->text('Order Number'),            'ordnumber' ],
 
 248       [ $::locale->text('Customer'),                'customer'                                                                 ],
 
 249       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
 
 250       [ $::locale->text('Project'),                 'globalproject', ],
 
 251       [ $::locale->text('Delivered'),               'delivered'                                                                ],
 
 252       [ $::locale->text('Closed'),                  'closed'                                                                   ],
 
 258 sub _purchase_delivery_order_list {
 
 259   my ($self, $list, %params) = @_;
 
 261   return $self->record_list(
 
 263     title   => $::locale->text('Purchase Delivery Orders'),
 
 265       [ $::locale->text('Delivery Order Date'),     'transdate'                                                                ],
 
 266       [ $::locale->text('Delivery Order Number'),   sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
 
 267       [ $::locale->text('Order Number'),            'ordnumber' ],
 
 268       [ $::locale->text('Vendor'),                  'vendor'                                                                 ],
 
 269       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
 
 270       [ $::locale->text('Project'),                 'globalproject', ],
 
 271       [ $::locale->text('Delivered'),               'delivered'                                                                ],
 
 272       [ $::locale->text('Closed'),                  'closed'                                                                   ],
 
 278 sub _sales_invoice_list {
 
 279   my ($self, $list, %params) = @_;
 
 281   return $self->record_list(
 
 283     title   => $::locale->text('Sales Invoices'),
 
 285       [ $::locale->text('Invoice Date'),            'transdate'               ],
 
 286       [ $::locale->text('Invoice Number'),          sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
 
 287       [ $::locale->text('Quotation Number'),        'quonumber' ],
 
 288       [ $::locale->text('Order Number'),            'ordnumber' ],
 
 289       [ $::locale->text('Customer'),                'customer'                ],
 
 290       [ $::locale->text('Net amount'),              'netamount'               ],
 
 291       [ $::locale->text('Paid'),                    'paid'                    ],
 
 292       [ $::locale->text('Transaction description'), 'transaction_description' ],
 
 298 sub _purchase_invoice_list {
 
 299   my ($self, $list, %params) = @_;
 
 301   return $self->record_list(
 
 303     title   => $::locale->text('Purchase Invoices'),
 
 305       [ $::locale->text('Invoice Date'),                 'transdate'               ],
 
 306       [ $::locale->text('Invoice Number'),               sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
 
 307       [ $::locale->text('Request for Quotation Number'), 'quonumber' ],
 
 308       [ $::locale->text('Order Number'),                 'ordnumber' ],
 
 309       [ $::locale->text('Vendor'),                       'vendor'                 ],
 
 310       [ $::locale->text('Net amount'),                   'netamount'               ],
 
 311       [ $::locale->text('Paid'),                         'paid'                    ],
 
 312       [ $::locale->text('Transaction description'),      'transaction_description' ],
 
 318 sub _ar_transaction_list {
 
 319   my ($self, $list, %params) = @_;
 
 321   return $self->record_list(
 
 323     title   => $::locale->text('AR Transactions'),
 
 325       [ $::locale->text('Invoice Date'),            'transdate'               ],
 
 326       [ $::locale->text('Invoice Number'),          sub { $self->ar_transaction($_[0], display => 'table-cell') } ],
 
 327       [ $::locale->text('Customer'),                'customer'                ],
 
 328       [ $::locale->text('Net amount'),              'netamount'               ],
 
 329       [ $::locale->text('Paid'),                    'paid'                    ],
 
 330       [ $::locale->text('Transaction description'), 'transaction_description' ],
 
 336 sub _ap_transaction_list {
 
 337   my ($self, $list, %params) = @_;
 
 339   return $self->record_list(
 
 341     title   => $::locale->text('AP Transactions'),
 
 343       [ $::locale->text('Invoice Date'),            'transdate'                      ],
 
 344       [ $::locale->text('Invoice Number'),          sub { $self->ar_transaction($_[0 ], display => 'table-cell') } ],
 
 345       [ $::locale->text('Vendor'),                  'vendor'                         ],
 
 346       [ $::locale->text('Net amount'),              'netamount'                      ],
 
 347       [ $::locale->text('Paid'),                    'paid'                           ],
 
 348       [ $::locale->text('Transaction description'), 'transaction_description'        ],
 
 364 SL::Presenter::Record - Presenter module for lists of
 
 365 sales/purchase/general ledger record Rose::DB objects
 
 369   # Retrieve a number of documents from somewhere, e.g.
 
 370   my $order   = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('sales_order') ]);
 
 371   my $records = $order->linked_records(destination => 'to');
 
 373   # Give HTML representation:
 
 374   my $html = SL::Presenter->get->grouped_record_list($records);
 
 384 =item C<empty_record_list>
 
 386 Returns a rendered version (actually an instance of
 
 387 L<SL::Presenter::EscapedText>) of an empty list of records. Is usually
 
 388 only called by L<grouped_record_list> if its list is empty.
 
 390 =item C<grouped_record_list $list, %params>
 
 392 Given a number of Rose::DB objects in the array reference C<$list>
 
 393 this function first groups them by type. Then it calls L<record_list>
 
 394 with each non-empty type-specific sub-list and the appropriate
 
 395 parameters for outputting a list of those records.
 
 397 Returns a rendered version (actually an instance of
 
 398 L<SL::Presenter::EscapedText>) of all the lists.
 
 400 The order in which the records are grouped is:
 
 404 =item * sales quotations
 
 408 =item * sales delivery orders
 
 410 =item * sales invoices
 
 412 =item * AR transactions
 
 414 =item * requests for quotations
 
 416 =item * purchase orders
 
 418 =item * purchase delivery orders
 
 420 =item * purchase invoices
 
 422 =item * AP transactions
 
 426 Objects of unknown types are skipped.
 
 428 =item C<record_list $list, %params>
 
 430 Returns a rendered version (actually an instance of
 
 431 L<SL::Presenter::EscapedText>) of a list of records. This list
 
 432 consists of a heading and a tabular representation of the list.
 
 434 The parameters include:
 
 440 Mandatory. The title to use in the heading. Must already be
 
 445 Mandatory. An array reference of column specs to output. Each column
 
 446 spec can be either an array reference or a hash reference.
 
 448 If a column spec is an array reference then the first element is the
 
 449 column's name shown in the table header. It must already be translated.
 
 451 The second element can be either a string or a code reference. A
 
 452 string is taken as the name of a function to call on the Rose::DB
 
 453 object for the current row. Its return value is formatted depending on
 
 454 the column's type (e.g. dates are output as the user expects them,
 
 455 floating point numbers are rounded to two decimal places and
 
 456 right-aligned etc). If it is a code reference then that code is called
 
 457 with the object as the first argument. Its return value should be an
 
 458 instance of L<SL::Presenter::EscapedText> and contain the rendered
 
 459 representation of the content to output.
 
 461 The third element, if present, can be a link to which the column will
 
 464 If the column spec is a hash reference then the same arguments are
 
 465 expected. The corresponding hash keys are C<title>, C<data> and
 
 478 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>