Record presenter - Dialogbuchungen ebenfalls anzeigen, z.B. bei Projekten
[kivitendo-erp.git] / SL / Presenter / Record.pm
1 package SL::Presenter::Record;
2
3 use strict;
4
5 use parent qw(Exporter);
6
7 use Exporter qw(import);
8 our @EXPORT = qw(grouped_record_list empty_record_list record_list record);
9
10 use SL::Util;
11
12 use Carp;
13 use List::Util qw(first);
14
15 sub _arrayify {
16   my ($array) = @_;
17   return []     if !defined $array;
18   return $array if ref $array;
19   return [ $array ];
20 }
21
22 sub record {
23   my ($self, $record, %params) = @_;
24
25   my %grouped = _group_records( [ $record ] ); # pass $record as arrayref
26   my $type    = (keys %grouped)[0];
27
28   return $self->sales_invoice(   $record, %params) if $type eq 'sales_invoices';
29   return $self->purchase_invoice($record, %params) if $type eq 'purchase_invoices';
30   return $self->ar_transaction(  $record, %params) if $type eq 'ar_transactions';
31   return $self->ap_transaction(  $record, %params) if $type eq 'ap_transactions';
32   return $self->gl_transaction(  $record, %params) if $type eq 'gl_transactions';
33
34   return '';
35 }
36
37 sub grouped_record_list {
38   my ($self, $list, %params) = @_;
39
40   %params    = map { exists $params{$_} ? ($_ => $params{$_}) : () } qw(edit_record_links with_columns object_id object_model);
41
42   my %groups = _sort_grouped_lists(_group_records($list));
43   my $output = '';
44
45   $output .= _requirement_spec_list(       $self, $groups{requirement_specs},        %params) if $groups{requirement_specs};
46   $output .= _sales_quotation_list(        $self, $groups{sales_quotations},         %params) if $groups{sales_quotations};
47   $output .= _sales_order_list(            $self, $groups{sales_orders},             %params) if $groups{sales_orders};
48   $output .= _sales_delivery_order_list(   $self, $groups{sales_delivery_orders},    %params) if $groups{sales_delivery_orders};
49   $output .= _sales_invoice_list(          $self, $groups{sales_invoices},           %params) if $groups{sales_invoices};
50   $output .= _ar_transaction_list(         $self, $groups{ar_transactions},          %params) if $groups{ar_transactions};
51
52   $output .= _request_quotation_list(      $self, $groups{purchase_quotations},      %params) if $groups{purchase_quotations};
53   $output .= _purchase_order_list(         $self, $groups{purchase_orders},          %params) if $groups{purchase_orders};
54   $output .= _purchase_delivery_order_list($self, $groups{purchase_delivery_orders}, %params) if $groups{purchase_delivery_orders};
55   $output .= _purchase_invoice_list(       $self, $groups{purchase_invoices},        %params) if $groups{purchase_invoices};
56   $output .= _ap_transaction_list(         $self, $groups{ap_transactions},          %params) if $groups{ap_transactions};
57
58   $output .= _gl_transaction_list(         $self, $groups{gl_transactions},          %params) if $groups{gl_transactions};
59
60   $output .= _bank_transactions(           $self, $groups{bank_transactions},        %params) if $groups{bank_transactions};
61
62   $output .= _sepa_collection_list(        $self, $groups{sepa_collections},         %params) if $groups{sepa_collections};
63   $output .= _sepa_transfer_list(          $self, $groups{sepa_transfers},           %params) if $groups{sepa_transfers};
64
65   $output .= _letter_list(                 $self, $groups{letters},                  %params) if $groups{letters};
66
67   $output  = $self->render('presenter/record/grouped_record_list', %params, output => $output);
68
69   return $output;
70 }
71
72 sub empty_record_list {
73   my ($self, %params) = @_;
74   return $self->grouped_record_list([], %params);
75 }
76
77 sub record_list {
78   my ($self, $list, %params) = @_;
79
80   my @columns;
81
82   if (ref($params{columns}) eq 'ARRAY') {
83     @columns = map {
84       if (ref($_) eq 'ARRAY') {
85         { title => $_->[0], data => $_->[1], link => $_->[2] }
86       } else {
87         $_;
88       }
89     } @{ delete $params{columns} };
90
91   } else {
92     croak "Wrong type for 'columns' argument: not an array reference";
93   }
94
95   my %with_columns = map { ($_ => 1) } @{ _arrayify($params{with_columns}) };
96   if ($with_columns{record_link_direction}) {
97     push @columns, {
98       title => $::locale->text('Link direction'),
99       data  => sub {
100           $_[0]->{_record_link_depth} > 1
101         ? $::locale->text('Row was linked to another record')
102         : $_[0]->{_record_link_direction} eq 'from'
103         ? $::locale->text('Row was source for current record')
104         : $::locale->text('Row was created from current record') },
105     };
106   }
107
108   my %column_meta   = map { $_->name => $_ } @{ $list->[0]->meta->columns       };
109   my %relationships = map { $_->name => $_ } @{ $list->[0]->meta->relationships };
110
111   my $call = sub {
112     my ($obj, $method, @args) = @_;
113     $obj->$method(@args);
114   };
115
116   my @data;
117   foreach my $obj (@{ $list }) {
118     my @row;
119
120     foreach my $spec (@columns) {
121       my %cell;
122
123       my $method       =  $spec->{column} || $spec->{data};
124       my $meta         =  $column_meta{ $spec->{data} };
125       my $type         =  ref $meta;
126       my $relationship =  $relationships{ $spec->{data} };
127       my $rel_type     =  !$relationship ? '' : $relationship->class;
128       $rel_type        =~ s/^SL::DB:://;
129       $rel_type        =  SL::Util::snakify($rel_type);
130
131       if (ref($spec->{data}) eq 'CODE') {
132         $cell{value} = $spec->{data}->($obj);
133
134       } else {
135         $cell{value} = $rel_type && $self->can($rel_type)                                       ? $self->$rel_type($obj->$method, display => 'table-cell')
136                      : $type eq 'Rose::DB::Object::Metadata::Column::Date'                      ? $call->($obj, $method . '_as_date')
137                      : $type =~ m/^Rose::DB::Object::Metadata::Column::(?:Float|Numeric|Real)$/ ? $::form->format_amount(\%::myconfig, $call->($obj, $method), 2)
138                      : $type eq 'Rose::DB::Object::Metadata::Column::Boolean'                   ? $call->($obj, $method . '_as_bool_yn')
139                      : $type =~ m/^Rose::DB::Object::Metadata::Column::(?:Integer|Serial)$/     ? $spec->{data} * 1
140                      :                                                                            $call->($obj, $method);
141       }
142
143       $cell{alignment} = 'right' if $type =~ m/int|serial|float|real|numeric/;
144
145       push @row, \%cell;
146     }
147
148     push @data, { columns => \@row, record_link => $obj->{_record_link} };
149   }
150
151   my @header =
152     map +{ value     => $columns[$_]->{title},
153            alignment => $data[0]->{columns}->[$_]->{alignment},
154          }, (0..scalar(@columns) - 1);
155
156   return $self->render(
157     'presenter/record/record_list',
158     %params,
159     TABLE_HEADER => \@header,
160     TABLE_ROWS   => \@data,
161   );
162 }
163
164 #
165 # private methods
166 #
167
168 sub _group_records {
169   my ($list) = @_;
170
171   my %matchers = (
172     requirement_specs        => sub { (ref($_[0]) eq 'SL::DB::RequirementSpec')                                         },
173     sales_quotations         => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('sales_quotation')   },
174     sales_orders             => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('sales_order')       },
175     sales_delivery_orders    => sub { (ref($_[0]) eq 'SL::DB::DeliveryOrder')   &&  $_[0]->is_sales                     },
176     sales_invoices           => sub { (ref($_[0]) eq 'SL::DB::Invoice')         &&  $_[0]->invoice                      },
177     ar_transactions          => sub { (ref($_[0]) eq 'SL::DB::Invoice')         && !$_[0]->invoice                      },
178     purchase_quotations      => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('request_quotation') },
179     purchase_orders          => sub { (ref($_[0]) eq 'SL::DB::Order')           &&  $_[0]->is_type('purchase_order')    },
180     purchase_delivery_orders => sub { (ref($_[0]) eq 'SL::DB::DeliveryOrder')   && !$_[0]->is_sales                     },
181     purchase_invoices        => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') &&  $_[0]->invoice                      },
182     ap_transactions          => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') && !$_[0]->invoice                      },
183     sepa_collections         => sub { (ref($_[0]) eq 'SL::DB::SepaExportItem')  &&  $_[0]->ar_id                        },
184     sepa_transfers           => sub { (ref($_[0]) eq 'SL::DB::SepaExportItem')  &&  $_[0]->ap_id                        },
185     gl_transactions          => sub { (ref($_[0]) eq 'SL::DB::GLTransaction')                                           },
186     bank_transactions        => sub { (ref($_[0]) eq 'SL::DB::BankTransaction') &&  $_[0]->id                           },
187     letters                  => sub { (ref($_[0]) eq 'SL::DB::Letter')          &&  $_[0]->id                           },
188   );
189
190   my %groups;
191
192   foreach my $record (@{ $list || [] }) {
193     my $type         = (first { $matchers{$_}->($record) } keys %matchers) || 'other';
194     $groups{$type} ||= [];
195     push @{ $groups{$type} }, $record;
196   }
197
198   return %groups;
199 }
200
201 sub _sort_grouped_lists {
202   my (%groups) = @_;
203
204   foreach my $group (keys %groups) {
205     next unless @{ $groups{$group} };
206     if ($groups{$group}->[0]->can('compare_to')) {
207       $groups{$group} = [ sort { $a->compare_to($b)    } @{ $groups{$group} } ];
208     } else {
209       $groups{$group} = [ sort { $a->date <=> $b->date } @{ $groups{$group} } ];
210     }
211   }
212
213   return %groups;
214 }
215
216 sub _requirement_spec_list {
217   my ($self, $list, %params) = @_;
218
219   return $self->record_list(
220     $list,
221     title   => $::locale->text('Requirement specs'),
222     type    => 'requirement_spec',
223     columns => [
224       [ $::locale->text('Requirement spec number'), sub { $self->requirement_spec($_[0], display => 'table-cell') } ],
225       [ $::locale->text('Customer'),                'customer'                                                      ],
226       [ $::locale->text('Title'),                   'title'                                                         ],
227       [ $::locale->text('Project'),                 'project',                                                      ],
228       [ $::locale->text('Status'),                  sub { $_[0]->status->description }                              ],
229     ],
230     %params,
231   );
232 }
233
234 sub _sales_quotation_list {
235   my ($self, $list, %params) = @_;
236
237   return $self->record_list(
238     $list,
239     title   => $::locale->text('Sales Quotations'),
240     type    => 'sales_quotation',
241     columns => [
242       [ $::locale->text('Quotation Date'),          'transdate'                                                                ],
243       [ $::locale->text('Quotation Number'),        sub { $self->sales_quotation($_[0], display => 'table-cell') }   ],
244       [ $::locale->text('Customer'),                'customer'                                                                 ],
245       [ $::locale->text('Net amount'),              'netamount'                                                                ],
246       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
247       [ $::locale->text('Project'),                 'globalproject', ],
248       [ $::locale->text('Closed'),                  'closed'                                                                   ],
249     ],
250     %params,
251   );
252 }
253
254 sub _request_quotation_list {
255   my ($self, $list, %params) = @_;
256
257   return $self->record_list(
258     $list,
259     title   => $::locale->text('Request Quotations'),
260     type    => 'request_quotation',
261     columns => [
262       [ $::locale->text('Quotation Date'),          'transdate'                                                                ],
263       [ $::locale->text('Quotation Number'),        sub { $self->request_quotation($_[0], display => 'table-cell') }   ],
264       [ $::locale->text('Vendor'),                  'vendor'                                                                   ],
265       [ $::locale->text('Net amount'),              'netamount'                                                                ],
266       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
267       [ $::locale->text('Project'),                 'globalproject', ],
268       [ $::locale->text('Closed'),                  'closed'                                                                   ],
269     ],
270     %params,
271   );
272 }
273
274 sub _sales_order_list {
275   my ($self, $list, %params) = @_;
276
277   return $self->record_list(
278     $list,
279     title   => $::locale->text('Sales Orders'),
280     type    => 'sales_order',
281     columns => [
282       [ $::locale->text('Order Date'),              'transdate'                                                                ],
283       [ $::locale->text('Order Number'),            sub { $self->sales_order($_[0], display => 'table-cell') }   ],
284       [ $::locale->text('Quotation'),               'quonumber' ],
285       [ $::locale->text('Customer'),                'customer'                                                                 ],
286       [ $::locale->text('Net amount'),              'netamount'                                                                ],
287       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
288       [ $::locale->text('Project'),                 'globalproject', ],
289       [ $::locale->text('Closed'),                  'closed'                                                                   ],
290     ],
291     %params,
292   );
293 }
294
295 sub _purchase_order_list {
296   my ($self, $list, %params) = @_;
297
298   return $self->record_list(
299     $list,
300     title   => $::locale->text('Purchase Orders'),
301     type    => 'purchase_order',
302     columns => [
303       [ $::locale->text('Order Date'),              'transdate'                                                                ],
304       [ $::locale->text('Order Number'),            sub { $self->purchase_order($_[0], display => 'table-cell') }   ],
305       [ $::locale->text('Request for Quotation'),   'quonumber' ],
306       [ $::locale->text('Vendor'),                  'vendor'                                                                 ],
307       [ $::locale->text('Net amount'),              'netamount'                                                                ],
308       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
309       [ $::locale->text('Project'),                 'globalproject', ],
310       [ $::locale->text('Closed'),                  'closed'                                                                   ],
311     ],
312     %params,
313   );
314 }
315
316 sub _sales_delivery_order_list {
317   my ($self, $list, %params) = @_;
318
319   return $self->record_list(
320     $list,
321     title   => $::locale->text('Sales Delivery Orders'),
322     type    => 'sales_delivery_order',
323     columns => [
324       [ $::locale->text('Delivery Order Date'),     'transdate'                                                                ],
325       [ $::locale->text('Delivery Order Number'),   sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
326       [ $::locale->text('Order Number'),            'ordnumber' ],
327       [ $::locale->text('Customer'),                'customer'                                                                 ],
328       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
329       [ $::locale->text('Project'),                 'globalproject', ],
330       [ $::locale->text('Delivered'),               'delivered'                                                                ],
331       [ $::locale->text('Closed'),                  'closed'                                                                   ],
332     ],
333     %params,
334   );
335 }
336
337 sub _purchase_delivery_order_list {
338   my ($self, $list, %params) = @_;
339
340   return $self->record_list(
341     $list,
342     title   => $::locale->text('Purchase Delivery Orders'),
343     type    => 'purchase_delivery_order',
344     columns => [
345       [ $::locale->text('Delivery Order Date'),     'transdate'                                                                ],
346       [ $::locale->text('Delivery Order Number'),   sub { $self->purchase_delivery_order($_[0], display => 'table-cell') } ],
347       [ $::locale->text('Order Number'),            'ordnumber' ],
348       [ $::locale->text('Vendor'),                  'vendor'                                                                 ],
349       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
350       [ $::locale->text('Project'),                 'globalproject', ],
351       [ $::locale->text('Delivered'),               'delivered'                                                                ],
352       [ $::locale->text('Closed'),                  'closed'                                                                   ],
353     ],
354     %params,
355   );
356 }
357
358 sub _sales_invoice_list {
359   my ($self, $list, %params) = @_;
360
361   return $self->record_list(
362     $list,
363     title   => $::locale->text('Sales Invoices'),
364     type    => 'sales_invoice',
365     columns => [
366       [ $::locale->text('Invoice Date'),            'transdate'               ],
367       [ $::locale->text('Type'),                    sub { $_[0]->displayable_type } ],
368       [ $::locale->text('Invoice Number'),          sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
369       [ $::locale->text('Quotation Number'),        'quonumber' ],
370       [ $::locale->text('Order Number'),            'ordnumber' ],
371       [ $::locale->text('Customer'),                'customer'                ],
372       [ $::locale->text('Net amount'),              'netamount'               ],
373       [ $::locale->text('Paid'),                    'paid'                    ],
374       [ $::locale->text('Transaction description'), 'transaction_description' ],
375     ],
376     %params,
377   );
378 }
379
380 sub _purchase_invoice_list {
381   my ($self, $list, %params) = @_;
382
383   return $self->record_list(
384     $list,
385     title   => $::locale->text('Purchase Invoices'),
386     type    => 'purchase_invoice',
387     columns => [
388       [ $::locale->text('Invoice Date'),                 'transdate'               ],
389       [ $::locale->text('Invoice Number'),               sub { $self->purchase_invoice($_[0], display => 'table-cell') } ],
390       [ $::locale->text('Request for Quotation Number'), 'quonumber' ],
391       [ $::locale->text('Order Number'),                 'ordnumber' ],
392       [ $::locale->text('Vendor'),                       'vendor'                 ],
393       [ $::locale->text('Net amount'),                   'netamount'               ],
394       [ $::locale->text('Paid'),                         'paid'                    ],
395       [ $::locale->text('Transaction description'),      'transaction_description' ],
396     ],
397     %params,
398   );
399 }
400
401 sub _ar_transaction_list {
402   my ($self, $list, %params) = @_;
403
404   return $self->record_list(
405     $list,
406     title   => $::locale->text('AR Transactions'),
407     type    => 'ar_transaction',
408     columns => [
409       [ $::locale->text('Invoice Date'),            'transdate'               ],
410       [ $::locale->text('Type'),                    sub { $_[0]->displayable_type } ],
411       [ $::locale->text('Invoice Number'),          sub { $self->ar_transaction($_[0], display => 'table-cell') } ],
412       [ $::locale->text('Customer'),                'customer'                ],
413       [ $::locale->text('Net amount'),              'netamount'               ],
414       [ $::locale->text('Paid'),                    'paid'                    ],
415       [ $::locale->text('Transaction description'), 'transaction_description' ],
416     ],
417     %params,
418   );
419 }
420
421 sub _ap_transaction_list {
422   my ($self, $list, %params) = @_;
423
424   return $self->record_list(
425     $list,
426     title   => $::locale->text('AP Transactions'),
427     type    => 'ap_transaction',
428     columns => [
429       [ $::locale->text('Invoice Date'),            'transdate'                      ],
430       [ $::locale->text('Invoice Number'),          sub { $self->ap_transaction($_[0 ], display => 'table-cell') } ],
431       [ $::locale->text('Vendor'),                  'vendor'                         ],
432       [ $::locale->text('Net amount'),              'netamount'                      ],
433       [ $::locale->text('Paid'),                    'paid'                           ],
434       [ $::locale->text('Transaction description'), 'transaction_description'        ],
435     ],
436     %params,
437   );
438 }
439
440 sub _gl_transaction_list {
441   my ($self, $list, %params) = @_;
442
443   return $self->record_list(
444     $list,
445     title   => $::locale->text('GL Transactions'),
446     type    => 'gl_transaction',
447     columns => [
448       [ $::locale->text('Date'),        'transdate'                                                    ],
449       [ $::locale->text('Reference'),   'reference'                                                    ],
450       [ $::locale->text('Description'), sub { $self->gl_transaction($_[0 ], display => 'table-cell') } ],
451     ],
452     %params,
453   );
454 }
455
456 sub _bank_transactions {
457   my ($self, $list, %params) = @_;
458
459   return $self->record_list(
460     $list,
461     title   => $::locale->text('Bank transactions'),
462     type    => 'bank_transactions',
463     columns => [
464       [ $::locale->text('Transdate'),            'transdate'                      ],
465       [ $::locale->text('Local Bank Code'),      sub { $self->bank_code($_[0]->local_bank_account) }  ],
466       [ $::locale->text('Local account number'), sub { $self->account_number($_[0]->local_bank_account) }  ],
467       [ $::locale->text('Remote Bank Code'),     'remote_bank_code' ],
468       [ $::locale->text('Remote account number'),'remote_account_number' ],
469       [ $::locale->text('Valutadate'),           'valutadate' ],
470       [ $::locale->text('Amount'),               'amount' ],
471       [ $::locale->text('Currency'),             sub { $_[0]->currency->name } ],
472       [ $::locale->text('Remote name'),          'remote_name' ],
473       [ $::locale->text('Purpose'),              'purpose' ],
474     ],
475     %params,
476   );
477 }
478
479 sub _sepa_export_list {
480   my ($self, $list, %params) = @_;
481
482   my ($source, $destination) = $params{type} eq 'sepa_transfer' ? qw(our vc)                                 : qw(vc our);
483   $params{title}             = $params{type} eq 'sepa_transfer' ? $::locale->text('Bank transfers via SEPA') : $::locale->text('Bank collections via SEPA');
484   $params{with_columns}      = [ grep { $_ ne 'record_link_direction' } @{ $params{with_columns} || [] } ];
485
486   delete $params{edit_record_links};
487
488   return $self->record_list(
489     $list,
490     columns => [
491       [ $::locale->text('Export Number'),    'sepa_export',                                  ],
492       [ $::locale->text('Execution date'),   'execution_date'                                ],
493       [ $::locale->text('Export date'),      sub { $_[0]->sepa_export->itime->to_kivitendo } ],
494       [ $::locale->text('Source BIC'),       "${source}_bic"                                 ],
495       [ $::locale->text('Source IBAN'),      "${source}_iban"                                ],
496       [ $::locale->text('Destination BIC'),  "${destination}_bic"                            ],
497       [ $::locale->text('Destination IBAN'), "${destination}_iban"                           ],
498       [ $::locale->text('Amount'),           'amount'                                        ],
499     ],
500     %params,
501   );
502 }
503
504 sub _sepa_transfer_list {
505   my ($self, $list, %params) = @_;
506   _sepa_export_list($self, $list, %params, type => 'sepa_transfer');
507 }
508
509 sub _sepa_collection_list {
510   my ($self, $list, %params) = @_;
511   _sepa_export_list($self, $list, %params, type => 'sepa_collection');
512 }
513
514 sub _letter_list {
515   my ($self, $list, %params) = @_;
516
517   return $self->record_list(
518     $list,
519     title   => $::locale->text('Letters'),
520     type    => 'letter',
521     columns => [
522       [ $::locale->text('Date'),         'date'                                                ],
523       [ $::locale->text('Letternumber'), sub { $self->letter($_[0], display => 'table-cell') } ],
524       [ $::locale->text('Customer'),     'customer'                                            ],
525       [ $::locale->text('Reference'),    'reference'                                           ],
526       [ $::locale->text('Subject'),      'subject'                                             ],
527     ],
528     %params,
529   );
530 }
531
532 1;
533
534 __END__
535
536 =pod
537
538 =encoding utf8
539
540 =head1 NAME
541
542 SL::Presenter::Record - Presenter module for lists of
543 sales/purchase/general ledger record Rose::DB objects
544
545 =head1 SYNOPSIS
546
547   # Retrieve a number of documents from somewhere, e.g.
548   my $order   = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('sales_order') ]);
549   my $records = $order->linked_records(destination => 'to');
550
551   # Give HTML representation:
552   my $html = SL::Presenter->get->grouped_record_list($records);
553
554 =head1 OVERVIEW
555
556 TODO
557
558 =head1 FUNCTIONS
559
560 =over 4
561
562 =item C<record>
563
564 Returns a rendered version (actually an instance of
565 L<SL::Presenter::EscapedText>) of a single ar, ap or gl object.
566
567 Example:
568   # fetch the record from a random acc_trans object and print its link (could be ar, ap or gl)
569   my $record = SL::DB::Manager::AccTransaction->get_first()->record;
570   my $html   = SL::Presenter->get->record($record, display => 'inline');
571
572 =item C<grouped_record_list $list, %params>
573
574 =item C<empty_record_list>
575
576 Returns a rendered version (actually an instance of
577 L<SL::Presenter::EscapedText>) of an empty list of records. Is usually
578 only called by L<grouped_record_list> if its list is empty.
579
580 =item C<grouped_record_list $list, %params>
581
582 Given a number of Rose::DB objects in the array reference C<$list>
583 this function first groups them by type. Then it calls L<record_list>
584 with each non-empty type-specific sub-list and the appropriate
585 parameters for outputting a list of those records.
586
587 Returns a rendered version (actually an instance of
588 L<SL::Presenter::EscapedText>) of all the lists.
589
590 The order in which the records are grouped is:
591
592 =over 2
593
594 =item * sales quotations
595
596 =item * sales orders
597
598 =item * sales delivery orders
599
600 =item * sales invoices
601
602 =item * AR transactions
603
604 =item * requests for quotations
605
606 =item * purchase orders
607
608 =item * purchase delivery orders
609
610 =item * purchase invoices
611
612 =item * AP transactions
613
614 =item * GL transactions
615
616 =item * SEPA collections
617
618 =item * SEPA transfers
619
620 =back
621
622 Objects of unknown types are skipped.
623
624 Parameters are passed to C<record_list> include C<with_objects> and
625 C<edit_record_links>.
626
627 =item C<record_list $list, %params>
628
629 Returns a rendered version (actually an instance of
630 L<SL::Presenter::EscapedText>) of a list of records. This list
631 consists of a heading and a tabular representation of the list.
632
633 The parameters include:
634
635 =over 2
636
637 =item C<title>
638
639 Mandatory. The title to use in the heading. Must already be
640 translated.
641
642 =item C<columns>
643
644 Mandatory. An array reference of column specs to output. Each column
645 spec can be either an array reference or a hash reference.
646
647 If a column spec is an array reference then the first element is the
648 column's name shown in the table header. It must already be translated.
649
650 The second element can be either a string or a code reference. A
651 string is taken as the name of a function to call on the Rose::DB
652 object for the current row. Its return value is formatted depending on
653 the column's type (e.g. dates are output as the user expects them,
654 floating point numbers are rounded to two decimal places and
655 right-aligned etc). If it is a code reference then that code is called
656 with the object as the first argument. Its return value should be an
657 instance of L<SL::Presenter::EscapedText> and contain the rendered
658 representation of the content to output.
659
660 The third element, if present, can be a link to which the column will
661 be linked.
662
663 If the column spec is a hash reference then the same arguments are
664 expected. The corresponding hash keys are C<title>, C<data> and
665 C<link>.
666
667 =item C<with_columns>
668
669 Can be set by the caller to indicate additional columns to
670 be listed. Currently supported:
671
672 =over 2
673
674 =item C<record_link_destination>
675
676 The record link destination. Requires that the records to be listed have
677 been retrieved via the L<SL::DB::Helper::LinkedRecords> helper.
678
679 =back
680
681 =item C<edit_record_links>
682
683 If trueish additional controls will be rendered that allow the user to
684 remove and add record links. Requires that the records to be listed have
685 been retrieved via the L<SL::DB::Helper::LinkedRecords> helper.
686
687 =back
688
689 =back
690
691 =head1 BUGS
692
693 Nothing here yet.
694
695 =head1 AUTHOR
696
697 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
698
699 =cut