Testfall bank_transactions angepasst
[kivitendo-erp.git] / SL / DB / Helper / Sorted.pm
index 9442c84..c536ccd 100644 (file)
@@ -16,7 +16,7 @@ sub make_sort_string {
   my $sort_dir         = defined($params{sort_dir}) ? $params{sort_dir} * 1 : $sort_spec->{default}->[1];
   my $sort_dir_str     = $sort_dir ? 'ASC' : 'DESC';
 
-  my $sort_by          = $params{sort_by};
+  my $sort_by          = $params{sort_by} || { };
   $sort_by             = $sort_spec->{default}->[0] unless $sort_spec->{columns}->{$sort_by};
 
   my $nulls_str        = '';
@@ -27,6 +27,10 @@ sub make_sort_string {
 
   my $sort_by_str = $sort_spec->{columns}->{$sort_by};
   $sort_by_str    = [ $sort_by_str ] unless ref($sort_by_str) eq 'ARRAY';
+
+  # generate tiebreaker
+  push @$sort_by_str, @{ $sort_spec->{tiebreaker} };
+
   $sort_by_str    = join(', ', map { "${_} ${sort_dir_str}${nulls_str}" } @{ $sort_by_str });
 
   return wantarray ? ($sort_by, $sort_dir, $sort_by_str) : $sort_by_str;
@@ -47,9 +51,10 @@ sub _get_sort_spec {
 sub _make_sort_spec {
   my ($class) = @_;
 
-  my %sort_spec = $class->_sort_spec if defined &{ "${class}::_sort_spec" };
+  my %sort_spec = defined &{ "${class}::_sort_spec" } ? $class->_sort_spec : ();
 
   my $meta = $class->object_class->meta;
+  my $table = $meta->table;
 
   if (!$sort_spec{default}) {
     my @primary_keys = $meta->primary_key;
@@ -59,8 +64,6 @@ sub _make_sort_spec {
   $sort_spec{columns} ||= { SIMPLE => [ map { "$_" } $meta->columns ] };
 
   if ($sort_spec{columns}->{SIMPLE}) {
-    my $table = $meta->table;
-
     if (!ref($sort_spec{columns}->{SIMPLE}) && ($sort_spec{columns}->{SIMPLE} eq 'ALL')) {
       map { $sort_spec{columns}->{"$_"} ||= "${table}.${_}"} @{ $meta->columns };
       delete $sort_spec{columns}->{SIMPLE};
@@ -69,6 +72,8 @@ sub _make_sort_spec {
     }
   }
 
+  $sort_spec{tiebreaker} ||= [ map { "${table}.${_}" } $meta->primary_key ];
+
   return \%sort_spec;
 }
 
@@ -122,7 +127,7 @@ sorting of database records
 Evaluates C<$params{sort_by}> and C<$params{sort_dir}> and returns an
 SQL string suitable for sorting. The package this package is mixed
 into has to provide a method L</_sort_spec> that returns a hash whose
-structure is explained below. That hash is authoritive in which
+structure is explained below. That hash is authoritative in which
 columns may be sorted, which column to sort by by default and how to
 handle C<NULL> values.
 
@@ -173,7 +178,7 @@ strings by which to sort. Example:
                customer_name           => 'lower(customer.name)',
              },
 
-If sorting by a column is requested that is not a key in this hash
+If sorting is requested for a column that is not a key in this hash
 then the default column name will be used.
 
 The value can be either a scalar or an array reference. If it's the
@@ -196,12 +201,12 @@ Either a scalar or a hash reference determining where C<NULL> values
 will be sorted. If undefined then the decision is left to the
 database.
 
-If it is a scalar then all the same value will be used for all
+If it is a scalar then the same value will be used for all
 classes. The value is either C<FIRST> or C<LAST>.
 
 If it is a hash reference then its keys are column names (not SQL
 names). The values are either C<FIRST> or C<LAST>. If a column name is
-not found in this hash then the special keu C<default> will be looked
+not found in this hash then the special key C<default> will be looked
 up and used if it is found.
 
 Example:
@@ -211,6 +216,15 @@ Example:
              default                 => 'LAST',
            },
 
+=item C<tiebreaker>
+
+Optional tiebreaker sorting that gets appended to any user requested sorting.
+Needed to make sorting by non unique columns deterministic.
+
+If present must be an arrayref of column sort specs (see C<column>).
+
+Defaults to primary keys.
+
 =back
 
 =back