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} };
75 my $relationship = $relationships{ $spec->{data} };
76 my $rel_type = !$relationship ? '' : lc $relationship->class;
77 $rel_type =~ s/^sl::db:://;
79 if (ref($spec->{data}) eq 'CODE') {
80 $cell{value} = $spec->{data}->($obj);
83 $cell{value} = $rel_type && $self->can($rel_type) ? $self->$rel_type($obj->$method, display => 'table-cell')
84 : $type eq 'Rose::DB::Object::Metadata::Column::Date' ? $call->($obj, $method . '_as_date')
85 : $type =~ m/^Rose::DB::Object::Metadata::Column::(?:Float|Numeric|Real)$/ ? $::form->format_amount(\%::myconfig, $call->($obj, $method), 2)
86 : $type eq 'Rose::DB::Object::Metadata::Column::Boolean' ? $call->($obj, $method . '_as_bool_yn')
87 : $type =~ m/^Rose::DB::Object::Metadata::Column::(?:Integer|Serial)$/ ? $spec->{data} * 1
88 : $call->($obj, $method);
91 $cell{alignment} = 'right' if $type =~ m/int|serial|float|real|numeric/;
100 map +{ value => $columns[$_]->{title},
101 alignment => $data[0]->[$_]->{alignment},
102 }, (0..scalar(@columns) - 1);
104 return $self->render(
105 'presenter/record/record_list',
107 TABLE_HEADER => \@header,
108 TABLE_ROWS => \@data,
120 sales_quotations => sub { (ref($_[0]) eq 'SL::DB::Order') && $_[0]->is_type('sales_quotation') },
121 sales_orders => sub { (ref($_[0]) eq 'SL::DB::Order') && $_[0]->is_type('sales_order') },
122 sales_delivery_orders => sub { (ref($_[0]) eq 'SL::DB::DeliveryOrder') && $_[0]->is_sales },
123 sales_invoices => sub { (ref($_[0]) eq 'SL::DB::Invoice') && $_[0]->invoice },
124 ar_transactions => sub { (ref($_[0]) eq 'SL::DB::Invoice') && !$_[0]->invoice },
125 purchase_quotations => sub { (ref($_[0]) eq 'SL::DB::Order') && $_[0]->is_type('request_quotation') },
126 purchase_orders => sub { (ref($_[0]) eq 'SL::DB::Order') && $_[0]->is_type('purchase_order') },
127 purchase_delivery_orders => sub { (ref($_[0]) eq 'SL::DB::DeliveryOrder') && !$_[0]->is_sales },
128 purchase_invoices => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') && $_[0]->invoice },
129 ap_transactions => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') && !$_[0]->invoice },
134 foreach my $record (@{ $list || [] }) {
135 my $type = (first { $matchers{$_}->($record) } keys %matchers) || 'other';
136 $groups{$type} ||= [];
137 push @{ $groups{$type} }, $record;
143 sub _sales_quotation_list {
144 my ($self, $list) = @_;
146 return $self->record_list(
148 title => $::locale->text('Sales Quotations'),
150 [ $::locale->text('Quotation Date'), 'transdate' ],
151 [ $::locale->text('Quotation Number'), sub { $self->sales_quotation($_[0], display => 'table-cell') } ],
152 [ $::locale->text('Customer'), 'customer' ],
153 [ $::locale->text('Net amount'), 'netamount' ],
154 [ $::locale->text('Transaction description'), 'transaction_description' ],
155 [ $::locale->text('Project'), 'globalproject', ],
156 [ $::locale->text('Closed'), 'closed' ],
161 sub _request_quotation_list {
162 my ($self, $list) = @_;
164 return $self->record_list(
166 title => $::locale->text('Request Quotations'),
168 [ $::locale->text('Quotation Date'), 'transdate' ],
169 [ $::locale->text('Quotation Number'), sub { $self->sales_quotation($_[0], display => 'table-cell') } ],
170 [ $::locale->text('Vendor'), 'vendor' ],
171 [ $::locale->text('Net amount'), 'netamount' ],
172 [ $::locale->text('Transaction description'), 'transaction_description' ],
173 [ $::locale->text('Project'), 'globalproject', ],
174 [ $::locale->text('Closed'), 'closed' ],
179 sub _sales_order_list {
180 my ($self, $list) = @_;
182 return $self->record_list(
184 title => $::locale->text('Sales Orders'),
186 [ $::locale->text('Order Date'), 'transdate' ],
187 [ $::locale->text('Order Number'), sub { $self->sales_order($_[0], display => 'table-cell') } ],
188 [ $::locale->text('Quotation'), 'quonumber' ],
189 [ $::locale->text('Customer'), 'customer' ],
190 [ $::locale->text('Net amount'), 'netamount' ],
191 [ $::locale->text('Transaction description'), 'transaction_description' ],
192 [ $::locale->text('Project'), 'globalproject', ],
193 [ $::locale->text('Closed'), 'closed' ],
198 sub _purchase_order_list {
199 my ($self, $list) = @_;
201 return $self->record_list(
203 title => $::locale->text('Purchase Orders'),
205 [ $::locale->text('Order Date'), 'transdate' ],
206 [ $::locale->text('Order Number'), sub { $self->sales_order($_[0], display => 'table-cell') } ],
207 [ $::locale->text('Request for Quotation'), 'quonumber' ],
208 [ $::locale->text('Vendor'), 'vendor' ],
209 [ $::locale->text('Net amount'), 'netamount' ],
210 [ $::locale->text('Transaction description'), 'transaction_description' ],
211 [ $::locale->text('Project'), 'globalproject', ],
212 [ $::locale->text('Closed'), 'closed' ],
217 sub _sales_delivery_order_list {
218 my ($self, $list) = @_;
220 return $self->record_list(
222 title => $::locale->text('Sales Delivery Orders'),
224 [ $::locale->text('Delivery Order Date'), 'transdate' ],
225 [ $::locale->text('Delivery Order Number'), sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
226 [ $::locale->text('Order Number'), 'ordnumber' ],
227 [ $::locale->text('Customer'), 'customer' ],
228 [ $::locale->text('Transaction description'), 'transaction_description' ],
229 [ $::locale->text('Project'), 'globalproject', ],
230 [ $::locale->text('Delivered'), 'delivered' ],
231 [ $::locale->text('Closed'), 'closed' ],
236 sub _purchase_delivery_order_list {
237 my ($self, $list) = @_;
239 return $self->record_list(
241 title => $::locale->text('Purchase Delivery Orders'),
243 [ $::locale->text('Delivery Order Date'), 'transdate' ],
244 [ $::locale->text('Delivery Order Number'), sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
245 [ $::locale->text('Order Number'), 'ordnumber' ],
246 [ $::locale->text('Vendor'), 'vendor' ],
247 [ $::locale->text('Transaction description'), 'transaction_description' ],
248 [ $::locale->text('Project'), 'globalproject', ],
249 [ $::locale->text('Delivered'), 'delivered' ],
250 [ $::locale->text('Closed'), 'closed' ],
255 sub _sales_invoice_list {
256 my ($self, $list) = @_;
258 return $self->record_list(
260 title => $::locale->text('Sales Invoices'),
262 [ $::locale->text('Invoice Date'), 'transdate' ],
263 [ $::locale->text('Invoice Number'), sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
264 [ $::locale->text('Quotation Number'), 'quonumber' ],
265 [ $::locale->text('Order Number'), 'ordnumber' ],
266 [ $::locale->text('Customer'), 'customer' ],
267 [ $::locale->text('Net amount'), 'netamount' ],
268 [ $::locale->text('Paid'), 'paid' ],
269 [ $::locale->text('Transaction description'), 'transaction_description' ],
274 sub _purchase_invoice_list {
275 my ($self, $list) = @_;
277 return $self->record_list(
279 title => $::locale->text('Purchase Invoices'),
281 [ $::locale->text('Invoice Date'), 'transdate' ],
282 [ $::locale->text('Invoice Number'), sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
283 [ $::locale->text('Request for Quotation Number'), 'quonumber' ],
284 [ $::locale->text('Order Number'), 'ordnumber' ],
285 [ $::locale->text('Vendor'), 'vendor' ],
286 [ $::locale->text('Net amount'), 'netamount' ],
287 [ $::locale->text('Paid'), 'paid' ],
288 [ $::locale->text('Transaction description'), 'transaction_description' ],
293 sub _ar_transaction_list {
294 my ($self, $list) = @_;
296 return $self->record_list(
298 title => $::locale->text('AR Transactions'),
300 [ $::locale->text('Invoice Date'), 'transdate' ],
301 [ $::locale->text('Invoice Number'), sub { $self->ar_transaction($_[0], display => 'table-cell') } ],
302 [ $::locale->text('Customer'), 'customer' ],
303 [ $::locale->text('Net amount'), 'netamount' ],
304 [ $::locale->text('Paid'), 'paid' ],
305 [ $::locale->text('Transaction description'), 'transaction_description' ],
310 sub _ap_transaction_list {
311 my ($self, $list) = @_;
313 return $self->record_list(
315 title => $::locale->text('AP Transactions'),
317 [ $::locale->text('Invoice Date'), 'transdate' ],
318 [ $::locale->text('Invoice Number'), sub { $self->ar_transaction($_[0 ], display => 'table-cell') } ],
319 [ $::locale->text('Vendor'), 'vendor' ],
320 [ $::locale->text('Net amount'), 'netamount' ],
321 [ $::locale->text('Paid'), 'paid' ],
322 [ $::locale->text('Transaction description'), 'transaction_description' ],
337 SL::Presenter::Record - Presenter module for lists of
338 sales/purchase/general ledger record Rose::DB objects
342 # Retrieve a number of documents from somewhere, e.g.
343 my $order = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('sales_order') ]);
344 my $records = $order->linked_records(destination => 'to');
346 # Give HTML representation:
347 my $html = SL::Presenter->get->grouped_record_list($records);
357 =item C<empty_record_list>
359 Returns a rendered version (actually an instance of
360 L<SL::Presenter::EscapedText>) of an empty list of records. Is usually
361 only called by L<grouped_record_list> if its list is empty.
363 =item C<grouped_record_list $list, %params>
365 Given a number of Rose::DB objects in the array reference C<$list>
366 this function first groups them by type. Then it calls L<record_list>
367 with each non-empty type-specific sub-list and the appropriate
368 parameters for outputting a list of those records.
370 Returns a rendered version (actually an instance of
371 L<SL::Presenter::EscapedText>) of all the lists.
373 The order in which the records are grouped is:
377 =item * sales quotations
381 =item * sales delivery orders
383 =item * sales invoices
385 =item * AR transactions
387 =item * requests for quotations
389 =item * purchase orders
391 =item * purchase delivery orders
393 =item * purchase invoices
395 =item * AP transactions
399 Objects of unknown types are skipped.
401 =item C<record_list $list, %params>
403 Returns a rendered version (actually an instance of
404 L<SL::Presenter::EscapedText>) of a list of records. This list
405 consists of a heading and a tabular representation of the list.
407 The parameters include:
413 Mandatory. The title to use in the heading. Must already be
418 Mandatory. An array reference of column specs to output. Each column
419 spec can be either an array reference or a hash reference.
421 If a column spec is an array reference then the first element is the
422 column's name shown in the table header. It must already be translated.
424 The second element can be either a string or a code reference. A
425 string is taken as the name of a function to call on the Rose::DB
426 object for the current row. Its return value is formatted depending on
427 the column's type (e.g. dates are output as the user expects them,
428 floating point numbers are rounded to two decimal places and
429 right-aligned etc). If it is a code reference then that code is called
430 with the object as the first argument. Its return value should be an
431 instance of L<SL::Presenter::EscapedText> and contain the rendered
432 representation of the content to output.
434 The third element, if present, can be a link to which the column will
437 If the column spec is a hash reference then the same arguments are
438 expected. The corresponding hash keys are C<title>, C<data> and
451 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>