Bug 10855: Squash several fixes
[koha.git] / C4 / Serials.pm
index 3cbd042..0e4e71b 100644 (file)
@@ -5,38 +5,64 @@ package C4::Serials;
 #
 # This file is part of Koha.
 #
-# Koha is free software; you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
 #
-# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
 #
-# You should have received a copy of the GNU General Public License along
-# with Koha; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 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 setlocale LC_TIME);
+use POSIX qw(strftime);
 use C4::Biblio;
 use C4::Log;    # logaction
 use C4::Debug;
 use C4::Serials::Frequency;
 use C4::Serials::Numberpattern;
+use Koha::AdditionalField;
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 
+# Define statuses
+use constant {
+    EXPECTED               => 1,
+    ARRIVED                => 2,
+    LATE                   => 3,
+    MISSING                => 4,
+    MISSING_NEVER_RECIEVED => 41,
+    MISSING_SOLD_OUT       => 42,
+    MISSING_DAMAGED        => 43,
+    MISSING_LOST           => 44,
+    NOT_ISSUED             => 5,
+    DELETED                => 6,
+    CLAIMED                => 7,
+    STOPPED                => 8,
+};
+
+use constant MISSING_STATUSES => (
+    MISSING,          MISSING_NEVER_RECIEVED,
+    MISSING_SOLD_OUT, MISSING_DAMAGED,
+    MISSING_LOST
+);
+
 BEGIN {
     $VERSION = 3.07.00.049;    # set version for version checking
     require Exporter;
     @ISA    = qw(Exporter);
     @EXPORT = qw(
-      &NewSubscription    &ModSubscription    &DelSubscription    &GetSubscriptions
+      &NewSubscription    &ModSubscription    &DelSubscription
       &GetSubscription    &CountSubscriptionFromBiblionumber      &GetSubscriptionsFromBiblionumber
       &SearchSubscriptions
       &GetFullSubscriptionsFromBiblionumber   &GetFullSubscription &ModSubscriptionHistory
@@ -45,7 +71,7 @@ BEGIN {
 
       &GetNextSeq &GetSeq &NewIssue           &ItemizeSerials    &GetSerials
       &GetLatestSerials   &ModSerialStatus    &GetNextDate       &GetSerials2
-      &ReNewSubscription  &GetLateIssues      &GetLateOrMissingIssues
+      &ReNewSubscription  &GetLateOrMissingIssues
       &GetSerialInformation                   &AddItem2Serial
       &PrepareSerialsData &GetNextExpected    &ModNextExpected
 
@@ -54,10 +80,11 @@ BEGIN {
       &GetDistributedTo   &SetDistributedTo
       &getroutinglist     &delroutingmember   &addroutingmember
       &reorder_members
-      &check_routing &updateClaim &removeMissingIssue
+      &check_routing &updateClaim
       &CountIssues
       HasItems
       &GetSubscriptionsFromBorrower
+      &subscriptionCurrentlyOnOrder
 
     );
 }
@@ -91,78 +118,22 @@ the array is in name order
 
 sub GetSuppliersWithLateIssues {
     my $dbh   = C4::Context->dbh;
+    my $statuses = join(',', ( LATE, MISSING_STATUSES, CLAIMED ) );
     my $query = qq|
-        SELECT DISTINCT id, name
+    SELECT DISTINCT id, name
     FROM            subscription
     LEFT JOIN       serial ON serial.subscriptionid=subscription.subscriptionid
     LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
     WHERE id > 0
         AND (
             (planneddate < now() AND serial.status=1)
-            OR serial.STATUS = 3 OR serial.STATUS = 4
+            OR serial.STATUS IN ( $statuses )
         )
         AND subscription.closed = 0
     ORDER BY name|;
     return $dbh->selectall_arrayref($query, { Slice => {} });
 }
 
-=head2 GetLateIssues
-
-@issuelist = GetLateIssues($supplierid)
-
-this function selects late issues from the database
-
-return :
-the issuelist as an array. Each element of this array contains a hashi_ref containing
-name,title,planneddate,serialseq,serial.subscriptionid from tables : subscription, serial & biblio
-
-=cut
-
-sub GetLateIssues {
-    my ($supplierid) = @_;
-    my $dbh = C4::Context->dbh;
-    my $sth;
-    if ($supplierid) {
-        my $query = qq|
-            SELECT     name,title,planneddate,serialseq,serial.subscriptionid
-            FROM       subscription
-            LEFT JOIN  serial ON subscription.subscriptionid = serial.subscriptionid
-            LEFT JOIN  biblio ON biblio.biblionumber = subscription.biblionumber
-            LEFT JOIN  aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
-            WHERE      ((planneddate < now() AND serial.STATUS =1) OR serial.STATUS = 3)
-            AND        subscription.aqbooksellerid=?
-            AND        subscription.closed = 0
-            ORDER BY   title
-        |;
-        $sth = $dbh->prepare($query);
-        $sth->execute($supplierid);
-    } else {
-        my $query = qq|
-            SELECT     name,title,planneddate,serialseq,serial.subscriptionid
-            FROM       subscription
-            LEFT JOIN  serial ON subscription.subscriptionid = serial.subscriptionid
-            LEFT JOIN  biblio ON biblio.biblionumber = subscription.biblionumber
-            LEFT JOIN  aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
-            WHERE      ((planneddate < now() AND serial.STATUS =1) OR serial.STATUS = 3)
-            AND        subscription.closed = 0
-            ORDER BY   title
-        |;
-        $sth = $dbh->prepare($query);
-        $sth->execute;
-    }
-    my @issuelist;
-    my $last_title;
-    my $odd   = 0;
-    while ( my $line = $sth->fetchrow_hashref ) {
-        $odd++ unless $line->{title} eq $last_title;
-        $line->{title} = "" if $line->{title} eq $last_title;
-        $last_title = $line->{title} if ( $line->{title} );
-        $line->{planneddate} = format_date( $line->{planneddate} );
-        push @issuelist, $line;
-    }
-    return @issuelist;
-}
-
 =head2 GetSubscriptionHistoryFromSubscriptionId
 
 $history = GetSubscriptionHistoryFromSubscriptionId($subscriptionid);
@@ -226,15 +197,7 @@ sub GetSerialInformation {
     my ($serialid) = @_;
     my $dbh        = C4::Context->dbh;
     my $query      = qq|
-        SELECT serial.*, serial.notes as sernotes, serial.status as serstatus,subscription.*,subscription.subscriptionid as subsid |;
-    if (   C4::Context->preference('IndependantBranches')
-        && C4::Context->userenv
-        && C4::Context->userenv->{'flags'} != 1
-        && C4::Context->userenv->{'branch'} ) {
-        $query .= "
-      , ((subscription.branchcode <>\"" . C4::Context->userenv->{'branch'} . "\") and subscription.branchcode <>\"\" and subscription.branchcode IS NOT NULL) as cannotedit ";
-    }
-    $query .= qq|             
+        SELECT serial.*, serial.notes as sernotes, serial.status as serstatus,subscription.*,subscription.subscriptionid as subsid
         FROM   serial LEFT JOIN subscription ON subscription.subscriptionid=serial.subscriptionid
         WHERE  serialid = ?
     |;
@@ -273,6 +236,7 @@ sub GetSerialInformation {
     $data->{ "status" . $data->{'serstatus'} } = 1;
     $data->{'subscriptionexpired'} = HasSubscriptionExpired( $data->{'subscriptionid'} ) && $data->{'status'} == 1;
     $data->{'abouttoexpire'} = abouttoexpire( $data->{'subscriptionid'} );
+    $data->{cannotedit} = not can_edit_subscription( $data );
     return $data;
 }
 
@@ -286,6 +250,9 @@ returns the number of rows affected
 
 sub AddItem2Serial {
     my ( $serialid, $itemnumber ) = @_;
+
+    return unless ($serialid and $itemnumber);
+
     my $dbh = C4::Context->dbh;
     my $rq  = $dbh->prepare("INSERT INTO `serialitems` SET serialid=? , itemnumber=?");
     $rq->execute( $serialid, $itemnumber );
@@ -303,13 +270,20 @@ Update Claimdate for issues in @$serialids list with date $date
 
 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
-        WHERE  serialid in (" . join( ",", map { '?' } @$serialids ) . ")";
+        UPDATE serial
+        SET claimdate = ?,
+            status = ?,
+            claims_count = claims_count + 1
+        WHERE  serialid in (" . join( ",", map { '?' } @$serialids ) . ")
+    ";
     my $rq = $dbh->prepare($query);
-    $rq->execute($date, @$serialids);
+    $rq->execute($date, CLAIMED, @$serialids);
     return $rq->rows;
 }
 
@@ -331,15 +305,7 @@ sub GetSubscription {
                 subscriptionhistory.*,
                 aqbooksellers.name AS aqbooksellername,
                 biblio.title AS bibliotitle,
-                subscription.biblionumber as bibnum);
-    if (   C4::Context->preference('IndependantBranches')
-        && C4::Context->userenv
-        && C4::Context->userenv->{'flags'} != 1
-        && C4::Context->userenv->{'branch'} ) {
-        $query .= "
-      , ((subscription.branchcode <>\"" . C4::Context->userenv->{'branch'} . "\") and subscription.branchcode <>\"\" and subscription.branchcode IS NOT NULL) as cannotedit ";
-    }
-    $query .= qq(             
+                subscription.biblionumber as bibnum
        FROM subscription
        LEFT JOIN subscriptionhistory ON subscription.subscriptionid=subscriptionhistory.subscriptionid
        LEFT JOIN aqbooksellers ON subscription.aqbooksellerid=aqbooksellers.id
@@ -347,16 +313,21 @@ sub GetSubscription {
        WHERE subscription.subscriptionid = ?
     );
 
-    #     if (C4::Context->preference('IndependantBranches') &&
-    #         C4::Context->userenv &&
-    #         C4::Context->userenv->{'flags'} != 1){
-    # #       $debug and warn "flags: ".C4::Context->userenv->{'flags'};
-    #       $query.=" AND subscription.branchcode IN ('".C4::Context->userenv->{'branch'}."',\"\")";
-    #     }
     $debug and warn "query : $query\nsubsid :$subscriptionid";
     my $sth = $dbh->prepare($query);
     $sth->execute($subscriptionid);
-    return $sth->fetchrow_hashref;
+    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;
 }
 
 =head2 GetFullSubscription
@@ -368,6 +339,9 @@ sub GetSubscription {
 
 sub GetFullSubscription {
     my ($subscriptionid) = @_;
+
+    return unless ($subscriptionid);
+
     my $dbh              = C4::Context->dbh;
     my $query            = qq|
   SELECT    serial.serialid,
@@ -380,15 +354,7 @@ sub GetFullSubscription {
             aqbooksellers.name as aqbooksellername,
             biblio.title as bibliotitle,
             subscription.branchcode AS branchcode,
-            subscription.subscriptionid AS subscriptionid |;
-    if (   C4::Context->preference('IndependantBranches')
-        && C4::Context->userenv
-        && C4::Context->userenv->{'flags'} != 1
-        && C4::Context->userenv->{'branch'} ) {
-        $query .= "
-      , ((subscription.branchcode <>\"" . C4::Context->userenv->{'branch'} . "\") and subscription.branchcode <>\"\" and subscription.branchcode IS NOT NULL) as cannotedit ";
-    }
-    $query .= qq|
+            subscription.subscriptionid AS subscriptionid
   FROM      serial 
   LEFT JOIN subscription ON 
           (serial.subscriptionid=subscription.subscriptionid )
@@ -402,7 +368,11 @@ sub GetFullSubscription {
     $debug and warn "GetFullSubscription query: $query";
     my $sth = $dbh->prepare($query);
     $sth->execute($subscriptionid);
-    return $sth->fetchall_arrayref( {} );
+    my $subscriptions = $sth->fetchall_arrayref( {} );
+    for my $subscription ( @$subscriptions ) {
+        $subscription->{cannotedit} = not can_edit_subscription( $subscription );
+    }
+    return $subscriptions;
 }
 
 =head2 PrepareSerialsData
@@ -414,6 +384,9 @@ sub GetFullSubscription {
 
 sub PrepareSerialsData {
     my ($lines) = @_;
+
+    return unless ($lines);
+
     my %tmpresults;
     my $year;
     my @res;
@@ -426,16 +399,15 @@ sub PrepareSerialsData {
 
     foreach my $subs (@{$lines}) {
         for my $datefield ( qw(publisheddate planneddate) ) {
-            # handle both undef and undef returned as 0000-00-00
-            if (!defined $subs->{$datefield} or $subs->{$datefield}=~m/^00/) {
-                $subs->{$datefield} = 'XXX';
-            }
-            else {
-                $subs->{$datefield} = format_date( $subs->{$datefield}  );
+            # handle 0000-00-00 dates
+            if (defined $subs->{$datefield} and $subs->{$datefield} =~ m/^00/) {
+                $subs->{$datefield} = undef;
             }
         }
         $subs->{ "status" . $subs->{'status'} } = 1;
-        $subs->{"checked"}                      = $subs->{'status'} =~ /1|3|4|7/;
+        if ( grep { $_ == $subs->{status} } ( EXPECTED, LATE, MISSING_STATUSES, CLAIMED ) ) {
+            $subs->{"checked"} = 1;
+        }
 
         if ( $subs->{'year'} && $subs->{'year'} ne "" ) {
             $year = $subs->{'year'};
@@ -473,6 +445,9 @@ startdate, histstartdate,opacnote,missinglist,recievedlist,periodicity,status &
 
 sub GetSubscriptionsFromBiblionumber {
     my ($biblionumber) = @_;
+
+    return unless ($biblionumber);
+
     my $dbh            = C4::Context->dbh;
     my $query          = qq(
         SELECT subscription.*,
@@ -500,13 +475,6 @@ sub GetSubscriptionsFromBiblionumber {
         $subs->{ "periodicity" . $subs->{periodicity} }     = 1;
         $subs->{ "numberpattern" . $subs->{numberpattern} } = 1;
         $subs->{ "status" . $subs->{'status'} }             = 1;
-        $subs->{'cannotedit'} =
-          (      C4::Context->preference('IndependantBranches')
-              && C4::Context->userenv
-              && C4::Context->userenv->{flags} % 2 != 1
-              && C4::Context->userenv->{branch}
-              && $subs->{branchcode}
-              && ( C4::Context->userenv->{branch} ne $subs->{branchcode} ) );
 
         if ( $subs->{enddate} eq '0000-00-00' ) {
             $subs->{enddate} = '';
@@ -515,6 +483,7 @@ sub GetSubscriptionsFromBiblionumber {
         }
         $subs->{'abouttoexpire'}       = abouttoexpire( $subs->{'subscriptionid'} );
         $subs->{'subscriptionexpired'} = HasSubscriptionExpired( $subs->{'subscriptionid'} );
+        $subs->{cannotedit} = not can_edit_subscription( $subs );
         push @res, $subs;
     }
     return \@res;
@@ -540,16 +509,7 @@ sub GetFullSubscriptionsFromBiblionumber {
             year(IF(serial.publisheddate="00-00-0000",serial.planneddate,serial.publisheddate)) as year,
             biblio.title as bibliotitle,
             subscription.branchcode AS branchcode,
-            subscription.subscriptionid AS subscriptionid|;
-    if (   C4::Context->preference('IndependantBranches')
-        && C4::Context->userenv
-        && C4::Context->userenv->{'flags'} != 1
-        && C4::Context->userenv->{'branch'} ) {
-        $query .= "
-      , ((subscription.branchcode <>\"" . C4::Context->userenv->{'branch'} . "\") and subscription.branchcode <>\"\" and subscription.branchcode IS NOT NULL) as cannotedit ";
-    }
-
-    $query .= qq|      
+            subscription.subscriptionid AS subscriptionid
   FROM      serial 
   LEFT JOIN subscription ON 
           (serial.subscriptionid=subscription.subscriptionid)
@@ -562,125 +522,77 @@ sub GetFullSubscriptionsFromBiblionumber {
           |;
     my $sth = $dbh->prepare($query);
     $sth->execute($biblionumber);
-    return $sth->fetchall_arrayref( {} );
-}
-
-=head2 GetSubscriptions
-
-@results = GetSubscriptions($title,$ISSN,$ean,$biblionumber);
-this function gets all subscriptions which have title like $title,ISSN like $ISSN,EAN like $ean and biblionumber like $biblionumber.
-return:
-a table of hashref. Each hash containt the subscription.
-
-=cut
-
-sub GetSubscriptions {
-    my ( $string, $issn, $ean, $biblionumber ) = @_;
-
-    #return unless $title or $ISSN or $biblionumber;
-    my $dbh = C4::Context->dbh;
-    my $sth;
-    my $sql = qq(
-            SELECT subscriptionhistory.*, subscription.*, biblio.title,biblioitems.issn,biblio.biblionumber
-            FROM   subscription
-            LEFT JOIN subscriptionhistory USING(subscriptionid)
-            LEFT JOIN biblio ON biblio.biblionumber = subscription.biblionumber
-            LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
-    );
-    my @bind_params;
-    my $sqlwhere = q{};
-    if ($biblionumber) {
-        $sqlwhere = "   WHERE biblio.biblionumber=?";
-        push @bind_params, $biblionumber;
-    }
-    if ($string) {
-        my @sqlstrings;
-        my @strings_to_search;
-        @strings_to_search = map { "%$_%" } split( / /, $string );
-        foreach my $index (qw(biblio.title subscription.callnumber subscription.location subscription.notes subscription.internalnotes)) {
-            push @bind_params, @strings_to_search;
-            my $tmpstring = "AND $index LIKE ? " x scalar(@strings_to_search);
-            $debug && warn "$tmpstring";
-            $tmpstring =~ s/^AND //;
-            push @sqlstrings, $tmpstring;
-        }
-        $sqlwhere .= ( $sqlwhere ? " AND " : " WHERE " ) . "((" . join( ") OR (", @sqlstrings ) . "))";
-    }
-    if ($issn) {
-        my @sqlstrings;
-        my @strings_to_search;
-        @strings_to_search = map { "%$_%" } split( / /, $issn );
-        foreach my $index ( qw(biblioitems.issn subscription.callnumber)) {
-            push @bind_params, @strings_to_search;
-            my $tmpstring = "OR $index LIKE ? " x scalar(@strings_to_search);
-            $debug && warn "$tmpstring";
-            $tmpstring =~ s/^OR //;
-            push @sqlstrings, $tmpstring;
-        }
-        $sqlwhere .= ( $sqlwhere ? " AND " : " WHERE " ) . "((" . join( ") OR (", @sqlstrings ) . "))";
-    }
-    if ($ean) {
-        my @sqlstrings;
-        my @strings_to_search;
-        @strings_to_search = map { "$_" } split( / /, $ean );
-        foreach my $index ( qw(biblioitems.ean) ) {
-            push @bind_params, @strings_to_search;
-            my $tmpstring = "OR $index = ? " x scalar(@strings_to_search);
-            $debug && warn "$tmpstring";
-            $tmpstring =~ s/^OR //;
-            push @sqlstrings, $tmpstring;
-        }
-        $sqlwhere .= ( $sqlwhere ? " AND " : " WHERE " ) . "((" . join( ") OR (", @sqlstrings ) . "))";
-    }
-
-    $sql .= "$sqlwhere ORDER BY title";
-    $debug and warn "GetSubscriptions query: $sql params : ", join( " ", @bind_params );
-    $sth = $dbh->prepare($sql);
-    $sth->execute(@bind_params);
-    my @results;
-
-    while ( my $line = $sth->fetchrow_hashref ) {
-        $line->{'cannotedit'} =
-          (      C4::Context->preference('IndependantBranches')
-              && C4::Context->userenv
-              && C4::Context->userenv->{flags} % 2 != 1
-              && C4::Context->userenv->{branch}
-              && $line->{branchcode}
-              && ( C4::Context->userenv->{branch} ne $line->{branchcode} ) );
-        push @results, $line;
+    my $subscriptions = $sth->fetchall_arrayref( {} );
+    for my $subscription ( @$subscriptions ) {
+        $subscription->{cannotedit} = not can_edit_subscription( $subscription );
     }
-    return @results;
+    return $subscriptions;
 }
 
 =head2 SearchSubscriptions
 
-@results = SearchSubscriptions($args);
-$args is a hashref. Its keys can be contained: title, issn, ean, publisher, bookseller and branchcode
+  @results = SearchSubscriptions($args);
 
-this function gets all subscriptions which have title like $title, ISSN like $issn, EAN like $ean, publisher like $publisher, bookseller like $bookseller AND branchcode eq $branch.
+This function returns a list of hashrefs, one for each subscription
+that meets the conditions specified by the $args hashref.
 
-return:
-a table of hashref. Each hash containt the subscription.
+The valid search fields are:
+
+  biblionumber
+  title
+  issn
+  ean
+  callnumber
+  location
+  publisher
+  bookseller
+  branch
+  expiration_date
+  closed
+
+The expiration_date search field is special; it specifies the maximum
+subscription expiration date.
 
 =cut
 
 sub SearchSubscriptions {
     my ( $args ) = @_;
 
-    my $query = qq{
-        SELECT subscription.*, subscriptionhistory.*, biblio.*, biblioitems.issn
+    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.*,
+            subscription.*,
+            biblio.notes AS biblionotes,
+            biblio.title,
+            biblio.author,
+            biblio.biblionumber,
+            biblioitems.issn
         FROM subscription
             LEFT JOIN subscriptionhistory USING(subscriptionid)
             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);
@@ -701,6 +613,10 @@ sub SearchSubscriptions {
         push @where_strs, "biblioitems.ean LIKE ?";
         push @where_args, "%$args->{ean}%";
     }
+    if ( $args->{callnumber} ) {
+        push @where_strs, "subscription.callnumber LIKE ?";
+        push @where_args, "%$args->{callnumber}%";
+    }
     if( $args->{publisher} ){
         push @where_strs, "biblioitems.publishercode LIKE ?";
         push @where_args, "%$args->{publisher}%";
@@ -713,19 +629,45 @@ sub SearchSubscriptions {
         push @where_strs, "subscription.branchcode = ?";
         push @where_args, "$args->{branch}";
     }
+    if ( $args->{location} ) {
+        push @where_strs, "subscription.location = ?";
+        push @where_args, "$args->{location}";
+    }
+    if ( $args->{expiration_date} ) {
+        push @where_strs, "subscription.enddate <= ?";
+        push @where_args, "$args->{expiration_date}";
+    }
     if( defined $args->{closed} ){
         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};
+
     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;
 }
@@ -744,15 +686,19 @@ FIXME: We should return \@serials.
 
 sub GetSerials {
     my ( $subscriptionid, $count ) = @_;
+
+    return unless $subscriptionid;
+
     my $dbh = C4::Context->dbh;
 
     # status = 2 is "arrived"
     my $counter = 0;
     $count = 5 unless ($count);
     my @serials;
+    my $statuses = join( ',', ( ARRIVED, MISSING_STATUSES, NOT_ISSUED ) );
     my $query = "SELECT serialid,serialseq, status, publisheddate, planneddate,notes, routingnotes
                         FROM   serial
-                        WHERE  subscriptionid = ? AND status NOT IN (2,4,5) 
+                        WHERE  subscriptionid = ? AND status NOT IN ( $statuses )
                         ORDER BY IF(publisheddate<>'0000-00-00',publisheddate,planneddate) DESC";
     my $sth = $dbh->prepare($query);
     $sth->execute($subscriptionid);
@@ -773,7 +719,7 @@ sub GetSerials {
     $query = "SELECT   serialid,serialseq, status, planneddate, publisheddate,notes, routingnotes
        FROM     serial
        WHERE    subscriptionid = ?
-       AND      (status in (2,4,5))
+       AND      status IN ( $statuses )
        ORDER BY IF(publisheddate<>'0000-00-00',publisheddate,planneddate) DESC
       ";
     $sth = $dbh->prepare($query);
@@ -801,20 +747,27 @@ 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);
+
+    my $statuses_string = join ',', @$statuses;
+
     my $dbh   = C4::Context->dbh;
     my $query = qq|
                  SELECT   serialid,serialseq, status, planneddate, publisheddate,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";
@@ -826,7 +779,7 @@ sub GetSerials2 {
         $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
         # Format dates for display
         for my $datefield ( qw( planneddate publisheddate ) ) {
-            if ($line->{$datefield} =~m/^00/) {
+            if (!defined($line->{$datefield}) || $line->{$datefield} =~m/^00/) {
                 $line->{$datefield} = q{};
             }
             else {
@@ -849,13 +802,16 @@ a ref to an array which contains all of the latest serials stored into a hash.
 
 sub GetLatestSerials {
     my ( $subscriptionid, $limit ) = @_;
+
+    return unless ($subscriptionid and $limit);
+
     my $dbh = C4::Context->dbh;
 
-    # status = 2 is "arrived"
-    my $strsth = "SELECT   serialid,serialseq, status, planneddate, notes
+    my $statuses = join( ',', ( ARRIVED, MISSING_STATUSES ) );
+    my $strsth = "SELECT   serialid,serialseq, status, planneddate, publisheddate, notes
                         FROM     serial
                         WHERE    subscriptionid = ?
-                        AND      (status =2 or status=4)
+                        AND      status IN ($statuses)
                         ORDER BY publisheddate DESC LIMIT 0,$limit
                 ";
     my $sth = $dbh->prepare($strsth);
@@ -864,6 +820,7 @@ sub GetLatestSerials {
     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"} );
         push @serials, $line;
     }
 
@@ -880,7 +837,10 @@ This function returns the field distributedto for the subscription matching subs
 sub GetDistributedTo {
     my $dbh = C4::Context->dbh;
     my $distributedto;
-    my $subscriptionid = @_;
+    my ($subscriptionid) = @_;
+
+    return unless ($subscriptionid);
+
     my $query          = "SELECT distributedto FROM subscription WHERE subscriptionid=?";
     my $sth            = $dbh->prepare($query);
     $sth->execute($subscriptionid);
@@ -889,26 +849,35 @@ sub GetDistributedTo {
 
 =head2 GetNextSeq
 
-GetNextSeq($val)
-$val is a hashref containing all the attributes of the table 'subscription'
+    my (
+        $nextseq,       $newlastvalue1, $newlastvalue2, $newlastvalue3,
+        $newinnerloop1, $newinnerloop2, $newinnerloop3
+    ) = GetNextSeq( $subscription, $pattern, $planneddate );
+
+$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.
 This function get the next issue for the subscription given on input arg
-return:
-a list containing all the input params updated.
 
 =cut
 
 sub GetNextSeq {
-    my ($val, $planneddate) = @_;
-    my ( $calculated, $newlastvalue1, $newlastvalue2, $newlastvalue3,
+    my ($subscription, $pattern, $planneddate) = @_;
+
+    return unless ($subscription and $pattern);
+
+    my ( $newlastvalue1, $newlastvalue2, $newlastvalue3,
     $newinnerloop1, $newinnerloop2, $newinnerloop3 );
     my $count = 1;
 
-    if($val->{'skip_serialseq'}) {
-        my @irreg = split /;/, $val->{'irregularity'};
+    if ($subscription->{'skip_serialseq'}) {
+        my @irreg = split /;/, $subscription->{'irregularity'};
         if(@irreg > 0) {
             my $irregularities = {};
             $irregularities->{$_} = 1 foreach(@irreg);
-            my $issueno = GetFictiveIssueNumber($val, $planneddate) + 1;
+            my $issueno = GetFictiveIssueNumber($subscription, $planneddate) + 1;
             while($irregularities->{$issueno}) {
                 $count++;
                 $issueno++;
@@ -916,64 +885,67 @@ sub GetNextSeq {
         }
     }
 
-    my $pattern = $val->{numberpattern};
-    $calculated    = $val->{numberingmethod};
-    my $locale = $val->{locale};
-    $newlastvalue1 = $val->{lastvalue1} || 0;
-    $newlastvalue2 = $val->{lastvalue2} || 0;
-    $newlastvalue3 = $val->{lastvalue3} || 0;
-    $newinnerloop1 = $val->{innerloop1} || 0;
-    $newinnerloop2 = $val->{innerloop2} || 0;
-    $newinnerloop3 = $val->{innerloop3} || 0;
-    my %calc;
-    foreach(qw/X Y Z/) {
-        $calc{$_} = 1 if ($val->{'numberingmethod'} =~ /\{$_\}/);
-    }
+    my $numberingmethod = $pattern->{numberingmethod};
+    my $calculated = "";
+    if ($numberingmethod) {
+        $calculated    = $numberingmethod;
+        my $locale = $subscription->{locale};
+        $newlastvalue1 = $subscription->{lastvalue1} || 0;
+        $newlastvalue2 = $subscription->{lastvalue2} || 0;
+        $newlastvalue3 = $subscription->{lastvalue3} || 0;
+        $newinnerloop1 = $subscription->{innerloop1} || 0;
+        $newinnerloop2 = $subscription->{innerloop2} || 0;
+        $newinnerloop3 = $subscription->{innerloop3} || 0;
+        my %calc;
+        foreach(qw/X Y Z/) {
+            $calc{$_} = 1 if ($numberingmethod =~ /\{$_\}/);
+        }
 
-    for(my $i = 0; $i < $count; $i++) {
-        if($calc{'X'}) {
-            # check if we have to increase the new value.
-            $newinnerloop1 += 1;
-            if ($newinnerloop1 >= $val->{every1}) {
-                $newinnerloop1  = 0;
-                $newlastvalue1 += $val->{add1};
+        for(my $i = 0; $i < $count; $i++) {
+            if($calc{'X'}) {
+                # check if we have to increase the new value.
+                $newinnerloop1 += 1;
+                if ($newinnerloop1 >= $pattern->{every1}) {
+                    $newinnerloop1  = 0;
+                    $newlastvalue1 += $pattern->{add1};
+                }
+                # reset counter if needed.
+                $newlastvalue1 = $pattern->{setto1} if ($newlastvalue1 > $pattern->{whenmorethan1});
+            }
+            if($calc{'Y'}) {
+                # check if we have to increase the new value.
+                $newinnerloop2 += 1;
+                if ($newinnerloop2 >= $pattern->{every2}) {
+                    $newinnerloop2  = 0;
+                    $newlastvalue2 += $pattern->{add2};
+                }
+                # reset counter if needed.
+                $newlastvalue2 = $pattern->{setto2} if ($newlastvalue2 > $pattern->{whenmorethan2});
+            }
+            if($calc{'Z'}) {
+                # check if we have to increase the new value.
+                $newinnerloop3 += 1;
+                if ($newinnerloop3 >= $pattern->{every3}) {
+                    $newinnerloop3  = 0;
+                    $newlastvalue3 += $pattern->{add3};
+                }
+                # reset counter if needed.
+                $newlastvalue3 = $pattern->{setto3} if ($newlastvalue3 > $pattern->{whenmorethan3});
             }
-            # reset counter if needed.
-            $newlastvalue1 = $val->{setto1} if ($newlastvalue1 > $val->{whenmorethan1});
+        }
+        if($calc{'X'}) {
+            my $newlastvalue1string = _numeration( $newlastvalue1, $pattern->{numbering1}, $locale );
+            $calculated =~ s/\{X\}/$newlastvalue1string/g;
         }
         if($calc{'Y'}) {
-            # check if we have to increase the new value.
-            $newinnerloop2 += 1;
-            if ($newinnerloop2 >= $val->{every2}) {
-                $newinnerloop2  = 0;
-                $newlastvalue2 += $val->{add2};
-            }
-            # reset counter if needed.
-            $newlastvalue2 = $val->{setto2} if ($newlastvalue2 > $val->{whenmorethan2});
+            my $newlastvalue2string = _numeration( $newlastvalue2, $pattern->{numbering2}, $locale );
+            $calculated =~ s/\{Y\}/$newlastvalue2string/g;
         }
         if($calc{'Z'}) {
-            # check if we have to increase the new value.
-            $newinnerloop3 += 1;
-            if ($newinnerloop3 >= $val->{every3}) {
-                $newinnerloop3  = 0;
-                $newlastvalue3 += $val->{add3};
-            }
-            # reset counter if needed.
-            $newlastvalue3 = $val->{setto3} if ($newlastvalue3 > $val->{whenmorethan3});
+            my $newlastvalue3string = _numeration( $newlastvalue3, $pattern->{numbering3}, $locale );
+            $calculated =~ s/\{Z\}/$newlastvalue3string/g;
         }
     }
-    if($calc{'X'}) {
-        my $newlastvalue1string = _numeration( $newlastvalue1, $val->{numbering1}, $locale );
-        $calculated =~ s/\{X\}/$newlastvalue1string/g;
-    }
-    if($calc{'Y'}) {
-        my $newlastvalue2string = _numeration( $newlastvalue2, $val->{numbering2}, $locale );
-        $calculated =~ s/\{Y\}/$newlastvalue2string/g;
-    }
-    if($calc{'Z'}) {
-        my $newlastvalue3string = _numeration( $newlastvalue3, $val->{numbering3}, $locale );
-        $calculated =~ s/\{Z\}/$newlastvalue3string/g;
-    }
 
     return ($calculated,
             $newlastvalue1, $newlastvalue2, $newlastvalue3,
@@ -982,31 +954,34 @@ sub GetNextSeq {
 
 =head2 GetSeq
 
-$calculated = GetSeq($val)
-$val is a hashref containing all the attributes of the table 'subscription'
+$calculated = GetSeq($subscription, $pattern)
+$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'
 this function transforms {X},{Y},{Z} to 150,0,0 for example.
 return:
-the sequence in integer format
+the sequence in string format
 
 =cut
 
 sub GetSeq {
-    my ($val) = @_;
-    my $locale = $val->{locale};
+    my ($subscription, $pattern) = @_;
+
+    return unless ($subscription and $pattern);
 
-    my $pattern = $val->{numberpattern};
-    my $calculated = $val->{numberingmethod};
+    my $locale = $subscription->{locale};
 
-    my $newlastvalue1 = $val->{'lastvalue1'} || 0;
-    $newlastvalue1 = _numeration($newlastvalue1, $val->{numbering1}, $locale) if ($val->{numbering1}); # reset counter if needed.
+    my $calculated = $pattern->{numberingmethod};
+
+    my $newlastvalue1 = $subscription->{'lastvalue1'} || 0;
+    $newlastvalue1 = _numeration($newlastvalue1, $pattern->{numbering1}, $locale) if ($pattern->{numbering1}); # reset counter if needed.
     $calculated =~ s/\{X\}/$newlastvalue1/g;
 
-    my $newlastvalue2 = $val->{'lastvalue2'} || 0;
-    $newlastvalue2 = _numeration($newlastvalue2, $val->{numbering2}, $locale) if ($val->{numbering2}); # reset counter if needed.
+    my $newlastvalue2 = $subscription->{'lastvalue2'} || 0;
+    $newlastvalue2 = _numeration($newlastvalue2, $pattern->{numbering2}, $locale) if ($pattern->{numbering2}); # reset counter if needed.
     $calculated =~ s/\{Y\}/$newlastvalue2/g;
 
-    my $newlastvalue3 = $val->{'lastvalue3'} || 0;
-    $newlastvalue3 = _numeration($newlastvalue3, $val->{numbering3}, $locale) if ($val->{numbering3}); # reset counter if needed.
+    my $newlastvalue3 = $subscription->{'lastvalue3'} || 0;
+    $newlastvalue3 = _numeration($newlastvalue3, $pattern->{numbering3}, $locale) if ($pattern->{numbering3}); # reset counter if needed.
     $calculated =~ s/\{Z\}/$newlastvalue3/g;
     return $calculated;
 }
@@ -1024,6 +999,9 @@ the enddate or undef
 
 sub GetExpirationDate {
     my ( $subscriptionid, $startdate ) = @_;
+
+    return unless ($subscriptionid);
+
     my $dbh          = C4::Context->dbh;
     my $subscription = GetSubscription($subscriptionid);
     my $enddate;
@@ -1031,7 +1009,9 @@ sub GetExpirationDate {
     # we don't do the same test if the subscription is based on X numbers or on X weeks/months
     $enddate = $startdate || $subscription->{startdate};
     my @date = split( /-/, $enddate );
+
     return if ( scalar(@date) != 3 || not check_date(@date) );
+
     my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
     if ( $frequency and $frequency->{unit} ) {
 
@@ -1073,6 +1053,9 @@ the number of subscriptions
 
 sub CountSubscriptionFromBiblionumber {
     my ($biblionumber) = @_;
+
+    return unless ($biblionumber);
+
     my $dbh            = C4::Context->dbh;
     my $query          = "SELECT count(*) FROM subscription WHERE biblionumber=?";
     my $sth            = $dbh->prepare($query);
@@ -1092,6 +1075,9 @@ returns the number of rows affected
 
 sub ModSubscriptionHistory {
     my ( $subscriptionid, $histstartdate, $enddate, $receivedlist, $missinglist, $opacnote, $librariannote ) = @_;
+
+    return unless ($subscriptionid);
+
     my $dbh   = C4::Context->dbh;
     my $query = "UPDATE subscriptionhistory 
                     SET histstartdate=?,histenddate=?,recievedlist=?,missinglist=?,opacnote=?,librariannote=?
@@ -1105,50 +1091,6 @@ sub ModSubscriptionHistory {
     return $sth->rows;
 }
 
-# Update missinglist field, used by ModSerialStatus
-sub _update_missinglist {
-    my $subscriptionid = shift;
-
-    my $dbh = C4::Context->dbh;
-    my @missingserials = GetSerials2($subscriptionid, "4,5");
-    my $missinglist;
-    foreach (@missingserials) {
-        if($_->{'status'} == 4) {
-            $missinglist .= $_->{'serialseq'} . "; ";
-        } elsif($_->{'status'} == 5) {
-            $missinglist .= "not issued " . $_->{'serialseq'} . "; ";
-        }
-    }
-    $missinglist =~ s/; $//;
-    my $query = qq{
-        UPDATE subscriptionhistory
-        SET missinglist = ?
-        WHERE subscriptionid = ?
-    };
-    my $sth = $dbh->prepare($query);
-    $sth->execute($missinglist, $subscriptionid);
-}
-
-# Update recievedlist field, used by ModSerialStatus
-sub _update_receivedlist {
-    my $subscriptionid = shift;
-
-    my $dbh = C4::Context->dbh;
-    my @receivedserials = GetSerials2($subscriptionid, "2");
-    my $receivedlist;
-    foreach (@receivedserials) {
-        $receivedlist .= $_->{'serialseq'} . "; ";
-    }
-    $receivedlist =~ s/; $//;
-    my $query = qq{
-        UPDATE subscriptionhistory
-        SET recievedlist = ?
-        WHERE subscriptionid = ?
-    };
-    my $sth = $dbh->prepare($query);
-    $sth->execute($receivedlist, $subscriptionid);
-}
-
 =head2 ModSerialStatus
 
 ModSerialStatus($serialid,$serialseq, $planneddate,$publisheddate,$status,$notes)
@@ -1161,6 +1103,7 @@ Note : if we change from "waited" to something else,then we will have to create
 sub ModSerialStatus {
     my ( $serialid, $serialseq, $planneddate, $publisheddate, $status, $notes ) = @_;
 
+    return unless ($serialid);
 
     #It is a usual serial
     # 1st, get previous status :
@@ -1176,14 +1119,10 @@ sub ModSerialStatus {
 
     # change status & update subscriptionhistory
     my $val;
-    if ( $status == 6 ) {
+    if ( $status == DELETED ) {
         DelIssue( { 'serialid' => $serialid, 'subscriptionid' => $subscriptionid, 'serialseq' => $serialseq } );
     } else {
 
-        unless ($frequency->{'unit'}) {
-            if ( not $planneddate or $planneddate eq '0000-00-00' ) { $planneddate = C4::Dates->new()->output('iso') };
-            if ( not $publisheddate or $publisheddate eq '0000-00-00' ) { $publisheddate = C4::Dates->new()->output('iso') };
-        }
         my $query = 'UPDATE serial SET serialseq=?,publisheddate=?,planneddate=?,status=?,notes=? WHERE  serialid = ?';
         $sth = $dbh->prepare($query);
         $sth->execute( $serialseq, $publisheddate, $planneddate, $status, $notes, $serialid );
@@ -1192,47 +1131,59 @@ sub ModSerialStatus {
         $sth->execute($subscriptionid);
         my $val = $sth->fetchrow_hashref;
         unless ( $val->{manualhistory} ) {
-            if ( $status == 2 || ($oldstatus == 2 && $status != 2) ) {
-                  _update_receivedlist($subscriptionid);
+            $query = "SELECT missinglist,recievedlist FROM subscriptionhistory WHERE  subscriptionid=?";
+            $sth   = $dbh->prepare($query);
+            $sth->execute($subscriptionid);
+            my ( $missinglist, $recievedlist ) = $sth->fetchrow;
+
+            if ( $status == ARRIVED || ($oldstatus == ARRIVED && $status != ARRIVED) ) {
+                $recievedlist .= "; $serialseq"
+                    if ($recievedlist !~ /(^|;)\s*$serialseq(?=;|$)/);
             }
-            if($status == 4 || $status == 5
-              || ($oldstatus == 4 && $status != 4)
-              || ($oldstatus == 5 && $status != 5)) {
-                _update_missinglist($subscriptionid);
+
+            # in case serial has been previously marked as missing
+            if (grep /$status/, (EXPECTED, ARRIVED, LATE, CLAIMED)) {
+                $missinglist=~ s/(^|;)\s*$serialseq(?=;|$)//g;
             }
+
+            $missinglist .= "; $serialseq"
+                if ( ( grep { $_ == $status } ( MISSING_STATUSES ) ) && ( $missinglist !~/(^|;)\s*$serialseq(?=;|$)/ ) );
+            $missinglist .= "; not issued $serialseq"
+                if ( $status == NOT_ISSUED && $missinglist !~ /(^|;)\s*$serialseq(?=;|$)/ );
+
+            $query = "UPDATE subscriptionhistory SET recievedlist=?, missinglist=? WHERE  subscriptionid=?";
+            $sth   = $dbh->prepare($query);
+            $recievedlist =~ s/^; //;
+            $missinglist  =~ s/^; //;
+            $sth->execute( $recievedlist, $missinglist, $subscriptionid );
         }
     }
 
     # create new waited entry if needed (ie : was a "waited" and has changed)
-    if ( $oldstatus == 1 && $status != 1 ) {
-        my $query = qq{
-            SELECT subscription.*, subscription_numberpatterns.*,
-                   subscription_frequencies.*
-            FROM subscription
-            LEFT JOIN subscription_numberpatterns ON subscription.numberpattern = subscription_numberpatterns.id
-            LEFT JOIN subscription_frequencies ON subscription.periodicity = subscription_frequencies.id
-            WHERE subscriptionid = ?
-        };
-        $sth = $dbh->prepare($query);
-        $sth->execute($subscriptionid);
-        my $val = $sth->fetchrow_hashref;
+    if ( $oldstatus == EXPECTED && $status != EXPECTED ) {
+        my $subscription = GetSubscription($subscriptionid);
+        my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
 
         # next issue number
-        my ( $newserialseq, $newlastvalue1, $newlastvalue2, $newlastvalue3, $newinnerloop1, $newinnerloop2, $newinnerloop3 ) = GetNextSeq($val, $publisheddate);
+        my (
+            $newserialseq,  $newlastvalue1, $newlastvalue2, $newlastvalue3,
+            $newinnerloop1, $newinnerloop2, $newinnerloop3
+          )
+          = GetNextSeq( $subscription, $pattern, $publisheddate );
 
         # next date (calculated from actual date & frequency parameters)
-        my $nextpublisheddate = GetNextDate($val, $publisheddate, 1);
+        my $nextpublisheddate = GetNextDate($subscription, $publisheddate, 1);
         my $nextpubdate = $nextpublisheddate;
-        NewIssue( $newserialseq, $subscriptionid, $val->{'biblionumber'}, 1, $nextpubdate, $nextpubdate );
+        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 );
 
         # check if an alert must be sent... (= a letter is defined & status became "arrived"
-        if ( $val->{letter} && $status == 2 && $oldstatus != 2 ) {
+        if ( $subscription->{letter} && $status == ARRIVED && $oldstatus != ARRIVED ) {
             require C4::Letters;
-            C4::Letters::SendAlerts( 'issue', $val->{subscriptionid}, $val->{letter} );
+            C4::Letters::SendAlerts( 'issue', $serialid, $subscription->{letter} );
         }
     }
 
@@ -1267,15 +1218,15 @@ 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{
             SELECT *
             FROM serial
             WHERE subscriptionid = ?
-            ORDER BY planneddate DESC
+            ORDER BY publisheddate DESC
             LIMIT 1
         };
         $sth = $dbh->prepare($query);
@@ -1315,15 +1266,15 @@ 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;
 
 }
 
 =head2 GetSubscriptionIrregularities
 
-=over4
+=over 4
 
 =item @irreg = &GetSubscriptionIrregularities($subscriptionid);
 get the list of irregularities for a subscription
@@ -1335,7 +1286,7 @@ get the list of irregularities for a subscription
 sub GetSubscriptionIrregularities {
     my $subscriptionid = shift;
 
-    return undef unless $subscriptionid;
+    return unless $subscriptionid;
 
     my $dbh = C4::Context->dbh;
     my $query = qq{
@@ -1405,10 +1356,10 @@ sub ModSubscription {
 =head2 NewSubscription
 
 $subscriptionid = &NewSubscription($auser,branchcode,$aqbooksellerid,$cost,$aqbudgetid,$biblionumber,
-    $startdate,$periodicity,$dow,$numberlength,$weeklength,$monthlength,
+    $startdate,$periodicity,$numberlength,$weeklength,$monthlength,
     $lastvalue1,$innerloop1,$lastvalue2,$innerloop2,$lastvalue3,$innerloop3,
     $status, $notes, $letter, $firstacquidate, $irregularity, $numberpattern,
-    $callnumber, $hemisphere, $manualhistory, $internalnotes, $serialsadditems,
+    $locale, $callnumber, $manualhistory, $internalnotes, $serialsadditems,
     $staffdisplaycount, $opacdisplaycount, $graceperiod, $location, $enddate, $skip_serialseq);
 
 Create a new subscription with value given on input args.
@@ -1468,32 +1419,25 @@ sub NewSubscription {
     # then create the 1st expected number
     $query = qq(
         INSERT INTO subscriptionhistory
-            (biblionumber, subscriptionid, histstartdate,  opacnote, librariannote)
-        VALUES (?,?,?,?,?)
+            (biblionumber, subscriptionid, histstartdate)
+        VALUES (?,?,?)
         );
     $sth = $dbh->prepare($query);
-    $sth->execute( $biblionumber, $subscriptionid, $startdate, $notes, $internalnotes );
+    $sth->execute( $biblionumber, $subscriptionid, $startdate);
 
     # reread subscription to get a hash (for calculation of the 1st issue number)
-    $query = qq(
-        SELECT *
-        FROM   subscription
-        LEFT JOIN subscription_numberpatterns ON subscription.numberpattern = subscription_numberpatterns.id
-        WHERE  subscriptionid = ?
-    );
-    $sth = $dbh->prepare($query);
-    $sth->execute($subscriptionid);
-    my $val = $sth->fetchrow_hashref;
+    my $subscription = GetSubscription($subscriptionid);
+    my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
 
     # calculate issue number
-    my $serialseq = GetSeq($val);
+    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 );
+    $sth->execute( $serialseq, $subscriptionid, $biblionumber, EXPECTED, $firstacquidate, $firstacquidate );
 
     logaction( "SERIAL", "ADD", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
 
@@ -1548,7 +1492,7 @@ sub ReNewSubscription {
     # renew subscription
     $query = qq|
         UPDATE subscription
-        SET    startdate=?,numberlength=?,weeklength=?,monthlength=?
+        SET    startdate=?,numberlength=?,weeklength=?,monthlength=?,reneweddate=NOW()
         WHERE  subscriptionid=?
     |;
     $sth = $dbh->prepare($query);
@@ -1588,6 +1532,8 @@ sub NewIssue {
     my ( $serialseq, $subscriptionid, $biblionumber, $status, $planneddate, $publisheddate, $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
@@ -1606,13 +1552,13 @@ sub NewIssue {
     $sth->execute($subscriptionid);
     my ( $missinglist, $recievedlist ) = $sth->fetchrow;
 
-    if ( $status == 2 ) {
+    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 ) {
+    if ( grep {/^$status$/} ( MISSING_STATUSES ) ) {
         $missinglist .= "; $serialseq" unless (index($missinglist,$serialseq)>0);
     }
     $query = qq|
@@ -1640,6 +1586,9 @@ return :
 
 sub ItemizeSerials {
     my ( $serialid, $info ) = @_;
+
+    return unless ($serialid);
+
     my $now = POSIX::strftime( "%Y-%m-%d", localtime );
 
     my $dbh   = C4::Context->dbh;
@@ -1774,6 +1723,9 @@ sub HasSubscriptionStrictlyExpired {
 
     # Getting end of subscription date
     my ($subscriptionid) = @_;
+
+    return unless ($subscriptionid);
+
     my $dbh              = C4::Context->dbh;
     my $subscription     = GetSubscription($subscriptionid);
     my $expirationdate = $subscription->{enddate} || GetExpirationDate($subscriptionid);
@@ -1815,6 +1767,9 @@ return :
 
 sub HasSubscriptionExpired {
     my ($subscriptionid) = @_;
+
+    return unless ($subscriptionid);
+
     my $dbh              = C4::Context->dbh;
     my $subscription     = GetSubscription($subscriptionid);
     my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
@@ -1942,7 +1897,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 
@@ -1952,7 +1907,11 @@ name,title,planneddate,serialseq,serial.subscriptionid from tables : subscriptio
 
 sub GetLateOrMissingIssues {
     my ( $supplierid, $serialid, $order ) = @_;
+
+    return unless ( $supplierid or $serialid );
+
     my $dbh = C4::Context->dbh;
+
     my $sth;
     my $byserial = '';
     if ($serialid) {
@@ -1963,92 +1922,65 @@ sub GetLateOrMissingIssues {
     } else {
         $order = "title";
     }
+    my $missing_statuses_string = join ',', (MISSING_STATUSES);
     if ($supplierid) {
         $sth = $dbh->prepare(
             "SELECT
                 serialid,      aqbooksellerid,        name,
-                biblio.title,  planneddate,           serialseq,
-                serial.status, serial.subscriptionid, claimdate,
+                biblio.title,  biblioitems.issn,      planneddate,    serialseq,
+                serial.status, serial.subscriptionid, claimdate, claims_count,
                 subscription.branchcode
-            FROM      serial 
-                LEFT JOIN subscription  ON serial.subscriptionid=subscription.subscriptionid 
+            FROM      serial
+                LEFT JOIN subscription  ON serial.subscriptionid=subscription.subscriptionid
                 LEFT JOIN biblio        ON subscription.biblionumber=biblio.biblionumber
+                LEFT JOIN biblioitems   ON subscription.biblionumber=biblioitems.biblionumber
                 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
-                WHERE subscription.subscriptionid = serial.subscriptionid 
-                AND (serial.STATUS = 4 OR ((planneddate < now() AND serial.STATUS =1) OR serial.STATUS = 3 OR serial.STATUS = 7))
+                WHERE subscription.subscriptionid = serial.subscriptionid
+                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"
         );
     } else {
         $sth = $dbh->prepare(
-            "SELECT 
+            "SELECT
             serialid,      aqbooksellerid,         name,
             biblio.title,  planneddate,           serialseq,
-                serial.status, serial.subscriptionid, claimdate,
+                serial.status, serial.subscriptionid, claimdate, claims_count,
                 subscription.branchcode
-            FROM serial 
-                LEFT JOIN subscription ON serial.subscriptionid=subscription.subscriptionid 
+            FROM serial
+                LEFT JOIN subscription ON serial.subscriptionid=subscription.subscriptionid
                 LEFT JOIN biblio ON subscription.biblionumber=biblio.biblionumber
                 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
-                WHERE subscription.subscriptionid = serial.subscriptionid 
-                        AND (serial.STATUS = 4 OR ((planneddate < now() AND serial.STATUS =1) OR serial.STATUS = 3 OR serial.STATUS = 7))
+                WHERE subscription.subscriptionid = serial.subscriptionid
+                        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} );
         }
         if ($line->{claimdate} && $line->{claimdate} !~/^0+\-/) {
+            $line->{claimdateISO} = $line->{claimdate};
             $line->{claimdate}   = format_date( $line->{claimdate} );
         }
         $line->{"status".$line->{status}}   = 1;
-        push @issuelist, $line;
-    }
-    return @issuelist;
-}
-
-=head2 removeMissingIssue
-
-removeMissingIssue($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
+        my $additional_field_values = Koha::AdditionalField->fetch_all_values({
+            record_id => $line->{subscriptionid},
+            tablename => 'subscription'
+        });
+        %$line = ( %$line, additional_fields => $additional_field_values->{$line->{subscriptionid}} );
 
-=cut
-
-sub removeMissingIssue {
-    my ( $sequence, $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
@@ -2064,12 +1996,12 @@ called from claims.pl file
 sub updateClaim {
     my ($serialid) = @_;
     my $dbh        = C4::Context->dbh;
-    my $sth        = $dbh->prepare(
-        "UPDATE serial SET claimdate = now()
-                WHERE serialid = ?
-        "
-    );
-    $sth->execute($serialid);
+    $dbh->do(q|
+        UPDATE serial
+        SET claimdate = NOW(),
+            claims_count = claims_count + 1
+        WHERE serialid = ?
+    |, {}, $serialid );
     return;
 }
 
@@ -2111,6 +2043,9 @@ used to show either an 'add' or 'edit' link
 
 sub check_routing {
     my ($subscriptionid) = @_;
+
+    return unless ($subscriptionid);
+
     my $dbh              = C4::Context->dbh;
     my $sth              = $dbh->prepare(
         "SELECT count(routingid) routingids FROM subscription LEFT JOIN subscriptionroutinglist 
@@ -2136,6 +2071,9 @@ of either 1 or highest current rank + 1
 
 sub addroutingmember {
     my ( $borrowernumber, $subscriptionid ) = @_;
+
+    return unless ($borrowernumber and $subscriptionid);
+
     my $rank;
     my $dbh = C4::Context->dbh;
     my $sth = $dbh->prepare( "SELECT max(ranking) rank FROM subscriptionroutinglist WHERE subscriptionid = ?" );
@@ -2336,18 +2274,26 @@ sub abouttoexpire {
     my $per = $subscription->{'periodicity'};
     my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($per);
     if ($frequency and $frequency->{unit}){
+
         my $expirationdate = GetExpirationDate($subscriptionid);
+
         my ($res) = $dbh->selectrow_array('select max(planneddate) from serial where subscriptionid = ?', undef, $subscriptionid);
         my $nextdate = GetNextDate($subscription, $res);
-        if(Date::Calc::Delta_Days(
-            split( /-/, $nextdate ),
-            split( /-/, $expirationdate )
-        ) <= 0) {
-            return 1;
+
+        # only compare dates if both dates exist.
+        if ($nextdate and $expirationdate) {
+            if(Date::Calc::Delta_Days(
+                split( /-/, $nextdate ),
+                split( /-/, $expirationdate )
+            ) <= 0) {
+                return 1;
+            }
         }
+
     } elsif ($subscription->{numberlength}>0) {
         return (countissuesfrom($subscriptionid,$subscription->{'startdate'}) >=$subscription->{numberlength}-1);
     }
+
     return 0;
 }
 
@@ -2447,6 +2393,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)
@@ -2462,13 +2491,15 @@ skipped then the returned date will be 2007-05-10
 return :
 $resultdate - then next date in the sequence (ISO date)
 
-Return $publisheddate if subscription is irregular
+Return undef if subscription is irregular
 
 =cut
 
 sub GetNextDate {
     my ( $subscription, $publisheddate, $updatecount ) = @_;
 
+    return unless $subscription and $publisheddate;
+
     my $freqdata = GetSubscriptionFrequency($subscription->{'periodicity'});
 
     if ($freqdata->{'unit'}) {
@@ -2479,10 +2510,12 @@ sub GetNextDate {
         # irreg1;irreg2;irreg3
         # where irregX is the number of issue which will not be received
         # (the first issue takes the number 1, the 2nd the number 2 and so on)
-        my @irreg = split /;/, $subscription->{'irregularity'} ;
         my %irregularities;
-        foreach my $irregularity (@irreg) {
-            $irregularities{$irregularity} = 1;
+        if ( $subscription->{irregularity} ) {
+            my @irreg = split /;/, $subscription->{'irregularity'} ;
+            foreach my $irregularity (@irreg) {
+                $irregularities{$irregularity} = 1;
+            }
         }
 
         # Get the 'fictive' next issue number
@@ -2493,107 +2526,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{
@@ -2604,11 +2571,9 @@ sub GetNextDate {
             my $sth = $dbh->prepare($query);
             $sth->execute($subscription->{'countissuesperunit'}, $subscription->{'subscriptionid'});
         }
+
         return sprintf("%04d-%02d-%02d", $year, $month, $day);
     }
-    else {
-        return $publisheddate;
-    }
 }
 
 =head2 _numeration
@@ -2627,50 +2592,41 @@ num_type can take :
 sub _numeration {
     my ($value, $num_type, $locale) = @_;
     $value ||= 0;
-    my $initlocale = setlocale(LC_TIME);
-    if($locale and $locale ne $initlocale) {
-        $locale = setlocale(LC_TIME, $locale);
-    }
-    $locale ||= $initlocale;
-    my $string;
     $num_type //= '';
-    given ($num_type) {
-        when (/^dayname$/) {
-              $value = $value % 7;
-              $string = POSIX::strftime("%A",0,0,0,0,0,0,$value);
-        }
-        when (/^monthname$/) {
-              $value = $value % 12;
-              $string = POSIX::strftime("%B",0,0,0,1,$value,0,0,0,0);
-        }
-        when (/^season$/) {
-              my $seasonlocale = ($locale)
-                               ? (substr $locale,0,2)
-                               : "en";
-              my %seasons=(
-                 "en" =>
-                    [qw(Spring Summer Fall Winter)],
-                 "fr"=>
-                    [qw(Printemps Été Automne Hiver)],
-              );
-              $value = $value % 4;
-              $string = ($seasons{$seasonlocale})
-                      ? $seasons{$seasonlocale}->[$value]
-                      : $seasons{'en'}->[$value];
-        }
-        default {
-            $string = $value;
-        }
-    }
-    if($locale ne $initlocale) {
-        setlocale(LC_TIME, $initlocale);
+    $locale ||= 'en';
+    my $string;
+    if ( $num_type =~ /^dayname$/ ) {
+        # 1970-11-01 was a Sunday
+        $value = $value % 7;
+        my $dt = DateTime->new(
+            year    => 1970,
+            month   => 11,
+            day     => $value + 1,
+            locale  => $locale,
+        );
+        $string = $dt->strftime("%A");
+    } elsif ( $num_type =~ /^monthname$/ ) {
+        $value = $value % 12;
+        my $dt = DateTime->new(
+            year    => 1970,
+            month   => $value + 1,
+            locale  => $locale,
+        );
+        $string = $dt->strftime("%B");
+    } elsif ( $num_type =~ /^season$/ ) {
+        my @seasons= qw( Spring Summer Fall Winter );
+        $value = $value % 4;
+        $string = $seasons[$value];
+    } else {
+        $string = $value;
     }
+
     return $string;
 }
 
 =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
 
@@ -2679,13 +2635,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
@@ -2695,7 +2651,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 = ?
@@ -2703,13 +2659,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
@@ -2719,7 +2675,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 = ?
@@ -2727,13 +2683,94 @@ 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
+
+    $bool = subscriptionCurrentlyOnOrder( $subscriptionid );
+
+Return 1 if subscription is currently on order else 0.
+
+=cut
+
+sub subscriptionCurrentlyOnOrder {
+    my ( $subscriptionid ) = @_;
+    my $dbh = C4::Context->dbh;
+    my $query = qq|
+        SELECT COUNT(*) FROM aqorders
+        WHERE subscriptionid = ?
+            AND datereceived IS NULL
+            AND datecancellationprinted IS NULL
+    |;
+    my $sth = $dbh->prepare( $query );
+    $sth->execute($subscriptionid);
+    return $sth->fetchrow_array;
+}
+
+=head2 can_edit_subscription
+
+    $can = can_edit_subscription( $subscriptionid[, $userid] );
+
+Return 1 if the subscription can be edited by the current logged user (or a given $userid), else 0.
+
+=cut
+
+sub can_edit_subscription {
+    my ( $subscription, $userid ) = @_;
+    return _can_do_on_subscription( $subscription, $userid, 'edit_subscription' );
+}
+
+=head2 can_show_subscription
+
+    $can = can_show_subscription( $subscriptionid[, $userid] );
+
+Return 1 if the subscription can be shown by the current logged user (or a given $userid), else 0.
+
+=cut
+
+sub can_show_subscription {
+    my ( $subscription, $userid ) = @_;
+    return _can_do_on_subscription( $subscription, $userid, '*' );
+}
+
+sub _can_do_on_subscription {
+    my ( $subscription, $userid, $permission ) = @_;
+    return 0 unless C4::Context->userenv;
+    my $flags = C4::Context->userenv->{flags};
+    $userid ||= C4::Context->userenv->{'id'};
+
+    if ( C4::Context->preference('IndependentBranches') ) {
+        return 1
+          if C4::Context->IsSuperLibrarian()
+              or
+              C4::Auth::haspermission( $userid, { serials => 'superserials' } )
+              or (
+                  C4::Auth::haspermission( $userid,
+                      { serials => $permission } )
+                  and (  not defined $subscription->{branchcode}
+                      or $subscription->{branchcode} eq ''
+                      or $subscription->{branchcode} eq
+                      C4::Context->userenv->{'branch'} )
+              );
+    }
+    else {
+        return 1
+          if C4::Context->IsSuperLibrarian()
+              or
+              C4::Auth::haspermission( $userid, { serials => 'superserials' } )
+              or C4::Auth::haspermission(
+                  $userid, { serials => $permission }
+              ),
+        ;
+    }
+    return 0;
 }
 
 1;