1 package SL::Presenter::Record;
5 use parent qw(Exporter);
7 use Exporter qw(import);
8 our @EXPORT = qw(grouped_record_list empty_record_list record_list);
11 use List::Util qw(first);
13 sub grouped_record_list {
14 my ($self, $list, %params) = @_;
16 my %groups = _group_records($list);
19 $output .= _sales_quotation_list( $self, $groups{sales_quotations}) if $groups{sales_quotations};
20 $output .= _sales_order_list( $self, $groups{sales_orders}) if $groups{sales_orders};
21 $output .= _sales_delivery_order_list( $self, $groups{sales_delivery_orders}) if $groups{sales_delivery_orders};
22 $output .= _sales_invoice_list( $self, $groups{sales_invoices}) if $groups{sales_invoices};
23 $output .= _ar_transaction_list( $self, $groups{ar_transactions}) if $groups{ar_transactions};
25 $output .= _request_quotation_list( $self, $groups{purchase_quotations}) if $groups{purchase_quotations};
26 $output .= _purchase_order_list( $self, $groups{purchase_orders}) if $groups{purchase_orders};
27 $output .= _purchase_delivery_order_list($self, $groups{purchase_delivery_orders}) if $groups{purchase_delivery_orders};
28 $output .= _purchase_invoice_list( $self, $groups{purchase_invoices}) if $groups{purchase_invoices};
29 $output .= _ar_transaction_list( $self, $groups{ar_transactions}) if $groups{ar_transactions};
31 return $output || $self->empty_record_list;
34 sub empty_record_list {
36 return $self->render('presenter/record/empty_record_list');
40 my ($self, $list, %params) = @_;
44 if (ref($params{columns}) eq 'ARRAY') {
46 if (ref($_) eq 'ARRAY') {
47 { title => $_->[0], data => $_->[1], link => $_->[2] }
51 } @{ delete $params{columns} };
54 croak "Wrong type for 'columns' argument: not an array reference";
57 my %column_meta = map { $_->name => $_ } @{ $list->[0]->meta->columns };
58 my %relationships = map { $_->name => $_ } @{ $list->[0]->meta->relationships };
61 my ($obj, $method, @args) = @_;
66 foreach my $obj (@{ $list }) {
69 foreach my $spec (@columns) {
72 my $method = $spec->{column} || $spec->{data};
73 my $meta = $column_meta{ $spec->{data} };
74 my $type = lc ref $meta;
76 my $relationship = $relationships{ $spec->{data} };
77 my $rel_type = !$relationship ? '' : lc $relationship->class;
78 $rel_type =~ s/.*:://;
80 if (ref($spec->{data}) eq 'CODE') {
81 $cell{value} = $spec->{data}->($obj);
84 $cell{value} = $rel_type eq 'customer' ? $self->customer($obj->$method, display => 'table-cell')
85 : $rel_type eq 'vendor' ? $self->vendor( $obj->$method, display => 'table-cell')
86 : $rel_type eq 'project' ? $self->project( $obj->$method, display => 'table-cell')
87 : $type eq 'date' ? $call->($obj, $method . '_as_date')
88 : $type =~ m/float|numeric|real/ ? $::form->format_amount(\%::myconfig, $call->($obj, $method), 2)
89 : $type eq 'boolean' ? $call->($obj, $method . '_as_bool_yn')
90 : $type =~ m/int|serial/ ? $spec->{data} * 1
91 : $call->($obj, $method);
94 $cell{alignment} = 'right' if $type =~ m/int|serial|float|real|numeric/;
103 map +{ value => $columns[$_]->{title},
104 alignment => $data[0]->[$_]->{alignment},
105 }, (0..scalar(@columns) - 1);
107 return $self->render(
108 'presenter/record/record_list',
110 TABLE_HEADER => \@header,
111 TABLE_ROWS => \@data,
123 sales_quotations => sub { (ref($_[0]) eq 'SL::DB::Order') && $_[0]->is_type('sales_quotation') },
124 sales_orders => sub { (ref($_[0]) eq 'SL::DB::Order') && $_[0]->is_type('sales_order') },
125 sales_delivery_orders => sub { (ref($_[0]) eq 'SL::DB::DeliveryOrder') && $_[0]->is_sales },
126 sales_invoices => sub { (ref($_[0]) eq 'SL::DB::Invoice') && $_[0]->invoice },
127 ar_transactions => sub { (ref($_[0]) eq 'SL::DB::Invoice') && !$_[0]->invoice },
128 purchase_quotations => sub { (ref($_[0]) eq 'SL::DB::Order') && $_[0]->is_type('request_quotation') },
129 purchase_orders => sub { (ref($_[0]) eq 'SL::DB::Order') && $_[0]->is_type('purchase_order') },
130 purchase_delivery_orders => sub { (ref($_[0]) eq 'SL::DB::DeliveryOrder') && !$_[0]->is_sales },
131 purchase_invoices => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') && $_[0]->invoice },
132 ap_transactions => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') && !$_[0]->invoice },
137 foreach my $record (@{ $list || [] }) {
138 my $type = (first { $matchers{$_}->($record) } keys %matchers) || 'other';
139 $groups{$type} ||= [];
140 push @{ $groups{$type} }, $record;
146 sub _sales_quotation_list {
147 my ($self, $list) = @_;
149 return $self->record_list(
151 title => $::locale->text('Sales Quotations'),
153 [ $::locale->text('Quotation Date'), 'transdate' ],
154 [ $::locale->text('Quotation Number'), sub { $self->sales_quotation($_[0], display => 'table-cell') } ],
155 [ $::locale->text('Customer'), 'customer' ],
156 [ $::locale->text('Net amount'), 'netamount' ],
157 [ $::locale->text('Transaction description'), 'transaction_description' ],
158 [ $::locale->text('Project'), 'globalproject', ],
159 [ $::locale->text('Closed'), 'closed' ],
164 sub _request_quotation_list {
165 my ($self, $list) = @_;
167 return $self->record_list(
169 title => $::locale->text('Request Quotations'),
171 [ $::locale->text('Quotation Date'), 'transdate' ],
172 [ $::locale->text('Quotation Number'), sub { $self->sales_quotation($_[0], display => 'table-cell') } ],
173 [ $::locale->text('Vendor'), 'vendor' ],
174 [ $::locale->text('Net amount'), 'netamount' ],
175 [ $::locale->text('Transaction description'), 'transaction_description' ],
176 [ $::locale->text('Project'), 'globalproject', ],
177 [ $::locale->text('Closed'), 'closed' ],
182 sub _sales_order_list {
183 my ($self, $list) = @_;
185 return $self->record_list(
187 title => $::locale->text('Sales Orders'),
189 [ $::locale->text('Order Date'), 'transdate' ],
190 [ $::locale->text('Order Number'), sub { $self->sales_order($_[0], display => 'table-cell') } ],
191 [ $::locale->text('Quotation'), 'quonumber' ],
192 [ $::locale->text('Customer'), 'customer' ],
193 [ $::locale->text('Net amount'), 'netamount' ],
194 [ $::locale->text('Transaction description'), 'transaction_description' ],
195 [ $::locale->text('Project'), 'globalproject', ],
196 [ $::locale->text('Closed'), 'closed' ],
201 sub _purchase_order_list {
202 my ($self, $list) = @_;
204 return $self->record_list(
206 title => $::locale->text('Purchase Orders'),
208 [ $::locale->text('Order Date'), 'transdate' ],
209 [ $::locale->text('Order Number'), sub { $self->sales_order($_[0], display => 'table-cell') } ],
210 [ $::locale->text('Request for Quotation'), 'quonumber' ],
211 [ $::locale->text('Vendor'), 'vendor' ],
212 [ $::locale->text('Net amount'), 'netamount' ],
213 [ $::locale->text('Transaction description'), 'transaction_description' ],
214 [ $::locale->text('Project'), 'globalproject', ],
215 [ $::locale->text('Closed'), 'closed' ],
220 sub _sales_delivery_order_list {
221 my ($self, $list) = @_;
223 return $self->record_list(
225 title => $::locale->text('Sales Delivery Orders'),
227 [ $::locale->text('Delivery Order Date'), 'transdate' ],
228 [ $::locale->text('Delivery Order Number'), sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
229 [ $::locale->text('Order Number'), 'ordnumber' ],
230 [ $::locale->text('Customer'), 'customer' ],
231 [ $::locale->text('Transaction description'), 'transaction_description' ],
232 [ $::locale->text('Project'), 'globalproject', ],
233 [ $::locale->text('Delivered'), 'delivered' ],
234 [ $::locale->text('Closed'), 'closed' ],
239 sub _purchase_delivery_order_list {
240 my ($self, $list) = @_;
242 return $self->record_list(
244 title => $::locale->text('Purchase Delivery Orders'),
246 [ $::locale->text('Delivery Order Date'), 'transdate' ],
247 [ $::locale->text('Delivery Order Number'), sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
248 [ $::locale->text('Order Number'), 'ordnumber' ],
249 [ $::locale->text('Vendor'), 'vendor' ],
250 [ $::locale->text('Transaction description'), 'transaction_description' ],
251 [ $::locale->text('Project'), 'globalproject', ],
252 [ $::locale->text('Delivered'), 'delivered' ],
253 [ $::locale->text('Closed'), 'closed' ],
258 sub _sales_invoice_list {
259 my ($self, $list) = @_;
261 return $self->record_list(
263 title => $::locale->text('Sales Invoices'),
265 [ $::locale->text('Invoice Date'), 'transdate' ],
266 [ $::locale->text('Invoice Number'), sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
267 [ $::locale->text('Quotation Number'), 'quonumber' ],
268 [ $::locale->text('Order Number'), 'ordnumber' ],
269 [ $::locale->text('Customer'), 'customer' ],
270 [ $::locale->text('Net amount'), 'netamount' ],
271 [ $::locale->text('Paid'), 'paid' ],
272 [ $::locale->text('Transaction description'), 'transaction_description' ],
277 sub _purchase_invoice_list {
278 my ($self, $list) = @_;
280 return $self->record_list(
282 title => $::locale->text('Purchase Invoices'),
284 [ $::locale->text('Invoice Date'), 'transdate' ],
285 [ $::locale->text('Invoice Number'), sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
286 [ $::locale->text('Request for Quotation Number'), 'quonumber' ],
287 [ $::locale->text('Order Number'), 'ordnumber' ],
288 [ $::locale->text('Vendor'), 'vendor' ],
289 [ $::locale->text('Net amount'), 'netamount' ],
290 [ $::locale->text('Paid'), 'paid' ],
291 [ $::locale->text('Transaction description'), 'transaction_description' ],
296 sub _ar_transaction_list {
297 my ($self, $list) = @_;
299 return $self->record_list(
301 title => $::locale->text('AR Transactions'),
303 [ $::locale->text('Invoice Date'), 'transdate' ],
304 [ $::locale->text('Invoice Number'), sub { $self->ar_transaction($_[0], display => 'table-cell') } ],
305 [ $::locale->text('Customer'), 'customer' ],
306 [ $::locale->text('Net amount'), 'netamount' ],
307 [ $::locale->text('Paid'), 'paid' ],
308 [ $::locale->text('Transaction description'), 'transaction_description' ],
313 sub _ap_transaction_list {
314 my ($self, $list) = @_;
316 return $self->record_list(
318 title => $::locale->text('AP Transactions'),
320 [ $::locale->text('Invoice Date'), 'transdate' ],
321 [ $::locale->text('Invoice Number'), sub { $self->ar_transaction($_[0 ], display => 'table-cell') } ],
322 [ $::locale->text('Vendor'), 'vendor' ],
323 [ $::locale->text('Net amount'), 'netamount' ],
324 [ $::locale->text('Paid'), 'paid' ],
325 [ $::locale->text('Transaction description'), 'transaction_description' ],
340 SL::Presenter::Record - Presenter module for lists of
341 sales/purchase/general ledger record Rose::DB objects
345 # Retrieve a number of documents from somewhere, e.g.
346 my $order = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('sales_order') ]);
347 my $records = $order->linked_records(destination => 'to');
349 # Give HTML representation:
350 my $html = SL::Presenter->get->grouped_record_list($records);
360 =item C<empty_record_list>
362 Returns a rendered version (actually an instance of
363 L<SL::Presenter::EscapedText>) of an empty list of records. Is usually
364 only called by L<grouped_record_list> if its list is empty.
366 =item C<grouped_record_list $list, %params>
368 Given a number of Rose::DB objects in the array reference C<$list>
369 this function first groups them by type. Then it calls L<record_list>
370 with each non-empty type-specific sub-list and the appropriate
371 parameters for outputting a list of those records.
373 Returns a rendered version (actually an instance of
374 L<SL::Presenter::EscapedText>) of all the lists.
376 The order in which the records are grouped is:
380 =item * sales quotations
384 =item * sales delivery orders
386 =item * sales invoices
388 =item * AR transactions
390 =item * requests for quotations
392 =item * purchase orders
394 =item * purchase delivery orders
396 =item * purchase invoices
398 =item * AP transactions
402 Objects of unknown types are skipped.
404 =item C<record_list $list, %params>
406 Returns a rendered version (actually an instance of
407 L<SL::Presenter::EscapedText>) of a list of records. This list
408 consists of a heading and a tabular representation of the list.
410 The parameters include:
416 Mandatory. The title to use in the heading. Must already be
421 Mandatory. An array reference of column specs to output. Each column
422 spec can be either an array reference or a hash reference.
424 If a column spec is an array reference then the first element is the
425 column's name shown in the table header. It must already be translated.
427 The second element can be either a string or a code reference. A
428 string is taken as the name of a function to call on the Rose::DB
429 object for the current row. Its return value is formatted depending on
430 the column's type (e.g. dates are output as the user expects them,
431 floating point numbers are rounded to two decimal places and
432 right-aligned etc). If it is a code reference then that code is called
433 with the object as the first argument. Its return value should be an
434 instance of L<SL::Presenter::EscapedText> and contain the rendered
435 representation of the content to output.
437 The third element, if present, can be a link to which the column will
440 If the column spec is a hash reference then the same arguments are
441 expected. The corresponding hash keys are C<title>, C<data> and
454 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>