1 package SL::Presenter::Record;
6 use SL::Presenter::EscapedText qw(escape is_escaped);
8 use Exporter qw(import);
9 our @EXPORT_OK = qw(grouped_record_list empty_record_list record_list record);
14 use List::Util qw(first);
18 return [] if !defined $array;
19 return $array if ref $array;
24 my ($record, %params) = @_;
26 my %grouped = _group_records( [ $record ] ); # pass $record as arrayref
27 my $type = (keys %grouped)[0];
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';
38 sub grouped_record_list {
39 my ($list, %params) = @_;
41 %params = map { exists $params{$_} ? ($_ => $params{$_}) : () } qw(edit_record_links with_columns object_id object_model);
43 my %groups = _sort_grouped_lists(_group_records($list));
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};
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};
60 $output .= _gl_transaction_list( $groups{gl_transactions}, %params) if $groups{gl_transactions};
62 $output .= _bank_transactions( $groups{bank_transactions}, %params) if $groups{bank_transactions};
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};
67 $output .= _letter_list( $groups{letters}, %params) if $groups{letters};
68 $output .= _email_journal_list( $groups{email_journals}, %params) if $groups{email_journals};
70 $output .= _dunning_list( $groups{dunnings}, %params) if $groups{dunnings};
72 $output = SL::Presenter->get->render('presenter/record/grouped_record_list', %params, output => $output);
77 sub grouped_list { goto &grouped_record_list }
79 sub empty_record_list {
81 return grouped_record_list([], %params);
84 sub empty_list { goto &empty_record_list }
87 my ($list, %params) = @_;
91 if (ref($params{columns}) eq 'ARRAY') {
93 if (ref($_) eq 'ARRAY') {
94 { title => $_->[0], data => $_->[1], link => $_->[2] }
98 } @{ delete $params{columns} };
101 croak "Wrong type for 'columns' argument: not an array reference";
104 my %with_columns = map { ($_ => 1) } @{ _arrayify($params{with_columns}) };
105 if ($with_columns{record_link_direction}) {
107 title => $::locale->text('Link direction'),
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') },
117 my %column_meta = map { $_->name => $_ } @{ $list->[0]->meta->columns };
118 my %relationships = map { $_->name => $_ } @{ $list->[0]->meta->relationships };
121 my ($obj, $method, @args) = @_;
122 $obj->$method(@args);
126 foreach my $obj (@{ $list }) {
129 foreach my $spec (@columns) {
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);
140 if (ref($spec->{data}) eq 'CODE') {
141 $cell{value} = $spec->{data}->($obj);
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);
152 $cell{alignment} = 'right' if $type =~ m/int|serial|float|real|numeric/;
157 push @data, { columns => \@row, record_link => $obj->{_record_link} };
161 map +{ value => $columns[$_]->{title},
162 alignment => $data[0]->{columns}->[$_]->{alignment},
163 }, (0..scalar(@columns) - 1);
165 return SL::Presenter->get->render(
166 'presenter/record/record_list',
168 TABLE_HEADER => \@header,
169 TABLE_ROWS => \@data,
173 sub list { goto &record_list }
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') },
205 foreach my $record (@{ $list || [] }) {
206 my $type = (first { $matchers{$_}->($record) } keys %matchers) || 'other';
207 $groups{$type} ||= [];
208 push @{ $groups{$type} }, $record;
214 sub _sort_grouped_lists {
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} } ];
222 $groups{$group} = [ sort { $a->date <=> $b->date } @{ $groups{$group} } ];
229 sub _requirement_spec_list {
230 my ($list, %params) = @_;
234 title => $::locale->text('Requirement specs'),
235 type => 'requirement_spec',
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 } ],
247 sub _shop_order_list {
248 my ($list, %params) = @_;
252 title => $::locale->text('Shop Orders'),
253 type => 'shop_order',
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' ],
264 sub _sales_quotation_list {
265 my ($list, %params) = @_;
269 title => $::locale->text('Sales Quotations'),
270 type => 'sales_quotation',
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' ],
284 sub _request_quotation_list {
285 my ($list, %params) = @_;
289 title => $::locale->text('Request Quotations'),
290 type => 'request_quotation',
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' ],
304 sub _sales_order_list {
305 my ($list, %params) = @_;
309 title => $::locale->text('Sales Orders'),
310 type => 'sales_order',
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' ],
325 sub _purchase_order_list {
326 my ($list, %params) = @_;
330 title => $::locale->text('Purchase Orders'),
331 type => 'purchase_order',
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' ],
346 sub _sales_delivery_order_list {
347 my ($list, %params) = @_;
351 title => $::locale->text('Sales Delivery Orders'),
352 type => 'sales_delivery_order',
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' ],
367 sub _purchase_delivery_order_list {
368 my ($list, %params) = @_;
372 title => $::locale->text('Purchase Delivery Orders'),
373 type => 'purchase_delivery_order',
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' ],
388 sub _sales_invoice_list {
389 my ($list, %params) = @_;
393 title => $::locale->text('Sales Invoices'),
394 type => 'sales_invoice',
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' ],
410 sub _purchase_invoice_list {
411 my ($list, %params) = @_;
415 title => $::locale->text('Purchase Invoices'),
416 type => 'purchase_invoice',
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' ],
431 sub _ar_transaction_list {
432 my ($list, %params) = @_;
436 title => $::locale->text('AR Transactions'),
437 type => 'ar_transaction',
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' ],
451 sub _ap_transaction_list {
452 my ($list, %params) = @_;
456 title => $::locale->text('AP Transactions'),
457 type => 'ap_transaction',
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' ],
470 sub _gl_transaction_list {
471 my ($list, %params) = @_;
475 title => $::locale->text('GL Transactions'),
476 type => 'gl_transaction',
478 [ $::locale->text('Transdate'), 'transdate' ],
479 [ $::locale->text('Reference'), 'reference' ],
480 [ $::locale->text('Description'), sub { $_[0]->presenter->gl_transaction(display => 'table-cell') } ],
486 sub _bank_transactions {
487 my ($list, %params) = @_;
491 title => $::locale->text('Bank transactions'),
492 type => 'bank_transactions',
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' ],
509 sub _sepa_export_list {
510 my ($list, %params) = @_;
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} || [] } ];
516 delete $params{edit_record_links};
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' ],
534 sub _sepa_transfer_list {
535 my ($list, %params) = @_;
536 _sepa_export_list($list, %params, type => 'sepa_transfer');
539 sub _sepa_collection_list {
540 my ($list, %params) = @_;
541 _sepa_export_list($list, %params, type => 'sepa_collection');
545 my ($list, %params) = @_;
549 title => $::locale->text('Letters'),
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' ],
562 sub _email_journal_list {
563 my ($list, %params) = @_;
567 title => $::locale->text('Email'),
568 type => 'email_journal',
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' ],
580 my ($list, %params) = @_;
584 title => $::locale->text('Dunnings'),
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' ],
607 SL::Presenter::Record - Presenter module for lists of
608 sales/purchase/general ledger record Rose::DB objects
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');
616 # Give HTML representation:
617 my $html = SL::Presenter->get->grouped_record_list($records);
629 Returns a rendered version (actually an instance of
630 L<SL::Presenter::EscapedText>) of a single ar, ap or gl object.
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');
637 =item C<grouped_record_list $list, %params>
639 =item C<empty_record_list>
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.
645 =item C<grouped_record_list $list, %params>
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.
652 Returns a rendered version (actually an instance of
653 L<SL::Presenter::EscapedText>) of all the lists.
655 The order in which the records are grouped is:
659 =item * sales quotations
663 =item * sales delivery orders
665 =item * sales invoices
667 =item * AR transactions
669 =item * requests for quotations
671 =item * purchase orders
673 =item * purchase delivery orders
675 =item * purchase invoices
677 =item * AP transactions
679 =item * GL transactions
681 =item * SEPA collections
683 =item * SEPA transfers
687 Objects of unknown types are skipped.
689 Parameters are passed to C<record_list> include C<with_objects> and
690 C<edit_record_links>.
692 =item C<record_list $list, %params>
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.
698 The parameters include:
704 Mandatory. The title to use in the heading. Must already be
709 Mandatory. An array reference of column specs to output. Each column
710 spec can be either an array reference or a hash reference.
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.
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.
725 The third element, if present, can be a link to which the column will
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
732 =item C<with_columns>
734 Can be set by the caller to indicate additional columns to
735 be listed. Currently supported:
739 =item C<record_link_destination>
741 The record link destination. Requires that the records to be listed have
742 been retrieved via the L<SL::DB::Helper::LinkedRecords> helper.
746 =item C<edit_record_links>
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.
762 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>