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