Bug 17025: Fix XSS in serials-search.pl
[koha.git] / C4 / Serials.pm
index a04a100..35d62c9 100644 (file)
@@ -22,7 +22,6 @@ use Modern::Perl;
 
 use C4::Auth qw(haspermission);
 use C4::Context;
-use C4::Dates qw(format_date format_date_in_iso);
 use DateTime;
 use Date::Calc qw(:all);
 use POSIX qw(strftime);
@@ -31,8 +30,13 @@ use C4::Log;    # logaction
 use C4::Debug;
 use C4::Serials::Frequency;
 use C4::Serials::Numberpattern;
+use Koha::AdditionalField;
+use Koha::DateUtils;
+use Koha::Serial;
+use Koha::Subscriptions;
+use Koha::Subscription::Histories;
 
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 
 # Define statuses
 use constant {
@@ -56,9 +60,7 @@ use constant MISSING_STATUSES => (
     MISSING_LOST
 );
 
-
 BEGIN {
-    $VERSION = 3.07.00.049;    # set version for version checking
     require Exporter;
     @ISA    = qw(Exporter);
     @EXPORT = qw(
@@ -69,18 +71,17 @@ BEGIN {
       &HasSubscriptionStrictlyExpired &HasSubscriptionExpired &GetExpirationDate &abouttoexpire
       &GetSubscriptionHistoryFromSubscriptionId
 
-      &GetNextSeq &GetSeq &NewIssue           &ItemizeSerials    &GetSerials
+      &GetNextSeq &GetSeq &NewIssue           &GetSerials
       &GetLatestSerials   &ModSerialStatus    &GetNextDate       &GetSerials2
       &ReNewSubscription  &GetLateOrMissingIssues
       &GetSerialInformation                   &AddItem2Serial
       &PrepareSerialsData &GetNextExpected    &ModNextExpected
 
-      &UpdateClaimdateIssues
       &GetSuppliersWithLateIssues             &getsupplierbyserialid
       &GetDistributedTo   &SetDistributedTo
       &getroutinglist     &delroutingmember   &addroutingmember
       &reorder_members
-      &check_routing &updateClaim &removeMissingIssue
+      &check_routing &updateClaim
       &CountIssues
       HasItems
       &GetSubscriptionsFromBorrower
@@ -259,34 +260,6 @@ sub AddItem2Serial {
     return $rq->rows;
 }
 
-=head2 UpdateClaimdateIssues
-
-UpdateClaimdateIssues($serialids,[$date]);
-
-Update Claimdate for issues in @$serialids list with date $date
-(Take Today if none)
-
-=cut
-
-sub UpdateClaimdateIssues {
-    my ( $serialids, $date ) = @_;
-
-    return unless ($serialids);
-
-    my $dbh = C4::Context->dbh;
-    $date = strftime( "%Y-%m-%d", localtime ) unless ($date);
-    my $query = "
-        UPDATE serial
-        SET claimdate = ?,
-            status = 7,
-            claims_count = claims_count + 1
-        WHERE  serialid in (" . join( ",", map { '?' } @$serialids ) . ")
-    ";
-    my $rq = $dbh->prepare($query);
-    $rq->execute($date, @$serialids);
-    return $rq->rows;
-}
-
 =head2 GetSubscription
 
 $subs = GetSubscription($subscriptionid)
@@ -317,7 +290,16 @@ sub GetSubscription {
     my $sth = $dbh->prepare($query);
     $sth->execute($subscriptionid);
     my $subscription = $sth->fetchrow_hashref;
+
     $subscription->{cannotedit} = not can_edit_subscription( $subscription );
+
+    # Add additional fields to the subscription into a new key "additional_fields"
+    my $additional_field_values = Koha::AdditionalField->fetch_all_values({
+            tablename => 'subscription',
+            record_id => $subscriptionid,
+    });
+    $subscription->{additional_fields} = $additional_field_values->{$subscriptionid};
+
     return $subscription;
 }
 
@@ -339,6 +321,7 @@ sub GetFullSubscription {
             serial.serialseq,
             serial.planneddate, 
             serial.publisheddate, 
+            serial.publisheddatetext,
             serial.status, 
             serial.notes as notes,
             year(IF(serial.publisheddate="00-00-0000",serial.planneddate,serial.publisheddate)) as year,
@@ -457,9 +440,9 @@ sub GetSubscriptionsFromBiblionumber {
     $sth->execute($biblionumber);
     my @res;
     while ( my $subs = $sth->fetchrow_hashref ) {
-        $subs->{startdate}     = format_date( $subs->{startdate} );
-        $subs->{histstartdate} = format_date( $subs->{histstartdate} );
-        $subs->{histenddate}   = format_date( $subs->{histenddate} );
+        $subs->{startdate}     = output_pref( { dt => dt_from_string( $subs->{startdate} ),     dateonly => 1 } );
+        $subs->{histstartdate} = output_pref( { dt => dt_from_string( $subs->{histstartdate} ), dateonly => 1 } );
+        $subs->{histenddate}   = output_pref( { dt => dt_from_string( $subs->{histenddate} ),   dateonly => 1 } );
         $subs->{opacnote}     =~ s/\n/\<br\/\>/g;
         $subs->{missinglist}  =~ s/\n/\<br\/\>/g;
         $subs->{recievedlist} =~ s/\n/\<br\/\>/g;
@@ -470,7 +453,7 @@ sub GetSubscriptionsFromBiblionumber {
         if ( $subs->{enddate} eq '0000-00-00' ) {
             $subs->{enddate} = '';
         } else {
-            $subs->{enddate} = format_date( $subs->{enddate} );
+            $subs->{enddate} = output_pref( { dt => dt_from_string( $subs->{enddate}), dateonly => 1 } );
         }
         $subs->{'abouttoexpire'}       = abouttoexpire( $subs->{'subscriptionid'} );
         $subs->{'subscriptionexpired'} = HasSubscriptionExpired( $subs->{'subscriptionid'} );
@@ -495,6 +478,7 @@ sub GetFullSubscriptionsFromBiblionumber {
             serial.serialseq,
             serial.planneddate, 
             serial.publisheddate, 
+            serial.publisheddatetext,
             serial.status, 
             serial.notes as notes,
             year(IF(serial.publisheddate="00-00-0000",serial.planneddate,serial.publisheddate)) as year,
@@ -549,7 +533,18 @@ subscription expiration date.
 sub SearchSubscriptions {
     my ( $args ) = @_;
 
-    my $query = q{
+    my $additional_fields = $args->{additional_fields} // [];
+    my $matching_record_ids_for_additional_fields = [];
+    if ( @$additional_fields ) {
+        $matching_record_ids_for_additional_fields = Koha::AdditionalField->get_matching_record_ids({
+                fields => $additional_fields,
+                tablename => 'subscription',
+                exact_match => 0,
+        });
+        return () unless @$matching_record_ids_for_additional_fields;
+    }
+
+    my $query = q|
         SELECT
             subscription.notes AS publicnotes,
             subscriptionhistory.*,
@@ -564,13 +559,15 @@ sub SearchSubscriptions {
             LEFT JOIN biblio ON biblio.biblionumber = subscription.biblionumber
             LEFT JOIN biblioitems ON biblioitems.biblionumber = subscription.biblionumber
             LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
-    };
+    |;
+    $query .= q| WHERE 1|;
     my @where_strs;
     my @where_args;
     if( $args->{biblionumber} ) {
         push @where_strs, "biblio.biblionumber = ?";
         push @where_args, $args->{biblionumber};
     }
+
     if( $args->{title} ){
         my @words = split / /, $args->{title};
         my (@strs, @args);
@@ -619,8 +616,14 @@ sub SearchSubscriptions {
         push @where_strs, "subscription.closed = ?";
         push @where_args, "$args->{closed}";
     }
+
     if(@where_strs){
-        $query .= " WHERE " . join(" AND ", @where_strs);
+        $query .= ' AND ' . join(' AND ', @where_strs);
+    }
+    if ( @$additional_fields ) {
+        $query .= ' AND subscriptionid IN ('
+            . join( ', ', @$matching_record_ids_for_additional_fields )
+        . ')';
     }
 
     $query .= " ORDER BY " . $args->{orderby} if $args->{orderby};
@@ -628,12 +631,17 @@ sub SearchSubscriptions {
     my $dbh = C4::Context->dbh;
     my $sth = $dbh->prepare($query);
     $sth->execute(@where_args);
-    my $results = $sth->fetchall_arrayref( {} );
-    $sth->finish;
+    my $results =  $sth->fetchall_arrayref( {} );
 
     for my $subscription ( @$results ) {
         $subscription->{cannotedit} = not can_edit_subscription( $subscription );
         $subscription->{cannotdisplay} = not can_show_subscription( $subscription );
+
+        my $additional_field_values = Koha::AdditionalField->fetch_all_values({
+            record_id => $subscription->{subscriptionid},
+            tablename => 'subscription'
+        });
+        $subscription->{additional_fields} = $additional_field_values->{$subscription->{subscriptionid}};
     }
 
     return @$results;
@@ -663,7 +671,8 @@ sub GetSerials {
     $count = 5 unless ($count);
     my @serials;
     my $statuses = join( ',', ( ARRIVED, MISSING_STATUSES, NOT_ISSUED ) );
-    my $query = "SELECT serialid,serialseq, status, publisheddate, planneddate,notes, routingnotes
+    my $query = "SELECT serialid,serialseq, status, publisheddate,
+        publisheddatetext, planneddate,notes, routingnotes
                         FROM   serial
                         WHERE  subscriptionid = ? AND status NOT IN ( $statuses )
                         ORDER BY IF(publisheddate<>'0000-00-00',publisheddate,planneddate) DESC";
@@ -674,7 +683,7 @@ sub GetSerials {
         $line->{ "status" . $line->{status} } = 1;                                         # fills a "statusX" value, used for template status select list
         for my $datefield ( qw( planneddate publisheddate) ) {
             if ($line->{$datefield} && $line->{$datefield}!~m/^00/) {
-                $line->{$datefield} = format_date( $line->{$datefield});
+                $line->{$datefield} =  output_pref( { dt => dt_from_string( $line->{$datefield} ), dateonly => 1 } );
             } else {
                 $line->{$datefield} = q{};
             }
@@ -683,7 +692,8 @@ sub GetSerials {
     }
 
     # OK, now add the last 5 issues arrives/missing
-    $query = "SELECT   serialid,serialseq, status, planneddate, publisheddate,notes, routingnotes
+    $query = "SELECT   serialid,serialseq, status, planneddate, publisheddate,
+        publisheddatetext, notes, routingnotes
        FROM     serial
        WHERE    subscriptionid = ?
        AND      status IN ( $statuses )
@@ -696,7 +706,7 @@ sub GetSerials {
         $line->{ "status" . $line->{status} } = 1;                                         # fills a "statusX" value, used for template status select list
         for my $datefield ( qw( planneddate publisheddate) ) {
             if ($line->{$datefield} && $line->{$datefield}!~m/^00/) {
-                $line->{$datefield} = format_date( $line->{$datefield});
+                $line->{$datefield} = output_pref( { dt => dt_from_string( $line->{$datefield} ), dateonly => 1 } );
             } else {
                 $line->{$datefield} = q{};
             }
@@ -714,23 +724,28 @@ sub GetSerials {
 
 =head2 GetSerials2
 
-@serials = GetSerials2($subscriptionid,$status);
+@serials = GetSerials2($subscriptionid,$statuses);
 this function returns every serial waited for a given subscription
 as well as the number of issues registered in the database (all types)
 this number is used to see if a subscription can be deleted (=it must have only 1 issue)
 
+$statuses is an arrayref of statuses and is mandatory.
+
 =cut
 
 sub GetSerials2 {
-    my ( $subscription, $status ) = @_;
+    my ( $subscription, $statuses ) = @_;
+
+    return unless ($subscription and @$statuses);
 
-    return unless ($subscription and $status);
+    my $statuses_string = join ',', @$statuses;
 
     my $dbh   = C4::Context->dbh;
     my $query = qq|
-                 SELECT   serialid,serialseq, status, planneddate, publisheddate,notes, routingnotes
+                 SELECT serialid,serialseq, status, planneddate, publisheddate,
+                    publisheddatetext, notes, routingnotes
                  FROM     serial 
-                 WHERE    subscriptionid=$subscription AND status IN ($status)
+                 WHERE    subscriptionid=$subscription AND status IN ($statuses_string)
                  ORDER BY publisheddate,serialid DESC
                     |;
     $debug and warn "GetSerials2 query: $query";
@@ -746,7 +761,7 @@ sub GetSerials2 {
                 $line->{$datefield} = q{};
             }
             else {
-                $line->{$datefield} = format_date( $line->{$datefield} );
+                $line->{$datefield} = output_pref( { dt => dt_from_string( $line->{$datefield} ), dateonly => 1 } );
             }
         }
         push @serials, $line;
@@ -770,11 +785,11 @@ sub GetLatestSerials {
 
     my $dbh = C4::Context->dbh;
 
-    # status = 2 is "arrived"
+    my $statuses = join( ',', ( ARRIVED, MISSING_STATUSES ) );
     my $strsth = "SELECT   serialid,serialseq, status, planneddate, publisheddate, notes
                         FROM     serial
                         WHERE    subscriptionid = ?
-                        AND      status IN (2, 4, 41, 42, 43, 44)
+                        AND      status IN ($statuses)
                         ORDER BY publisheddate DESC LIMIT 0,$limit
                 ";
     my $sth = $dbh->prepare($strsth);
@@ -782,8 +797,8 @@ sub GetLatestSerials {
     my @serials;
     while ( my $line = $sth->fetchrow_hashref ) {
         $line->{ "status" . $line->{status} } = 1;                        # fills a "statusX" value, used for template status select list
-        $line->{"planneddate"} = format_date( $line->{"planneddate"} );
-        $line->{"publisheddate"} = format_date( $line->{"publisheddate"} );
+        $line->{planneddate}   = output_pref( { dt => dt_from_string( $line->{planneddate} ),   dateonly => 1 } );
+        $line->{publisheddate} = output_pref( { dt => dt_from_string( $line->{publisheddate} ), dateonly => 1 } );
         push @serials, $line;
     }
 
@@ -821,7 +836,7 @@ $subscription is a hashref containing all the attributes of the table
 'subscription'.
 $pattern is a hashref containing all the attributes of the table
 'subscription_numberpatterns'.
-$planneddate is a C4::Dates object.
+$planneddate is a date string in iso format.
 This function get the next issue for the subscription given on input arg
 
 =cut
@@ -1056,7 +1071,8 @@ sub ModSubscriptionHistory {
 
 =head2 ModSerialStatus
 
-ModSerialStatus($serialid,$serialseq, $planneddate,$publisheddate,$status,$notes)
+    ModSerialStatus($serialid, $serialseq, $planneddate, $publisheddate,
+        $publisheddatetext, $status, $notes);
 
 This function modify the serial status. Serial status is a number.(eg 2 is "arrived")
 Note : if we change from "waited" to something else,then we will have to create a new "waited" entry
@@ -1064,7 +1080,8 @@ Note : if we change from "waited" to something else,then we will have to create
 =cut
 
 sub ModSerialStatus {
-    my ( $serialid, $serialseq, $planneddate, $publisheddate, $status, $notes ) = @_;
+    my ($serialid, $serialseq, $planneddate, $publisheddate, $publisheddatetext,
+        $status, $notes) = @_;
 
     return unless ($serialid);
 
@@ -1082,13 +1099,19 @@ sub ModSerialStatus {
 
     # change status & update subscriptionhistory
     my $val;
-    if ( $status == 6 ) {
+    if ( $status == DELETED ) {
         DelIssue( { 'serialid' => $serialid, 'subscriptionid' => $subscriptionid, 'serialseq' => $serialseq } );
     } else {
 
-        my $query = 'UPDATE serial SET serialseq=?,publisheddate=?,planneddate=?,status=?,notes=? WHERE  serialid = ?';
+        my $query = '
+            UPDATE serial
+            SET serialseq = ?, publisheddate = ?, publisheddatetext = ?,
+                planneddate = ?, status = ?, notes = ?
+            WHERE  serialid = ?
+        ';
         $sth = $dbh->prepare($query);
-        $sth->execute( $serialseq, $publisheddate, $planneddate, $status, $notes, $serialid );
+        $sth->execute( $serialseq, $publisheddate, $publisheddatetext,
+            $planneddate, $status, $notes, $serialid );
         $query = "SELECT * FROM   subscription WHERE  subscriptionid = ?";
         $sth   = $dbh->prepare($query);
         $sth->execute($subscriptionid);
@@ -1099,21 +1122,20 @@ sub ModSerialStatus {
             $sth->execute($subscriptionid);
             my ( $missinglist, $recievedlist ) = $sth->fetchrow;
 
-            if ( $status == 2 || ($oldstatus == 2 && $status != 2) ) {
+            if ( $status == ARRIVED || ($oldstatus == ARRIVED && $status != ARRIVED) ) {
                 $recievedlist .= "; $serialseq"
                     if ($recievedlist !~ /(^|;)\s*$serialseq(?=;|$)/);
             }
 
             # in case serial has been previously marked as missing
-            if (grep /$status/, (1,2,3,7)) {
+            if (grep /$status/, (EXPECTED, ARRIVED, LATE, CLAIMED)) {
                 $missinglist=~ s/(^|;)\s*$serialseq(?=;|$)//g;
             }
 
-            my @missing_statuses = qw( 4 41 42 43 44 );
             $missinglist .= "; $serialseq"
-                if ( ( grep { $_ == $status } @missing_statuses ) && ( $missinglist !~/(^|;)\s*$serialseq(?=;|$)/ ) );
+                if ( ( grep { $_ == $status } ( MISSING_STATUSES ) ) && ( $missinglist !~/(^|;)\s*$serialseq(?=;|$)/ ) );
             $missinglist .= "; not issued $serialseq"
-                if ( $status == 5 && $missinglist !~ /(^|;)\s*$serialseq(?=;|$)/ );
+                if ( $status == NOT_ISSUED && $missinglist !~ /(^|;)\s*$serialseq(?=;|$)/ );
 
             $query = "UPDATE subscriptionhistory SET recievedlist=?, missinglist=? WHERE  subscriptionid=?";
             $sth   = $dbh->prepare($query);
@@ -1123,8 +1145,9 @@ sub ModSerialStatus {
         }
     }
 
-    # create new waited entry if needed (ie : was a "waited" and has changed)
-    if ( $oldstatus == 1 && $status != 1 ) {
+    # create new expected entry if needed (ie : was "expected" and has changed)
+    my $otherIssueExpected = scalar findSerialsByStatus(EXPECTED, $subscriptionid);
+    if ( !$otherIssueExpected && $oldstatus == EXPECTED && $status != EXPECTED ) {
         my $subscription = GetSubscription($subscriptionid);
         my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
 
@@ -1138,16 +1161,17 @@ sub ModSerialStatus {
         # next date (calculated from actual date & frequency parameters)
         my $nextpublisheddate = GetNextDate($subscription, $publisheddate, 1);
         my $nextpubdate = $nextpublisheddate;
-        NewIssue( $newserialseq, $subscriptionid, $subscription->{'biblionumber'}, 1, $nextpubdate, $nextpubdate );
         $query = "UPDATE subscription SET lastvalue1=?, lastvalue2=?, lastvalue3=?, innerloop1=?, innerloop2=?, innerloop3=?
                     WHERE  subscriptionid = ?";
         $sth = $dbh->prepare($query);
         $sth->execute( $newlastvalue1, $newlastvalue2, $newlastvalue3, $newinnerloop1, $newinnerloop2, $newinnerloop3, $subscriptionid );
 
+        NewIssue( $newserialseq, $subscriptionid, $subscription->{'biblionumber'}, 1, $nextpubdate, $nextpubdate );
+
         # check if an alert must be sent... (= a letter is defined & status became "arrived"
-        if ( $subscription->{letter} && $status == 2 && $oldstatus != 2 ) {
+        if ( $subscription->{letter} && $status == ARRIVED && $oldstatus != ARRIVED ) {
             require C4::Letters;
-            C4::Letters::SendAlerts( 'issue', $subscription->{subscriptionid}, $subscription->{letter} );
+            C4::Letters::SendAlerts( 'issue', $serialid, $subscription->{letter} );
         }
     }
 
@@ -1182,8 +1206,8 @@ sub GetNextExpected {
     };
     my $sth = $dbh->prepare($query);
 
-    # Each subscription has only one 'expected' issue, with serial.status==1.
-    $sth->execute( $subscriptionid, 1 );
+    # Each subscription has only one 'expected' issue.
+    $sth->execute( $subscriptionid, EXPECTED );
     my $nextissue = $sth->fetchrow_hashref;
     if ( !$nextissue ) {
         $query = qq{
@@ -1230,8 +1254,8 @@ sub ModNextExpected {
     #FIXME: Would expect to only set planneddate, but we set both on new issue creation, so updating it here
     my $sth = $dbh->prepare('UPDATE serial SET planneddate=?,publisheddate=? WHERE subscriptionid=? AND status=?');
 
-    # Each subscription has only one 'expected' issue, with serial.status==1.
-    $sth->execute( $date, $date, $subscriptionid, 1 );
+    # Each subscription has only one 'expected' issue.
+    $sth->execute( $date, $date, $subscriptionid, EXPECTED );
     return 0;
 
 }
@@ -1395,13 +1419,20 @@ sub NewSubscription {
 
     # calculate issue number
     my $serialseq = GetSeq($subscription, $pattern) || q{};
-    $query = qq|
-        INSERT INTO serial
-            (serialseq,subscriptionid,biblionumber,status, planneddate, publisheddate)
-        VALUES (?,?,?,?,?,?)
-    |;
-    $sth = $dbh->prepare($query);
-    $sth->execute( $serialseq, $subscriptionid, $biblionumber, 1, $firstacquidate, $firstacquidate );
+
+    Koha::Serial->new(
+        {
+            serialseq      => $serialseq,
+            serialseq_x    => $subscription->{'lastvalue1'},
+            serialseq_y    => $subscription->{'lastvalue2'},
+            serialseq_z    => $subscription->{'lastvalue3'},
+            subscriptionid => $subscriptionid,
+            biblionumber   => $biblionumber,
+            status         => EXPECTED,
+            planneddate    => $firstacquidate,
+            publisheddate  => $firstacquidate,
+        }
+    )->store();
 
     logaction( "SERIAL", "ADD", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
 
@@ -1493,183 +1524,56 @@ returns the serial id
 =cut
 
 sub NewIssue {
-    my ( $serialseq, $subscriptionid, $biblionumber, $status, $planneddate, $publisheddate, $notes ) = @_;
+    my ( $serialseq, $subscriptionid, $biblionumber, $status, $planneddate,
+        $publisheddate, $publisheddatetext, $notes ) = @_;
     ### FIXME biblionumber CAN be provided by subscriptionid. So Do we STILL NEED IT ?
 
     return unless ($subscriptionid);
 
-    my $dbh   = C4::Context->dbh;
-    my $query = qq|
-        INSERT INTO serial
-            (serialseq,subscriptionid,biblionumber,status,publisheddate,planneddate,notes)
-        VALUES (?,?,?,?,?,?,?)
-    |;
-    my $sth = $dbh->prepare($query);
-    $sth->execute( $serialseq, $subscriptionid, $biblionumber, $status, $publisheddate, $planneddate, $notes );
-    my $serialid = $dbh->{'mysql_insertid'};
-    $query = qq|
-        SELECT missinglist,recievedlist
-        FROM   subscriptionhistory
-        WHERE  subscriptionid=?
-    |;
-    $sth = $dbh->prepare($query);
-    $sth->execute($subscriptionid);
-    my ( $missinglist, $recievedlist ) = $sth->fetchrow;
+    my $schema = Koha::Database->new()->schema();
+
+    my $subscription = Koha::Subscriptions->find( $subscriptionid );
+
+    my $serial = Koha::Serial->new(
+        {
+            serialseq         => $serialseq,
+            serialseq_x       => $subscription->lastvalue1(),
+            serialseq_y       => $subscription->lastvalue2(),
+            serialseq_z       => $subscription->lastvalue3(),
+            subscriptionid    => $subscriptionid,
+            biblionumber      => $biblionumber,
+            status            => $status,
+            planneddate       => $planneddate,
+            publisheddate     => $publisheddate,
+            publisheddatetext => $publisheddatetext,
+            notes             => $notes,
+        }
+    )->store();
+
+    my $serialid = $serial->id();
 
-    if ( $status == 2 ) {
-      ### TODO Add a feature that improves recognition and description.
-      ### As such count (serialseq) i.e. : N18,2(N19),N20
-      ### Would use substr and index But be careful to previous presence of ()
-        $recievedlist .= "; $serialseq" unless (index($recievedlist,$serialseq)>0);
+    my $subscription_history = Koha::Subscription::Histories->find($subscriptionid);
+    my $missinglist = $subscription_history->missinglist();
+    my $recievedlist = $subscription_history->recievedlist();
+
+    if ( $status == ARRIVED ) {
+        ### TODO Add a feature that improves recognition and description.
+        ### As such count (serialseq) i.e. : N18,2(N19),N20
+        ### Would use substr and index But be careful to previous presence of ()
+        $recievedlist .= "; $serialseq" unless ( index( $recievedlist, $serialseq ) > 0 );
     }
-    if ( $status == 4 ) {
-        $missinglist .= "; $serialseq" unless (index($missinglist,$serialseq)>0);
+    if ( grep { /^$status$/ } (MISSING_STATUSES) ) {
+        $missinglist .= "; $serialseq" unless ( index( $missinglist, $serialseq ) > 0 );
     }
-    $query = qq|
-        UPDATE subscriptionhistory
-        SET    recievedlist=?, missinglist=?
-        WHERE  subscriptionid=?
-    |;
-    $sth = $dbh->prepare($query);
+
     $recievedlist =~ s/^; //;
     $missinglist  =~ s/^; //;
-    $sth->execute( $recievedlist, $missinglist, $subscriptionid );
-    return $serialid;
-}
 
-=head2 ItemizeSerials
+    $subscription_history->recievedlist($recievedlist);
+    $subscription_history->missinglist($missinglist);
+    $subscription_history->store();
 
-ItemizeSerials($serialid, $info);
-$info is a hashref containing  barcode branch, itemcallnumber, status, location
-$serialid the serialid
-return :
-1 if the itemize is a succes.
-0 and @error otherwise. @error containts the list of errors found.
-
-=cut
-
-sub ItemizeSerials {
-    my ( $serialid, $info ) = @_;
-
-    return unless ($serialid);
-
-    my $now = POSIX::strftime( "%Y-%m-%d", localtime );
-
-    my $dbh   = C4::Context->dbh;
-    my $query = qq|
-        SELECT *
-        FROM   serial
-        WHERE  serialid=?
-    |;
-    my $sth = $dbh->prepare($query);
-    $sth->execute($serialid);
-    my $data = $sth->fetchrow_hashref;
-    if ( C4::Context->preference("RoutingSerials") ) {
-
-        # check for existing biblioitem relating to serial issue
-        my ( $count, @results ) = GetBiblioItemByBiblioNumber( $data->{'biblionumber'} );
-        my $bibitemno = 0;
-        for ( my $i = 0 ; $i < $count ; $i++ ) {
-            if ( $results[$i]->{'volumeddesc'} eq $data->{'serialseq'} . ' (' . $data->{'planneddate'} . ')' ) {
-                $bibitemno = $results[$i]->{'biblioitemnumber'};
-                last;
-            }
-        }
-        if ( $bibitemno == 0 ) {
-            my $sth = $dbh->prepare( "SELECT * FROM biblioitems WHERE biblionumber = ? ORDER BY biblioitemnumber DESC" );
-            $sth->execute( $data->{'biblionumber'} );
-            my $biblioitem = $sth->fetchrow_hashref;
-            $biblioitem->{'volumedate'}  = $data->{planneddate};
-            $biblioitem->{'volumeddesc'} = $data->{serialseq} . ' (' . format_date( $data->{'planneddate'} ) . ')';
-            $biblioitem->{'dewey'}       = $info->{itemcallnumber};
-        }
-    }
-
-    my $fwk = GetFrameworkCode( $data->{'biblionumber'} );
-    if ( $info->{barcode} ) {
-        my @errors;
-        if ( is_barcode_in_use( $info->{barcode} ) ) {
-            push @errors, 'barcode_not_unique';
-        } else {
-            my $marcrecord = MARC::Record->new();
-            my ( $tag, $subfield ) = GetMarcFromKohaField( "items.barcode", $fwk );
-            my $newField = MARC::Field->new( "$tag", '', '', "$subfield" => $info->{barcode} );
-            $marcrecord->insert_fields_ordered($newField);
-            if ( $info->{branch} ) {
-                my ( $tag, $subfield ) = GetMarcFromKohaField( "items.homebranch", $fwk );
-
-                #warn "items.homebranch : $tag , $subfield";
-                if ( $marcrecord->field($tag) ) {
-                    $marcrecord->field($tag)->add_subfields( "$subfield" => $info->{branch} );
-                } else {
-                    my $newField = MARC::Field->new( "$tag", '', '', "$subfield" => $info->{branch} );
-                    $marcrecord->insert_fields_ordered($newField);
-                }
-                ( $tag, $subfield ) = GetMarcFromKohaField( "items.holdingbranch", $fwk );
-
-                #warn "items.holdingbranch : $tag , $subfield";
-                if ( $marcrecord->field($tag) ) {
-                    $marcrecord->field($tag)->add_subfields( "$subfield" => $info->{branch} );
-                } else {
-                    my $newField = MARC::Field->new( "$tag", '', '', "$subfield" => $info->{branch} );
-                    $marcrecord->insert_fields_ordered($newField);
-                }
-            }
-            if ( $info->{itemcallnumber} ) {
-                my ( $tag, $subfield ) = GetMarcFromKohaField( "items.itemcallnumber", $fwk );
-
-                if ( $marcrecord->field($tag) ) {
-                    $marcrecord->field($tag)->add_subfields( "$subfield" => $info->{itemcallnumber} );
-                } else {
-                    my $newField = MARC::Field->new( "$tag", '', '', "$subfield" => $info->{itemcallnumber} );
-                    $marcrecord->insert_fields_ordered($newField);
-                }
-            }
-            if ( $info->{notes} ) {
-                my ( $tag, $subfield ) = GetMarcFromKohaField( "items.itemnotes", $fwk );
-
-                if ( $marcrecord->field($tag) ) {
-                    $marcrecord->field($tag)->add_subfields( "$subfield" => $info->{notes} );
-                } else {
-                    my $newField = MARC::Field->new( "$tag", '', '', "$subfield" => $info->{notes} );
-                    $marcrecord->insert_fields_ordered($newField);
-                }
-            }
-            if ( $info->{location} ) {
-                my ( $tag, $subfield ) = GetMarcFromKohaField( "items.location", $fwk );
-
-                if ( $marcrecord->field($tag) ) {
-                    $marcrecord->field($tag)->add_subfields( "$subfield" => $info->{location} );
-                } else {
-                    my $newField = MARC::Field->new( "$tag", '', '', "$subfield" => $info->{location} );
-                    $marcrecord->insert_fields_ordered($newField);
-                }
-            }
-            if ( $info->{status} ) {
-                my ( $tag, $subfield ) = GetMarcFromKohaField( "items.notforloan", $fwk );
-
-                if ( $marcrecord->field($tag) ) {
-                    $marcrecord->field($tag)->add_subfields( "$subfield" => $info->{status} );
-                } else {
-                    my $newField = MARC::Field->new( "$tag", '', '', "$subfield" => $info->{status} );
-                    $marcrecord->insert_fields_ordered($newField);
-                }
-            }
-            if ( C4::Context->preference("RoutingSerials") ) {
-                my ( $tag, $subfield ) = GetMarcFromKohaField( "items.dateaccessioned", $fwk );
-                if ( $marcrecord->field($tag) ) {
-                    $marcrecord->field($tag)->add_subfields( "$subfield" => $now );
-                } else {
-                    my $newField = MARC::Field->new( "$tag", '', '', "$subfield" => $now );
-                    $marcrecord->insert_fields_ordered($newField);
-                }
-            }
-            require C4::Items;
-            C4::Items::AddItemFromMarc( $marcrecord, $data->{'biblionumber'} );
-            return 1;
-        }
-        return ( 0, @errors );
-    }
+    return $serialid;
 }
 
 =head2 HasSubscriptionStrictlyExpired
@@ -1803,10 +1707,14 @@ this function deletes subscription which has $subscriptionid as id.
 sub DelSubscription {
     my ($subscriptionid) = @_;
     my $dbh = C4::Context->dbh;
-    $subscriptionid = $dbh->quote($subscriptionid);
-    $dbh->do("DELETE FROM subscription WHERE subscriptionid=$subscriptionid");
-    $dbh->do("DELETE FROM subscriptionhistory WHERE subscriptionid=$subscriptionid");
-    $dbh->do("DELETE FROM serial WHERE subscriptionid=$subscriptionid");
+    $dbh->do("DELETE FROM subscription WHERE subscriptionid=?", undef, $subscriptionid);
+    $dbh->do("DELETE FROM subscriptionhistory WHERE subscriptionid=?", undef, $subscriptionid);
+    $dbh->do("DELETE FROM serial WHERE subscriptionid=?", undef, $subscriptionid);
+
+    my $afs = Koha::AdditionalField->all({tablename => 'subscription'});
+    foreach my $af (@$afs) {
+        $af->delete_values({record_id => $subscriptionid});
+    }
 
     logaction( "SERIAL", "DELETE", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
 }
@@ -1861,7 +1769,7 @@ sub DelIssue {
 
 @issuelist = GetLateMissingIssues($supplierid,$serialid)
 
-this function selects missing issues on database - where serial.status = 4 or serial.status=3 or planneddate<now
+this function selects missing issues on database - where serial.status = MISSING* or serial.status = LATE or planneddate<now
 
 return :
 the issuelist as an array of hash refs. Each element of this array contains 
@@ -1875,6 +1783,7 @@ sub GetLateOrMissingIssues {
     return unless ( $supplierid or $serialid );
 
     my $dbh = C4::Context->dbh;
+
     my $sth;
     my $byserial = '';
     if ($serialid) {
@@ -1885,6 +1794,7 @@ sub GetLateOrMissingIssues {
     } else {
         $order = "title";
     }
+    my $missing_statuses_string = join ',', (MISSING_STATUSES);
     if ($supplierid) {
         $sth = $dbh->prepare(
             "SELECT
@@ -1898,7 +1808,7 @@ sub GetLateOrMissingIssues {
                 LEFT JOIN biblioitems   ON subscription.biblionumber=biblioitems.biblionumber
                 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
                 WHERE subscription.subscriptionid = serial.subscriptionid
-                AND (serial.STATUS IN (4, 41, 42, 43, 44) OR ((planneddate < now() AND serial.STATUS =1) OR serial.STATUS = 3 OR serial.STATUS = 7))
+                AND (serial.STATUS IN ($missing_statuses_string) OR ((planneddate < now() AND serial.STATUS = ?) OR serial.STATUS = ? OR serial.STATUS = ?))
                 AND subscription.aqbooksellerid=$supplierid
                 $byserial
                 ORDER BY $order"
@@ -1915,68 +1825,34 @@ sub GetLateOrMissingIssues {
                 LEFT JOIN biblio ON subscription.biblionumber=biblio.biblionumber
                 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
                 WHERE subscription.subscriptionid = serial.subscriptionid
-                        AND (serial.STATUS IN (4, 41, 42, 43, 44) OR ((planneddate < now() AND serial.STATUS =1) OR serial.STATUS = 3 OR serial.STATUS = 7))
+                        AND (serial.STATUS IN ($missing_statuses_string) OR ((planneddate < now() AND serial.STATUS = ?) OR serial.STATUS = ? OR serial.STATUS = ?))
                 $byserial
                 ORDER BY $order"
         );
     }
-    $sth->execute;
+    $sth->execute( EXPECTED, LATE, CLAIMED );
     my @issuelist;
     while ( my $line = $sth->fetchrow_hashref ) {
 
         if ($line->{planneddate} && $line->{planneddate} !~/^0+\-/) {
             $line->{planneddateISO} = $line->{planneddate};
-            $line->{planneddate} = format_date( $line->{planneddate} );
+            $line->{planneddate} = output_pref( { dt => dt_from_string( $line->{"planneddate"} ), dateonly => 1 } );
         }
         if ($line->{claimdate} && $line->{claimdate} !~/^0+\-/) {
             $line->{claimdateISO} = $line->{claimdate};
-            $line->{claimdate}   = format_date( $line->{claimdate} );
+            $line->{claimdate}   = output_pref( { dt => dt_from_string( $line->{"claimdate"} ), dateonly => 1 } );
         }
         $line->{"status".$line->{status}}   = 1;
-        push @issuelist, $line;
-    }
-    return @issuelist;
-}
-
-=head2 removeMissingIssue
 
-removeMissingIssue($subscriptionid)
+        my $additional_field_values = Koha::AdditionalField->fetch_all_values({
+            record_id => $line->{subscriptionid},
+            tablename => 'subscription'
+        });
+        %$line = ( %$line, additional_fields => $additional_field_values->{$line->{subscriptionid}} );
 
-this function removes an issue from being part of the missing string in 
-subscriptionlist.missinglist column
-
-called when a missing issue is found from the serials-recieve.pl file
-
-=cut
-
-sub removeMissingIssue {
-    my ( $sequence, $subscriptionid ) = @_;
-
-    return unless ($sequence and $subscriptionid);
-
-    my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare("SELECT * FROM subscriptionhistory WHERE subscriptionid = ?");
-    $sth->execute($subscriptionid);
-    my $data              = $sth->fetchrow_hashref;
-    my $missinglist       = $data->{'missinglist'};
-    my $missinglistbefore = $missinglist;
-
-    # warn $missinglist." before";
-    $missinglist =~ s/($sequence)//;
-
-    # warn $missinglist." after";
-    if ( $missinglist ne $missinglistbefore ) {
-        $missinglist =~ s/\|\s\|/\|/g;
-        $missinglist =~ s/^\| //g;
-        $missinglist =~ s/\|$//g;
-        my $sth2 = $dbh->prepare(
-            "UPDATE subscriptionhistory
-                    SET missinglist = ?
-                    WHERE subscriptionid = ?"
-        );
-        $sth2->execute( $missinglist, $subscriptionid );
+        push @issuelist, $line;
     }
-    return;
+    return @issuelist;
 }
 
 =head2 updateClaim
@@ -1990,15 +1866,19 @@ called from claims.pl file
 =cut
 
 sub updateClaim {
-    my ($serialid) = @_;
-    my $dbh        = C4::Context->dbh;
-    $dbh->do(q|
+    my ($serialids) = @_;
+    return unless $serialids;
+    unless ( ref $serialids ) {
+        $serialids = [ $serialids ];
+    }
+    my $dbh = C4::Context->dbh;
+    return $dbh->do(q|
         UPDATE serial
         SET claimdate = NOW(),
-            claims_count = claims_count + 1
-        WHERE serialid = ?
-    |, {}, $serialid );
-    return;
+            claims_count = claims_count + 1,
+            status = ?
+        WHERE serialid in (| . join( q|,|, (q|?|) x @$serialids ) . q|)|,
+        {}, CLAIMED, @$serialids );
 }
 
 =head2 getsupplierbyserialid
@@ -2389,6 +2269,89 @@ sub GetFictiveIssueNumber {
     return $issueno;
 }
 
+sub _get_next_date_day {
+    my ($subscription, $freqdata, $year, $month, $day) = @_;
+
+    if ($subscription->{countissuesperunit} + 1 > $freqdata->{issuesperunit}){
+        ($year,$month,$day) = Add_Delta_Days($year,$month, $day , $freqdata->{unitsperissue} );
+        $subscription->{countissuesperunit} = 1;
+    } else {
+        $subscription->{countissuesperunit}++;
+    }
+
+    return ($year, $month, $day);
+}
+
+sub _get_next_date_week {
+    my ($subscription, $freqdata, $year, $month, $day) = @_;
+
+    my ($wkno, $yr) = Week_of_Year($year, $month, $day);
+    my $fa_dow = Day_of_Week(split /-/, $subscription->{firstacquidate});
+
+    if ($subscription->{countissuesperunit} + 1 > $freqdata->{issuesperunit}){
+        $subscription->{countissuesperunit} = 1;
+        $wkno += $freqdata->{unitsperissue};
+        if($wkno > 52){
+            $wkno = $wkno % 52;
+            $yr++;
+        }
+        ($year,$month,$day) = Monday_of_Week($wkno, $yr);
+        ($year,$month,$day) = Add_Delta_Days($year, $month, $day, $fa_dow - 1);
+    } else {
+        # Try to guess the next day of week
+        my $delta_days = int((7 - ($fa_dow - 1)) / $freqdata->{issuesperunit});
+        ($year,$month,$day) = Add_Delta_Days($year, $month, $day, $delta_days);
+        $subscription->{countissuesperunit}++;
+    }
+
+    return ($year, $month, $day);
+}
+
+sub _get_next_date_month {
+    my ($subscription, $freqdata, $year, $month, $day) = @_;
+
+    my $fa_day;
+    (undef, undef, $fa_day) = split /-/, $subscription->{firstacquidate};
+
+    if ($subscription->{countissuesperunit} + 1 > $freqdata->{issuesperunit}){
+        $subscription->{countissuesperunit} = 1;
+        ($year,$month,$day) = Add_Delta_YM($year,$month,$day, 0,
+            $freqdata->{unitsperissue});
+        my $days_in_month = Days_in_Month($year, $month);
+        $day = $fa_day <= $days_in_month ? $fa_day : $days_in_month;
+    } else {
+        # Try to guess the next day in month
+        my $days_in_month = Days_in_Month($year, $month);
+        my $delta_days = int(($days_in_month - ($fa_day - 1)) / $freqdata->{issuesperunit});
+        ($year,$month,$day) = Add_Delta_Days($year, $month, $day, $delta_days);
+        $subscription->{countissuesperunit}++;
+    }
+
+    return ($year, $month, $day);
+}
+
+sub _get_next_date_year {
+    my ($subscription, $freqdata, $year, $month, $day) = @_;
+
+    my ($fa_year, $fa_month, $fa_day) = split /-/, $subscription->{firstacquidate};
+
+    if ($subscription->{countissuesperunit} + 1 > $freqdata->{issuesperunit}){
+        $subscription->{countissuesperunit} = 1;
+        ($year) = Add_Delta_YM($year,$month,$day, $freqdata->{"unitsperissue"},0);
+        $month = $fa_month;
+        my $days_in_month = Days_in_Month($year, $month);
+        $day = $fa_day <= $days_in_month ? $fa_day : $days_in_month;
+    } else {
+        # Try to guess the next day in year
+        my $days_in_year = Days_in_Year($year,12); #Sum the days of all the months of this year
+        my $delta_days = int(($days_in_year - ($fa_day - 1)) / $freqdata->{issuesperunit});
+        ($year,$month,$day) = Add_Delta_Days($year, $month, $day, $delta_days);
+        $subscription->{countissuesperunit}++;
+    }
+
+    return ($year, $month, $day);
+}
+
 =head2 GetNextDate
 
 $resultdate = GetNextDate($publisheddate,$subscription)
@@ -2439,107 +2402,41 @@ sub GetNextDate {
         my $unit = lc $freqdata->{'unit'};
         if ($unit eq 'day') {
             while ($irregularities{$issueno}) {
-                if ($subscription->{'countissuesperunit'} + 1 > $freqdata->{'issuesperunit'}){
-                    ($year,$month,$day) = Add_Delta_Days($year,$month, $day , $freqdata->{'unitsperissue'} );
-                    $subscription->{'countissuesperunit'} = 1;
-                } else {
-                    $subscription->{'countissuesperunit'}++;
-                }
+                ($year, $month, $day) = _get_next_date_day($subscription,
+                    $freqdata, $year, $month, $day);
                 $issueno++;
             }
-            if ($subscription->{'countissuesperunit'} + 1 > $freqdata->{'issuesperunit'}){
-                ($year,$month,$day) = Add_Delta_Days($year,$month, $day , $freqdata->{"unitsperissue"} );
-                $subscription->{'countissuesperunit'} = 1;
-            } else {
-                $subscription->{'countissuesperunit'}++;
-            }
+            ($year, $month, $day) = _get_next_date_day($subscription, $freqdata,
+                $year, $month, $day);
         }
         elsif ($unit eq 'week') {
-            my ($wkno, $yr) = Week_of_Year($year, $month, $day);
             while ($irregularities{$issueno}) {
-                if ($subscription->{'countissuesperunit'} + 1 > $freqdata->{'issuesperunit'}){
-                    $subscription->{'countissuesperunit'} = 1;
-                    $wkno += $freqdata->{"unitsperissue"};
-                    if($wkno > 52){
-                        $wkno = $wkno % 52;
-                        $yr++;
-                    }
-                    my $dow = Day_of_Week($year, $month, $day);
-                    ($year,$month,$day) = Monday_of_Week($wkno, $yr);
-                    if($freqdata->{'issuesperunit'} == 1) {
-                        ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $dow - 1);
-                    }
-                } else {
-                    $subscription->{'countissuesperunit'}++;
-                }
+                ($year, $month, $day) = _get_next_date_week($subscription,
+                    $freqdata, $year, $month, $day);
                 $issueno++;
             }
-            if ($subscription->{'countissuesperunit'} + 1 > $freqdata->{'issuesperunit'}){
-                $subscription->{'countissuesperunit'} = 1;
-                $wkno += $freqdata->{"unitsperissue"};
-                if($wkno > 52){
-                    $wkno = $wkno % 52 ;
-                    $yr++;
-                }
-                my $dow = Day_of_Week($year, $month, $day);
-                ($year,$month,$day) = Monday_of_Week($wkno, $yr);
-                if($freqdata->{'issuesperunit'} == 1) {
-                    ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $dow - 1);
-                }
-            } else {
-                $subscription->{'countissuesperunit'}++;
-            }
+            ($year, $month, $day) = _get_next_date_week($subscription,
+                $freqdata, $year, $month, $day);
         }
         elsif ($unit eq 'month') {
             while ($irregularities{$issueno}) {
-                if ($subscription->{'countissuesperunit'} + 1 > $freqdata->{'issuesperunit'}){
-                    $subscription->{'countissuesperunit'} = 1;
-                    ($year,$month,$day) = Add_Delta_YM($year,$month,$day, 0,$freqdata->{"unitsperissue"});
-                    unless($freqdata->{'issuesperunit'} == 1) {
-                        $day = 1;   # Jumping to the first day of month, because we don't know what day is expected
-                    }
-                } else {
-                    $subscription->{'countissuesperunit'}++;
-                }
+                ($year, $month, $day) = _get_next_date_month($subscription,
+                    $freqdata, $year, $month, $day);
                 $issueno++;
             }
-            if ($subscription->{'countissuesperunit'} + 1 > $freqdata->{'issuesperunit'}){
-                $subscription->{'countissuesperunit'} = 1;
-                ($year,$month,$day) = Add_Delta_YM($year,$month,$day, 0,$freqdata->{"unitsperissue"});
-                unless($freqdata->{'issuesperunit'} == 1) {
-                    $day = 1;   # Jumping to the first day of month, because we don't know what day is expected
-                }
-            } else {
-                $subscription->{'countissuesperunit'}++;
-            }
+            ($year, $month, $day) = _get_next_date_month($subscription,
+                $freqdata, $year, $month, $day);
         }
         elsif ($unit eq 'year') {
             while ($irregularities{$issueno}) {
-                if ($subscription->{'countissuesperunit'} + 1 > $freqdata->{'issuesperunit'}){
-                    $subscription->{'countissuesperunit'} = 1;
-                    ($year,$month,$day) = Add_Delta_YM($year,$month,$day, $freqdata->{"unitsperissue"},0);
-                    unless($freqdata->{'issuesperunit'} == 1) {
-                        # Jumping to the first day of year, because we don't know what day is expected
-                        $month = 1;
-                        $day = 1;
-                    }
-                } else {
-                    $subscription->{'countissuesperunit'}++;
-                }
+                ($year, $month, $day) = _get_next_date_year($subscription,
+                    $freqdata, $year, $month, $day);
                 $issueno++;
             }
-            if ($subscription->{'countissuesperunit'} + 1 > $freqdata->{'issuesperunit'}){
-                $subscription->{'countissuesperunit'} = 1;
-                ($year,$month,$day) = Add_Delta_YM($year,$month,$day, $freqdata->{"unitsperissue"},0);
-                unless($freqdata->{'issuesperunit'} == 1) {
-                    # Jumping to the first day of year, because we don't know what day is expected
-                    $month = 1;
-                    $day = 1;
-                }
-            } else {
-                $subscription->{'countissuesperunit'}++;
-            }
+            ($year, $month, $day) = _get_next_date_year($subscription,
+                $freqdata, $year, $month, $day);
         }
+
         if ($updatecount){
             my $dbh = C4::Context->dbh;
             my $query = qq{
@@ -2550,6 +2447,7 @@ sub GetNextDate {
             my $sth = $dbh->prepare($query);
             $sth->execute($subscription->{'countissuesperunit'}, $subscription->{'subscriptionid'});
         }
+
         return sprintf("%04d-%02d-%02d", $year, $month, $day);
     }
 }
@@ -2561,8 +2459,11 @@ sub GetNextDate {
 _numeration returns the string corresponding to $value in the num_type
 num_type can take :
     -dayname
+    -dayabrv
     -monthname
+    -monthabrv
     -season
+    -seasonabrv
 =cut
 
 #'
@@ -2573,7 +2474,7 @@ sub _numeration {
     $num_type //= '';
     $locale ||= 'en';
     my $string;
-    if ( $num_type =~ /^dayname$/ ) {
+    if ( $num_type =~ /^dayname$/ or $num_type =~ /^dayabrv$/ ) {
         # 1970-11-01 was a Sunday
         $value = $value % 7;
         my $dt = DateTime->new(
@@ -2582,19 +2483,27 @@ sub _numeration {
             day     => $value + 1,
             locale  => $locale,
         );
-        $string = $dt->strftime("%A");
-    } elsif ( $num_type =~ /^monthname$/ ) {
+        $string = $num_type =~ /^dayname$/
+            ? $dt->strftime("%A")
+            : $dt->strftime("%a");
+    } elsif ( $num_type =~ /^monthname$/ or $num_type =~ /^monthabrv$/ ) {
         $value = $value % 12;
         my $dt = DateTime->new(
             year    => 1970,
             month   => $value + 1,
             locale  => $locale,
         );
-        $string = $dt->strftime("%B");
+        $string = $num_type =~ /^monthname$/
+            ? $dt->strftime("%B")
+            : $dt->strftime("%b");
     } elsif ( $num_type =~ /^season$/ ) {
         my @seasons= qw( Spring Summer Fall Winter );
         $value = $value % 4;
         $string = $seasons[$value];
+    } elsif ( $num_type =~ /^seasonabrv$/ ) {
+        my @seasonsabrv= qw( Spr Sum Fal Win );
+        $value = $value % 4;
+        $string = $seasonsabrv[$value];
     } else {
         $string = $value;
     }
@@ -2604,7 +2513,7 @@ sub _numeration {
 
 =head2 is_barcode_in_use
 
-Returns number of occurence of the barcode in the items table
+Returns number of occurrences of the barcode in the items table
 Can be used as a boolean test of whether the barcode has
 been deployed as yet
 
@@ -2613,13 +2522,13 @@ been deployed as yet
 sub is_barcode_in_use {
     my $barcode = shift;
     my $dbh       = C4::Context->dbh;
-    my $occurences = $dbh->selectall_arrayref(
+    my $occurrences = $dbh->selectall_arrayref(
         'SELECT itemnumber from items where barcode = ?',
         {}, $barcode
 
     );
 
-    return @{$occurences};
+    return @{$occurrences};
 }
 
 =head2 CloseSubscription
@@ -2629,7 +2538,7 @@ sub CloseSubscription {
     my ( $subscriptionid ) = @_;
     return unless $subscriptionid;
     my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare( qq{
+    my $sth = $dbh->prepare( q{
         UPDATE subscription
         SET closed = 1
         WHERE subscriptionid = ?
@@ -2637,13 +2546,13 @@ sub CloseSubscription {
     $sth->execute( $subscriptionid );
 
     # Set status = missing when status = stopped
-    $sth = $dbh->prepare( qq{
+    $sth = $dbh->prepare( q{
         UPDATE serial
-        SET status = 8
+        SET status = ?
         WHERE subscriptionid = ?
-        AND status = 1
+        AND status = ?
     } );
-    $sth->execute( $subscriptionid );
+    $sth->execute( STOPPED, $subscriptionid, EXPECTED );
 }
 
 =head2 ReopenSubscription
@@ -2653,7 +2562,7 @@ sub ReopenSubscription {
     my ( $subscriptionid ) = @_;
     return unless $subscriptionid;
     my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare( qq{
+    my $sth = $dbh->prepare( q{
         UPDATE subscription
         SET closed = 0
         WHERE subscriptionid = ?
@@ -2661,13 +2570,13 @@ sub ReopenSubscription {
     $sth->execute( $subscriptionid );
 
     # Set status = expected when status = stopped
-    $sth = $dbh->prepare( qq{
+    $sth = $dbh->prepare( q{
         UPDATE serial
-        SET status = 1
+        SET status = ?
         WHERE subscriptionid = ?
-        AND status = 8
+        AND status = ?
     } );
-    $sth->execute( $subscriptionid );
+    $sth->execute( EXPECTED, $subscriptionid, STOPPED );
 }
 
 =head2 subscriptionCurrentlyOnOrder
@@ -2692,6 +2601,19 @@ sub subscriptionCurrentlyOnOrder {
     return $sth->fetchrow_array;
 }
 
+=head2 can_claim_subscription
+
+    $can = can_claim_subscription( $subscriptionid[, $userid] );
+
+Return 1 if the subscription can be claimed by the current logged user (or a given $userid), else 0.
+
+=cut
+
+sub can_claim_subscription {
+    my ( $subscription, $userid ) = @_;
+    return _can_do_on_subscription( $subscription, $userid, 'claim_serials' );
+}
+
 =head2 can_edit_subscription
 
     $can = can_edit_subscription( $subscriptionid[, $userid] );
@@ -2751,6 +2673,25 @@ sub _can_do_on_subscription {
     return 0;
 }
 
+=head2 findSerialsByStatus
+
+    @serials = findSerialsByStatus($status, $subscriptionid);
+
+    Returns an array of serials matching a given status and subscription id.
+
+=cut
+
+sub findSerialsByStatus {
+    my ( $status, $subscriptionid ) = @_;
+    my $dbh   = C4::Context->dbh;
+    my $query = q| SELECT * from serial
+                    WHERE status = ?
+                    AND subscriptionid = ?
+                |;
+    my $serials = $dbh->selectall_arrayref( $query, { Slice => {} }, $status, $subscriptionid );
+    return @$serials;
+}
+
 1;
 __END__