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