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