+ my $link = SL::DB::Manager::RecordLink->find_by(and => [ %data ]);
+ push @links, $link ? $link : SL::DB::RecordLink->new(%data)->save;
+ }
+
+ return wantarray ? @links : $links[0];
+}
+
+sub sort_linked_records {
+ my ($self_or_class, $sort_by, $sort_dir, @records) = @_;
+
+ @records = @{ $records[0] } if (1 == scalar(@records)) && (ref($records[0]) eq 'ARRAY');
+ $sort_dir = $sort_dir * 1 ? 1 : -1;
+
+ my %numbers = ( 'SL::DB::SalesProcess' => sub { $_[0]->id },
+ 'SL::DB::Order' => sub { $_[0]->quotation ? $_[0]->quonumber : $_[0]->ordnumber },
+ 'SL::DB::DeliveryOrder' => sub { $_[0]->donumber },
+ 'SL::DB::Invoice' => sub { $_[0]->invnumber },
+ 'SL::DB::PurchaseInvoice' => sub { $_[0]->invnumber },
+ 'SL::DB::RequirementSpec' => sub { $_[0]->id },
+ 'SL::DB::Letter' => sub { $_[0]->letternumber },
+ UNKNOWN => '9999999999999999',
+ );
+ my $number_xtor = sub {
+ my $number = $numbers{ ref($_[0]) };
+ $number = $number->($_[0]) if ref($number) eq 'CODE';
+ return $number || $numbers{UNKNOWN};
+ };
+ my $number_comparator = sub {
+ my $number_a = $number_xtor->($a);
+ my $number_b = $number_xtor->($b);
+
+ ncmp($number_a, $number_b) * $sort_dir;
+ };
+
+ my %scores;
+ %scores = ( 'SL::DB::SalesProcess' => 10,
+ 'SL::DB::RequirementSpec' => 15,
+ 'SL::DB::Order' => sub { $scores{ $_[0]->type } },
+ sales_quotation => 20,
+ sales_order => 30,
+ sales_delivery_order => 40,
+ 'SL::DB::DeliveryOrder' => sub { $scores{ $_[0]->type } },
+ 'SL::DB::Invoice' => 50,
+ request_quotation => 120,
+ purchase_order => 130,
+ purchase_delivery_order => 140,
+ 'SL::DB::PurchaseInvoice' => 150,
+ 'SL::DB::PurchaseInvoice' => 150,
+ 'SL::DB::Letter' => 200,
+ UNKNOWN => 999,
+ );
+ my $score_xtor = sub {
+ my $score = $scores{ ref($_[0]) };
+ $score = $score->($_[0]) if ref($score) eq 'CODE';
+ return $score || $scores{UNKNOWN};
+ };
+ my $type_comparator = sub {
+ my $score_a = $score_xtor->($a);
+ my $score_b = $score_xtor->($b);
+
+ $score_a == $score_b ? $number_comparator->() : ($score_a <=> $score_b) * $sort_dir;
+ };
+
+ my $today = DateTime->today_local;
+ my $date_xtor = sub {
+ $_[0]->can('transdate_as_date') ? $_[0]->transdate
+ : $_[0]->can('itime_as_date') ? $_[0]->itime->clone->truncate(to => 'day')
+ : $today;
+ };
+ my $date_comparator = sub {
+ my $date_a = $date_xtor->($a);
+ my $date_b = $date_xtor->($b);
+
+ ($date_a <=> $date_b) * $sort_dir;
+ };
+
+ my $comparator = $sort_by eq 'number' ? $number_comparator
+ : $sort_by eq 'date' ? $date_comparator
+ : $type_comparator;
+
+ return [ sort($comparator @records) ];
+}
+
+sub filter_linked_records {
+ my ($self_or_class, $filter, @records) = @_;
+
+ if ($filter eq 'accessible') {
+ my $employee = SL::DB::Manager::Employee->current;
+ @records = grep { !$_->can('may_be_accessed') || $_->may_be_accessed($employee) } @records;
+ } else {
+ croak "Unsupported filter parameter '${filter}'";
+ }
+
+ return \@records;