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