Bug 29697: Replace GetMarcBiblio occurrences with $biblio->metadata->record
[srvgit] / C4 / Serials.pm
index 1f2cbc0..6a1274f 100644 (file)
@@ -20,21 +20,30 @@ package C4::Serials;
 
 use Modern::Perl;
 
-use C4::Auth qw(haspermission);
+use C4::Auth qw( haspermission );
 use C4::Context;
 use DateTime;
-use Date::Calc qw(:all);
-use POSIX qw(strftime);
-use C4::Biblio;
-use C4::Log;    # logaction
-use C4::Debug;
-use C4::Serials::Frequency;
+use Date::Calc qw(
+    Add_Delta_Days
+    Add_Delta_YM
+    check_date
+    Delta_Days
+    N_Delta_YMD
+    Today
+);
+use POSIX qw( strftime );
+use C4::Biblio qw( GetMarcFromKohaField ModBiblio );
+use C4::Log qw( logaction );    # logaction
+use C4::Serials::Frequency qw( GetSubscriptionFrequency );
 use C4::Serials::Numberpattern;
-use Koha::AdditionalField;
-use Koha::DateUtils;
-use Koha::Database;
-
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+use Koha::AdditionalFieldValues;
+use Koha::Biblios;
+use Koha::DateUtils qw( dt_from_string output_pref );
+use Koha::Serial;
+use Koha::Subscriptions;
+use Koha::Subscription::Histories;
+use Koha::SharedContent;
+use Scalar::Util qw( looks_like_number );
 
 # Define statuses
 use constant {
@@ -58,34 +67,39 @@ use constant MISSING_STATUSES => (
     MISSING_LOST
 );
 
+our (@ISA, @EXPORT_OK);
 BEGIN {
-    $VERSION = 3.07.00.049;    # set version for version checking
     require Exporter;
     @ISA    = qw(Exporter);
-    @EXPORT = qw(
-      &NewSubscription    &ModSubscription    &DelSubscription
-      &GetSubscription    &CountSubscriptionFromBiblionumber      &GetSubscriptionsFromBiblionumber
-      &SearchSubscriptions
-      &GetFullSubscriptionsFromBiblionumber   &GetFullSubscription &ModSubscriptionHistory
-      &HasSubscriptionStrictlyExpired &HasSubscriptionExpired &GetExpirationDate &abouttoexpire
-      &GetSubscriptionHistoryFromSubscriptionId
-
-      &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
-      &CountIssues
+    @EXPORT_OK = qw(
+      NewSubscription    ModSubscription    DelSubscription
+      GetSubscription    CountSubscriptionFromBiblionumber      GetSubscriptionsFromBiblionumber
+      SearchSubscriptions
+      GetFullSubscriptionsFromBiblionumber   GetFullSubscription ModSubscriptionHistory
+      HasSubscriptionStrictlyExpired HasSubscriptionExpired GetExpirationDate abouttoexpire
+      GetFictiveIssueNumber
+      GetSubscriptionHistoryFromSubscriptionId
+
+      GetNextSeq GetSeq NewIssue           GetSerials
+      GetLatestSerials   ModSerialStatus    GetNextDate
+      CloseSubscription ReopenSubscription
+      subscriptionCurrentlyOnOrder
+      can_claim_subscription can_edit_subscription can_show_subscription
+      GetSerials2
+      GetSubscriptionLength ReNewSubscription  GetLateOrMissingIssues
+      GetSerialInformation                   AddItem2Serial
+      PrepareSerialsData GetNextExpected    ModNextExpected
+      GetSubscriptionIrregularities
+      GetPreviousSerialid
+
+      GetSuppliersWithLateIssues
+      getroutinglist     delroutingmember   addroutingmember
+      reorder_members
+      check_routing updateClaim
+      CountIssues
       HasItems
-      &GetSubscriptionsFromBorrower
-      &subscriptionCurrentlyOnOrder
+
+      findSerialsByStatus
 
     );
 }
@@ -162,29 +176,8 @@ sub GetSubscriptionHistoryFromSubscriptionId {
     return $results;
 }
 
-=head2 GetSerialStatusFromSerialId
-
-$sth = GetSerialStatusFromSerialId();
-this function returns a statement handle
-After this function, don't forget to execute it by using $sth->execute($serialid)
-return :
-$sth = $dbh->prepare($query).
-
-=cut
-
-sub GetSerialStatusFromSerialId {
-    my $dbh   = C4::Context->dbh;
-    my $query = qq|
-        SELECT status
-        FROM   serial
-        WHERE  serialid = ?
-    |;
-    return $dbh->prepare($query);
-}
-
 =head2 GetSerialInformation
 
-
 $data = GetSerialInformation($serialid);
 returns a hash_ref containing :
   items : items marcrecord (can be an array)
@@ -217,7 +210,6 @@ sub GetSerialInformation {
 
                 #It is ASSUMED that GetMarcItem ALWAYS WORK...
                 #Maybe GetMarcItem should return values on failure
-                $debug and warn "itemnumber :$itemnum->[0], bibnum :" . $data->{'biblionumber'};
                 my $itemprocessed = C4::Items::PrepareItemrecordDisplay( $data->{'biblionumber'}, $itemnum->[0], $data );
                 $itemprocessed->{'itemnumber'}   = $itemnum->[0];
                 $itemprocessed->{'itemid'}       = $itemnum->[0];
@@ -260,40 +252,12 @@ 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 = ?,
-            claims_count = claims_count + 1
-        WHERE  serialid in (" . join( ",", map { '?' } @$serialids ) . ")
-    ";
-    my $rq = $dbh->prepare($query);
-    $rq->execute($date, CLAIMED, @$serialids);
-    return $rq->rows;
-}
-
 =head2 GetSubscription
 
 $subs = GetSubscription($subscriptionid)
 this function returns the subscription which has $subscriptionid as id.
 return :
-a hashref. This hash containts
+a hashref. This hash contains
 subscription, subscriptionhistory, aqbooksellers.name, biblio.title
 
 =cut
@@ -306,6 +270,7 @@ sub GetSubscription {
                 subscriptionhistory.*,
                 aqbooksellers.name AS aqbooksellername,
                 biblio.title AS bibliotitle,
+                biblio.subtitle AS bibliosubtitle,
                 subscription.biblionumber as bibnum
        FROM subscription
        LEFT JOIN subscriptionhistory ON subscription.subscriptionid=subscriptionhistory.subscriptionid
@@ -314,19 +279,19 @@ sub GetSubscription {
        WHERE subscription.subscriptionid = ?
     );
 
-    $debug and warn "query : $query\nsubsid :$subscriptionid";
     my $sth = $dbh->prepare($query);
     $sth->execute($subscriptionid);
     my $subscription = $sth->fetchrow_hashref;
 
+    return unless $subscription;
+
     $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};
+    if ( my $mana_id = $subscription->{mana_id} ) {
+        my $mana_subscription = Koha::SharedContent::get_entity_by_id(
+            'subscription', $mana_id, {usecomments => 1});
+        $subscription->{comments} = $mana_subscription->{data}->{comments};
+    }
 
     return $subscription;
 }
@@ -352,7 +317,7 @@ sub GetFullSubscription {
             serial.publisheddatetext,
             serial.status, 
             serial.notes as notes,
-            year(IF(serial.publisheddate="00-00-0000",serial.planneddate,serial.publisheddate)) as year,
+            year(IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate)) as year,
             aqbooksellers.name as aqbooksellername,
             biblio.title as bibliotitle,
             subscription.branchcode AS branchcode,
@@ -364,16 +329,19 @@ sub GetFullSubscription {
   LEFT JOIN biblio on biblio.biblionumber=subscription.biblionumber 
   WHERE     serial.subscriptionid = ? 
   ORDER BY year DESC,
-          IF(serial.publisheddate="00-00-0000",serial.planneddate,serial.publisheddate) DESC,
+          IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate) DESC,
           serial.subscriptionid
           |;
-    $debug and warn "GetFullSubscription query: $query";
     my $sth = $dbh->prepare($query);
     $sth->execute($subscriptionid);
     my $subscriptions = $sth->fetchall_arrayref( {} );
-    for my $subscription ( @$subscriptions ) {
-        $subscription->{cannotedit} = not can_edit_subscription( $subscription );
+    if (scalar @$subscriptions) {
+        my $cannotedit = not can_edit_subscription( $subscriptions->[0] );
+        for my $subscription ( @$subscriptions ) {
+            $subscription->{cannotedit} = $cannotedit;
+        }
     }
+
     return $subscriptions;
 }
 
@@ -393,19 +361,10 @@ sub PrepareSerialsData {
     my $year;
     my @res;
     my $startdate;
-    my $aqbooksellername;
-    my $bibliotitle;
-    my @loopissues;
     my $first;
     my $previousnote = "";
 
     foreach my $subs (@{$lines}) {
-        for my $datefield ( qw(publisheddate planneddate) ) {
-            # handle 0000-00-00 dates
-            if (defined $subs->{$datefield} and $subs->{$datefield} =~ m/^00/) {
-                $subs->{$datefield} = undef;
-            }
-        }
         $subs->{ "status" . $subs->{'status'} } = 1;
         if ( grep { $_ == $subs->{status} } ( EXPECTED, LATE, MISSING_STATUSES, CLAIMED ) ) {
             $subs->{"checked"} = 1;
@@ -470,15 +429,17 @@ sub GetSubscriptionsFromBiblionumber {
     while ( my $subs = $sth->fetchrow_hashref ) {
         $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;
+        if ( defined $subs->{histenddate} ) {
+           $subs->{histenddate}   = output_pref( { dt => dt_from_string( $subs->{histenddate} ),   dateonly => 1 } );
+        } else {
+            $subs->{histenddate} = "";
+        }
+        $subs->{opacnote}     //= "";
         $subs->{ "periodicity" . $subs->{periodicity} }     = 1;
         $subs->{ "numberpattern" . $subs->{numberpattern} } = 1;
         $subs->{ "status" . $subs->{'status'} }             = 1;
 
-        if ( $subs->{enddate} eq '0000-00-00' ) {
+        if (not defined $subs->{enddate} ) {
             $subs->{enddate} = '';
         } else {
             $subs->{enddate} = output_pref( { dt => dt_from_string( $subs->{enddate}), dateonly => 1 } );
@@ -509,10 +470,11 @@ sub GetFullSubscriptionsFromBiblionumber {
             serial.publisheddatetext,
             serial.status, 
             serial.notes as notes,
-            year(IF(serial.publisheddate="00-00-0000",serial.planneddate,serial.publisheddate)) as year,
+            year(IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate)) as year,
             biblio.title as bibliotitle,
             subscription.branchcode AS branchcode,
-            subscription.subscriptionid AS subscriptionid
+            subscription.subscriptionid AS subscriptionid,
+            subscription.location AS location
   FROM      serial 
   LEFT JOIN subscription ON 
           (serial.subscriptionid=subscription.subscriptionid)
@@ -520,15 +482,19 @@ sub GetFullSubscriptionsFromBiblionumber {
   LEFT JOIN biblio on biblio.biblionumber=subscription.biblionumber 
   WHERE     subscription.biblionumber = ? 
   ORDER BY year DESC,
-          IF(serial.publisheddate="00-00-0000",serial.planneddate,serial.publisheddate) DESC,
+          IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate) DESC,
           serial.subscriptionid
           |;
     my $sth = $dbh->prepare($query);
     $sth->execute($biblionumber);
     my $subscriptions = $sth->fetchall_arrayref( {} );
-    for my $subscription ( @$subscriptions ) {
-        $subscription->{cannotedit} = not can_edit_subscription( $subscription );
+    if (scalar @$subscriptions) {
+        my $cannotedit = not can_edit_subscription( $subscriptions->[0] );
+        for my $subscription ( @$subscriptions ) {
+            $subscription->{cannotedit} = $cannotedit;
+        }
     }
+
     return $subscriptions;
 }
 
@@ -564,12 +530,13 @@ sub SearchSubscriptions {
     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 @subscriptions = Koha::Subscriptions->filter_by_additional_fields($additional_fields)->as_list;
+
+        return () unless @subscriptions;
+
+        $matching_record_ids_for_additional_fields = [ map {
+            $_->subscriptionid
+        } @subscriptions ];
     }
 
     my $query = q|
@@ -579,8 +546,10 @@ sub SearchSubscriptions {
             subscription.*,
             biblio.notes AS biblionotes,
             biblio.title,
+            biblio.subtitle,
             biblio.author,
             biblio.biblionumber,
+            aqbooksellers.name AS vendorname,
             biblioitems.issn
         FROM subscription
             LEFT JOIN subscriptionhistory USING(subscriptionid)
@@ -665,11 +634,10 @@ sub SearchSubscriptions {
         $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}};
+        my $subscription_object = Koha::Subscriptions->find($subscription->{subscriptionid});
+        $subscription->{additional_fields} = { map { $_->field->name => $_->value }
+            $subscription_object->additional_field_values->as_list };
+
     }
 
     return @$results;
@@ -703,7 +671,7 @@ sub GetSerials {
         publisheddatetext, planneddate,notes, routingnotes
                         FROM   serial
                         WHERE  subscriptionid = ? AND status NOT IN ( $statuses )
-                        ORDER BY IF(publisheddate<>'0000-00-00',publisheddate,planneddate) DESC";
+                        ORDER BY IF(publisheddate IS NULL,planneddate,publisheddate) DESC";
     my $sth = $dbh->prepare($query);
     $sth->execute($subscriptionid);
 
@@ -725,7 +693,7 @@ sub GetSerials {
        FROM     serial
        WHERE    subscriptionid = ?
        AND      status IN ( $statuses )
-       ORDER BY IF(publisheddate<>'0000-00-00',publisheddate,planneddate) DESC
+       ORDER BY IF(publisheddate IS NULL,planneddate,publisheddate) DESC
       ";
     $sth = $dbh->prepare($query);
     $sth->execute($subscriptionid);
@@ -766,19 +734,19 @@ sub GetSerials2 {
 
     return unless ($subscription and @$statuses);
 
-    my $statuses_string = join ',', @$statuses;
-
     my $dbh   = C4::Context->dbh;
-    my $query = qq|
+    my $query = q|
                  SELECT serialid,serialseq, status, planneddate, publisheddate,
                     publisheddatetext, notes, routingnotes
                  FROM     serial 
-                 WHERE    subscriptionid=$subscription AND status IN ($statuses_string)
+                 WHERE    subscriptionid=?
+            |
+            . q| AND status IN (| . join( ",", ('?') x @$statuses ) . q|)|
+            . q|
                  ORDER BY publisheddate,serialid DESC
-                    |;
-    $debug and warn "GetSerials2 query: $query";
+    |;
     my $sth = $dbh->prepare($query);
-    $sth->execute;
+    $sth->execute( $subscription, @$statuses );
     my @serials;
 
     while ( my $line = $sth->fetchrow_hashref ) {
@@ -825,32 +793,41 @@ 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}   = 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;
     }
 
     return \@serials;
 }
 
-=head2 GetDistributedTo
+=head2 GetPreviousSerialid
 
-$distributedto=GetDistributedTo($subscriptionid)
-This function returns the field distributedto for the subscription matching subscriptionid
+$serialid = GetPreviousSerialid($subscriptionid, $nth)
+get the $nth's previous serial for the given subscriptionid
+return :
+the serialid
 
 =cut
 
-sub GetDistributedTo {
+sub GetPreviousSerialid {
+    my ( $subscriptionid, $nth ) = @_;
+    $nth ||= 1;
     my $dbh = C4::Context->dbh;
-    my $distributedto;
-    my ($subscriptionid) = @_;
-
-    return unless ($subscriptionid);
+    my $return = undef;
 
-    my $query          = "SELECT distributedto FROM subscription WHERE subscriptionid=?";
-    my $sth            = $dbh->prepare($query);
+    # Status 2: Arrived
+    my $strsth = "SELECT   serialid
+                        FROM     serial
+                        WHERE    subscriptionid = ?
+                        AND      status = 2
+                        ORDER BY serialid DESC LIMIT $nth,1
+                ";
+    my $sth = $dbh->prepare($strsth);
     $sth->execute($subscriptionid);
-    return ($distributedto) = $sth->fetchrow;
+    my @serials;
+    my $line = $sth->fetchrow_hashref;
+    $return = $line->{'serialid'} if ($line);
+
+    return $return;
 }
 
 =head2 GetNextSeq
@@ -858,19 +835,20 @@ sub GetDistributedTo {
     my (
         $nextseq,       $newlastvalue1, $newlastvalue2, $newlastvalue3,
         $newinnerloop1, $newinnerloop2, $newinnerloop3
-    ) = GetNextSeq( $subscription, $pattern, $planneddate );
+    ) = GetNextSeq( $subscription, $pattern, $frequency, $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'.
+$frequency is a hashref containing all the attributes of the table 'subscription_frequencies'
 $planneddate is a date string in iso format.
 This function get the next issue for the subscription given on input arg
 
 =cut
 
 sub GetNextSeq {
-    my ($subscription, $pattern, $planneddate) = @_;
+    my ($subscription, $pattern, $frequency, $planneddate) = @_;
 
     return unless ($subscription and $pattern);
 
@@ -883,7 +861,7 @@ sub GetNextSeq {
         if(@irreg > 0) {
             my $irregularities = {};
             $irregularities->{$_} = 1 foreach(@irreg);
-            my $issueno = GetFictiveIssueNumber($subscription, $planneddate) + 1;
+            my $issueno = GetFictiveIssueNumber($subscription, $planneddate, $frequency) + 1;
             while($irregularities->{$issueno}) {
                 $count++;
                 $issueno++;
@@ -1026,7 +1004,7 @@ sub GetExpirationDate {
 
             #calculate the date of the last issue.
             for ( my $i = 1 ; $i <= $length ; $i++ ) {
-                $enddate = GetNextDate( $subscription, $enddate );
+                $enddate = GetNextDate( $subscription, $enddate, $frequency );
             }
         } elsif ( $subscription->{monthlength} ) {
             if ( $$subscription{startdate} ) {
@@ -1116,13 +1094,13 @@ sub ModSerialStatus {
     #It is a usual serial
     # 1st, get previous status :
     my $dbh   = C4::Context->dbh;
-    my $query = "SELECT serial.subscriptionid,serial.status,subscription.periodicity
+    my $query = "SELECT serial.subscriptionid,serial.status,subscription.periodicity,serial.routingnotes
         FROM serial, subscription
         WHERE serial.subscriptionid=subscription.subscriptionid
             AND serialid=?";
     my $sth   = $dbh->prepare($query);
     $sth->execute($serialid);
-    my ( $subscriptionid, $oldstatus, $periodicity ) = $sth->fetchrow;
+    my ( $subscriptionid, $oldstatus, $periodicity, $routingnotes ) = $sth->fetchrow;
     my $frequency = GetSubscriptionFrequency($periodicity);
 
     # change status & update subscriptionhistory
@@ -1130,16 +1108,15 @@ sub ModSerialStatus {
     if ( $status == DELETED ) {
         DelIssue( { 'serialid' => $serialid, 'subscriptionid' => $subscriptionid, 'serialseq' => $serialseq } );
     } else {
-
         my $query = '
             UPDATE serial
             SET serialseq = ?, publisheddate = ?, publisheddatetext = ?,
-                planneddate = ?, status = ?, notes = ?
+                planneddate = ?, status = ?, notes = ?, routingnotes = ?
             WHERE  serialid = ?
         ';
         $sth = $dbh->prepare($query);
         $sth->execute( $serialseq, $publisheddate, $publisheddatetext,
-            $planneddate, $status, $notes, $serialid );
+            $planneddate, $status, $notes, $routingnotes, $serialid );
         $query = "SELECT * FROM   subscription WHERE  subscriptionid = ?";
         $sth   = $dbh->prepare($query);
         $sth->execute($subscriptionid);
@@ -1151,19 +1128,16 @@ sub ModSerialStatus {
             my ( $missinglist, $recievedlist ) = $sth->fetchrow;
 
             if ( $status == ARRIVED || ($oldstatus == ARRIVED && $status != ARRIVED) ) {
-                $recievedlist .= "; $serialseq"
-                    if ($recievedlist !~ /(^|;)\s*$serialseq(?=;|$)/);
+                $recievedlist = _handle_seqno($serialseq, $recievedlist);
             }
 
             # in case serial has been previously marked as missing
             if (grep /$status/, (EXPECTED, ARRIVED, LATE, CLAIMED)) {
-                $missinglist=~ s/(^|;)\s*$serialseq(?=;|$)//g;
+                $missinglist = _handle_seqno($serialseq, $missinglist, 'REMOVE');
             }
 
-            $missinglist .= "; $serialseq"
-                if ( ( grep { $_ == $status } ( MISSING_STATUSES ) ) && ( $missinglist !~/(^|;)\s*$serialseq(?=;|$)/ ) );
-            $missinglist .= "; not issued $serialseq"
-                if ( $status == NOT_ISSUED && $missinglist !~ /(^|;)\s*$serialseq(?=;|$)/ );
+            $missinglist = _handle_seqno($serialseq, $missinglist) if grep { $_ == $status } MISSING_STATUSES;
+            $missinglist .= "; not issued $serialseq" if $status == NOT_ISSUED and not _handle_seqno($serialseq, $missinglist, 'CHECK');
 
             $query = "UPDATE subscriptionhistory SET recievedlist=?, missinglist=? WHERE  subscriptionid=?";
             $sth   = $dbh->prepare($query);
@@ -1173,28 +1147,29 @@ sub ModSerialStatus {
         }
     }
 
-    # create new waited entry if needed (ie : was a "waited" and has changed)
-    if ( $oldstatus == EXPECTED && $status != EXPECTED ) {
+    # 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});
+        my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
 
         # next issue number
         my (
             $newserialseq,  $newlastvalue1, $newlastvalue2, $newlastvalue3,
             $newinnerloop1, $newinnerloop2, $newinnerloop3
           )
-          = GetNextSeq( $subscription, $pattern, $publisheddate );
+          = GetNextSeq( $subscription, $pattern, $frequency, $publisheddate );
 
         # next date (calculated from actual date & frequency parameters)
-        my $nextpublisheddate = GetNextDate($subscription, $publisheddate, 1);
+        my $nextpublisheddate = GetNextDate($subscription, $publisheddate, $frequency, 1);
         my $nextpubdate = $nextpublisheddate;
         $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 );
-
+        my $newnote = C4::Context->preference('PreserveSerialNotes') ? $notes : "";
+        NewIssue( $newserialseq, $subscriptionid, $subscription->{'biblionumber'}, 1, $nextpubdate, $nextpubdate, undef, $newnote, $routingnotes );
         # check if an alert must be sent... (= a letter is defined & status became "arrived"
         if ( $subscription->{letter} && $status == ARRIVED && $oldstatus != ARRIVED ) {
             require C4::Letters;
@@ -1205,6 +1180,24 @@ sub ModSerialStatus {
     return;
 }
 
+sub _handle_seqno {
+# Adds or removes seqno from list when needed; returns list
+# Or checks and returns true when present
+
+    my ( $seq, $list, $op ) = @_; # op = ADD | REMOVE | CHECK (default: ADD)
+    my $seq_r = $seq;
+    $seq_r =~ s/([()])/\\$1/g; # Adjust disturbing parentheses for regex, maybe extend in future
+
+    if( !$op or $op eq 'ADD' ) {
+        $list .= "; $seq" if $list !~ /(^|;)\s*$seq_r(?=;|$)/;
+    } elsif( $op eq 'REMOVE' ) {
+        $list=~ s/(^|;)\s*(not issued )?$seq_r(?=;|$)//g;
+    } else { # CHECK
+        return $list =~ /(^|;)\s*$seq_r(?=;|$)/ ? 1 : q{};
+    }
+    return $list;
+}
+
 =head2 GetNextExpected
 
 $nextexpected = GetNextExpected($subscriptionid)
@@ -1249,13 +1242,8 @@ sub GetNextExpected {
         $nextissue = $sth->fetchrow_hashref;
     }
     foreach(qw/planneddate publisheddate/) {
-        if ( !defined $nextissue->{$_} ) {
-            # or should this default to 1st Jan ???
-            $nextissue->{$_} = strftime( '%Y-%m-%d', localtime );
-        }
-        $nextissue->{$_} = ($nextissue->{$_} ne '0000-00-00')
-                         ? $nextissue->{$_}
-                         : undef;
+        # or should this default to 1st Jan ???
+        $nextissue->{$_} //= strftime( '%Y-%m-%d', localtime );
     }
 
     return $nextissue;
@@ -1333,39 +1321,61 @@ sub ModSubscription {
     $lastvalue2, $innerloop2, $lastvalue3, $innerloop3, $status,
     $biblionumber, $callnumber, $notes, $letter, $manualhistory,
     $internalnotes, $serialsadditems, $staffdisplaycount, $opacdisplaycount,
-    $graceperiod, $location, $enddate, $subscriptionid, $skip_serialseq
+    $graceperiod, $location, $enddate, $subscriptionid, $skip_serialseq,
+    $itemtype, $previousitemtype, $mana_id, $ccode
     ) = @_;
 
-    my $dbh   = C4::Context->dbh;
-    my $query = "UPDATE subscription
-        SET librarian=?, branchcode=?, aqbooksellerid=?, cost=?, aqbudgetid=?,
-            startdate=?, periodicity=?, firstacquidate=?, irregularity=?,
-            numberpattern=?, locale=?, numberlength=?, weeklength=?, monthlength=?,
-            lastvalue1=?, innerloop1=?, lastvalue2=?, innerloop2=?,
-            lastvalue3=?, innerloop3=?, status=?, biblionumber=?,
-            callnumber=?, notes=?, letter=?, manualhistory=?,
-            internalnotes=?, serialsadditems=?, staffdisplaycount=?,
-            opacdisplaycount=?, graceperiod=?, location = ?, enddate=?,
-            skip_serialseq=?
-        WHERE subscriptionid = ?";
-
-    my $sth = $dbh->prepare($query);
-    $sth->execute(
-        $auser,           $branchcode,     $aqbooksellerid, $cost,
-        $aqbudgetid,      $startdate,      $periodicity,    $firstacquidate,
-        $irregularity,    $numberpattern,  $locale,         $numberlength,
-        $weeklength,      $monthlength,    $lastvalue1,     $innerloop1,
-        $lastvalue2,      $innerloop2,     $lastvalue3,     $innerloop3,
-        $status,          $biblionumber,   $callnumber,     $notes,
-        $letter,          ($manualhistory ? $manualhistory : 0),
-        $internalnotes, $serialsadditems, $staffdisplaycount, $opacdisplaycount,
-        $graceperiod,     $location,       $enddate,        $skip_serialseq,
-        $subscriptionid
-    );
-    my $rows = $sth->rows;
+    my $subscription = Koha::Subscriptions->find($subscriptionid);
+    $subscription->set(
+        {
+            librarian         => $auser,
+            branchcode        => $branchcode,
+            aqbooksellerid    => $aqbooksellerid,
+            cost              => $cost,
+            aqbudgetid        => $aqbudgetid,
+            biblionumber      => $biblionumber,
+            startdate         => $startdate,
+            periodicity       => $periodicity,
+            numberlength      => $numberlength,
+            weeklength        => $weeklength,
+            monthlength       => $monthlength,
+            lastvalue1        => $lastvalue1,
+            innerloop1        => $innerloop1,
+            lastvalue2        => $lastvalue2,
+            innerloop2        => $innerloop2,
+            lastvalue3        => $lastvalue3,
+            innerloop3        => $innerloop3,
+            status            => $status,
+            notes             => $notes,
+            letter            => $letter,
+            firstacquidate    => $firstacquidate,
+            irregularity      => $irregularity,
+            numberpattern     => $numberpattern,
+            locale            => $locale,
+            callnumber        => $callnumber,
+            manualhistory     => $manualhistory,
+            internalnotes     => $internalnotes,
+            serialsadditems   => $serialsadditems,
+            staffdisplaycount => $staffdisplaycount,
+            opacdisplaycount  => $opacdisplaycount,
+            graceperiod       => $graceperiod,
+            location          => $location,
+            enddate           => $enddate,
+            skip_serialseq    => $skip_serialseq,
+            itemtype          => $itemtype,
+            previousitemtype  => $previousitemtype,
+            mana_id           => $mana_id,
+            ccode             => $ccode,
+        }
+    )->store;
+    # FIXME Must be $subscription->serials
+    # FIXME We shouldn't need serial.subscription (instead use serial->subscription->biblionumber)
+    Koha::Serials->search({ subscriptionid => $subscriptionid })->update({ biblionumber => $biblionumber });
 
     logaction( "SERIAL", "MODIFY", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
-    return $rows;
+
+    $subscription->discard_changes;
+    return $subscription;
 }
 
 =head2 NewSubscription
@@ -1375,7 +1385,8 @@ $subscriptionid = &NewSubscription($auser,branchcode,$aqbooksellerid,$cost,$aqbu
     $lastvalue1,$innerloop1,$lastvalue2,$innerloop2,$lastvalue3,$innerloop3,
     $status, $notes, $letter, $firstacquidate, $irregularity, $numberpattern,
     $locale, $callnumber, $manualhistory, $internalnotes, $serialsadditems,
-    $staffdisplaycount, $opacdisplaycount, $graceperiod, $location, $enddate, $skip_serialseq);
+    $staffdisplaycount, $opacdisplaycount, $graceperiod, $location, $enddate,
+    $skip_serialseq, $itemtype, $previousitemtype);
 
 Create a new subscription with value given on input args.
 
@@ -1392,34 +1403,55 @@ sub NewSubscription {
     $innerloop3, $status, $notes, $letter, $firstacquidate, $irregularity,
     $numberpattern, $locale, $callnumber, $manualhistory, $internalnotes,
     $serialsadditems, $staffdisplaycount, $opacdisplaycount, $graceperiod,
-    $location, $enddate, $skip_serialseq
+    $location, $enddate, $skip_serialseq, $itemtype, $previousitemtype, $mana_id, $ccode
     ) = @_;
     my $dbh = C4::Context->dbh;
 
-    #save subscription (insert into database)
-    my $query = qq|
-        INSERT INTO subscription
-            (librarian, branchcode, aqbooksellerid, cost, aqbudgetid,
-            biblionumber, startdate, periodicity, numberlength, weeklength,
-            monthlength, lastvalue1, innerloop1, lastvalue2, innerloop2,
-            lastvalue3, innerloop3, status, notes, letter, firstacquidate,
-            irregularity, numberpattern, locale, callnumber,
-            manualhistory, internalnotes, serialsadditems, staffdisplaycount,
-            opacdisplaycount, graceperiod, location, enddate, skip_serialseq)
-        VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
-        |;
-    my $sth = $dbh->prepare($query);
-    $sth->execute(
-        $auser, $branchcode, $aqbooksellerid, $cost, $aqbudgetid, $biblionumber,
-        $startdate, $periodicity, $numberlength, $weeklength,
-        $monthlength, $lastvalue1, $innerloop1, $lastvalue2, $innerloop2,
-        $lastvalue3, $innerloop3, $status, $notes, $letter,
-        $firstacquidate, $irregularity, $numberpattern, $locale, $callnumber,
-        $manualhistory, $internalnotes, $serialsadditems, $staffdisplaycount,
-        $opacdisplaycount, $graceperiod, $location, $enddate, $skip_serialseq
-    );
-
-    my $subscriptionid = $dbh->{'mysql_insertid'};
+    my $subscription = Koha::Subscription->new(
+        {
+            librarian         => $auser,
+            branchcode        => $branchcode,
+            aqbooksellerid    => $aqbooksellerid,
+            cost              => $cost,
+            aqbudgetid        => $aqbudgetid,
+            biblionumber      => $biblionumber,
+            startdate         => $startdate,
+            periodicity       => $periodicity,
+            numberlength      => $numberlength,
+            weeklength        => $weeklength,
+            monthlength       => $monthlength,
+            lastvalue1        => $lastvalue1,
+            innerloop1        => $innerloop1,
+            lastvalue2        => $lastvalue2,
+            innerloop2        => $innerloop2,
+            lastvalue3        => $lastvalue3,
+            innerloop3        => $innerloop3,
+            status            => $status,
+            notes             => $notes,
+            letter            => $letter,
+            firstacquidate    => $firstacquidate,
+            irregularity      => $irregularity,
+            numberpattern     => $numberpattern,
+            locale            => $locale,
+            callnumber        => $callnumber,
+            manualhistory     => $manualhistory,
+            internalnotes     => $internalnotes,
+            serialsadditems   => $serialsadditems,
+            staffdisplaycount => $staffdisplaycount,
+            opacdisplaycount  => $opacdisplaycount,
+            graceperiod       => $graceperiod,
+            location          => $location,
+            enddate           => $enddate,
+            skip_serialseq    => $skip_serialseq,
+            itemtype          => $itemtype,
+            previousitemtype  => $previousitemtype,
+            mana_id           => $mana_id,
+            ccode             => $ccode
+        }
+    )->store;
+    $subscription->discard_changes;
+    my $subscriptionid = $subscription->subscriptionid;
+    my ( $query, $sth );
     unless ($enddate) {
         $enddate = GetExpirationDate( $subscriptionid, $startdate );
         $query = qq|
@@ -1434,21 +1466,20 @@ sub NewSubscription {
     # then create the 1st expected number
     $query = qq(
         INSERT INTO subscriptionhistory
-            (biblionumber, subscriptionid, histstartdate)
-        VALUES (?,?,?)
+            (biblionumber, subscriptionid, histstartdate, missinglist, recievedlist)
+        VALUES (?,?,?, '', '')
         );
     $sth = $dbh->prepare($query);
     $sth->execute( $biblionumber, $subscriptionid, $startdate);
 
     # reread subscription to get a hash (for calculation of the 1st issue number)
-    my $subscription = GetSubscription($subscriptionid);
+    $subscription = GetSubscription($subscriptionid); # We should not do that
     my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
 
     # calculate issue number
     my $serialseq = GetSeq($subscription, $pattern) || q{};
 
-    my $serial_rs = Koha::Database->new()->schema()->resultset('Serial');
-    $serial_rs->create(
+    Koha::Serial->new(
         {
             serialseq      => $serialseq,
             serialseq_x    => $subscription->{'lastvalue1'},
@@ -1460,33 +1491,66 @@ sub NewSubscription {
             planneddate    => $firstacquidate,
             publisheddate  => $firstacquidate,
         }
-    );
+    )->store();
 
     logaction( "SERIAL", "ADD", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
 
     #set serial flag on biblio if not already set.
-    my $bib = GetBiblio($biblionumber);
-    if ( $bib and !$bib->{'serial'} ) {
-        my $record = GetMarcBiblio($biblionumber);
-        my ( $tag, $subf ) = GetMarcFromKohaField( 'biblio.serial', $bib->{'frameworkcode'} );
+    my $biblio = Koha::Biblios->find( $biblionumber );
+    if ( $biblio and !$biblio->serial ) {
+        my $record = $biblio->metadata->record;
+        my ( $tag, $subf ) = GetMarcFromKohaField( 'biblio.serial' );
         if ($tag) {
             eval { $record->field($tag)->update( $subf => 1 ); };
         }
-        ModBiblio( $record, $biblionumber, $bib->{'frameworkcode'} );
+        ModBiblio( $record, $biblionumber, $biblio->frameworkcode );
     }
     return $subscriptionid;
 }
 
+=head2 GetSubscriptionLength
+
+my ($numberlength, $weeklength, $monthlength) = GetSubscriptionLength( $subtype, $sublength );
+
+This function calculates the subscription length.
+
+=cut
+
+sub GetSubscriptionLength {
+    my ($subtype, $length) = @_;
+
+    return unless looks_like_number($length);
+
+    return
+    (
+          $subtype eq 'issues' ? $length : 0,
+          $subtype eq 'weeks'  ? $length : 0,
+          $subtype eq 'months' ? $length : 0,
+    );
+}
+
+
 =head2 ReNewSubscription
 
-ReNewSubscription($subscriptionid,$user,$startdate,$numberlength,$weeklength,$monthlength,$note)
+ReNewSubscription($params);
+
+$params is a hashref with the following keys: subscriptionid, user, startdate, numberlength, weeklength, monthlength, note, branchcode
 
 this function renew a subscription with values given on input args.
 
 =cut
 
 sub ReNewSubscription {
-    my ( $subscriptionid, $user, $startdate, $numberlength, $weeklength, $monthlength, $note ) = @_;
+    my ( $params ) = @_;
+    my $subscriptionid = $params->{subscriptionid};
+    my $user           = $params->{user};
+    my $startdate      = $params->{startdate};
+    my $numberlength   = $params->{numberlength};
+    my $weeklength     = $params->{weeklength};
+    my $monthlength    = $params->{monthlength};
+    my $note           = $params->{note};
+    my $branchcode     = $params->{branchcode};
+
     my $dbh          = C4::Context->dbh;
     my $subscription = GetSubscription($subscriptionid);
     my $query        = qq|
@@ -1506,12 +1570,16 @@ sub ReNewSubscription {
                 'title'         => $subscription->{bibliotitle},
                 'author'        => $biblio->{author},
                 'publishercode' => $biblio->{publishercode},
-                'note'          => $biblio->{note},
-                'biblionumber'  => $subscription->{biblionumber}
+                'note'          => $note,
+                'biblionumber'  => $subscription->{biblionumber},
+                'branchcode'    => $branchcode,
             }
         );
     }
 
+    $numberlength ||= 0; # Should not we raise an exception instead?
+    $weeklength   ||= 0;
+
     # renew subscription
     $query = qq|
         UPDATE subscription
@@ -1521,7 +1589,6 @@ sub ReNewSubscription {
     $sth = $dbh->prepare($query);
     $sth->execute( $startdate, $numberlength, $weeklength, $monthlength, $subscriptionid );
     my $enddate = GetExpirationDate($subscriptionid);
-       $debug && warn "enddate :$enddate";
     $query = qq|
         UPDATE subscription
         SET    enddate=?
@@ -1529,13 +1596,6 @@ sub ReNewSubscription {
     |;
     $sth = $dbh->prepare($query);
     $sth->execute( $enddate, $subscriptionid );
-    $query = qq|
-        UPDATE subscriptionhistory
-        SET    histenddate=?
-        WHERE  subscriptionid=?
-    |;
-    $sth = $dbh->prepare($query);
-    $sth->execute( $enddate, $subscriptionid );
 
     logaction( "SERIAL", "RENEW", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
     return;
@@ -1543,7 +1603,7 @@ sub ReNewSubscription {
 
 =head2 NewIssue
 
-NewIssue($serialseq,$subscriptionid,$biblionumber,$status, $planneddate, $publisheddate,  $notes)
+NewIssue($serialseq,$subscriptionid,$biblionumber,$status, $planneddate, $publisheddate, $notes, $routingnotes)
 
 Create a new issue stored on the database.
 Note : we have to update the recievedlist and missinglist on subscriptionhistory for this subscription.
@@ -1553,61 +1613,55 @@ returns the serial id
 
 sub NewIssue {
     my ( $serialseq, $subscriptionid, $biblionumber, $status, $planneddate,
-        $publisheddate, $publisheddatetext, $notes ) = @_;
+        $publisheddate, $publisheddatetext, $notes, $routingnotes ) = @_;
     ### FIXME biblionumber CAN be provided by subscriptionid. So Do we STILL NEED IT ?
 
     return unless ($subscriptionid);
 
     my $schema = Koha::Database->new()->schema();
 
-    my $subscription = $schema->resultset('Subscription')->find( $subscriptionid );
+    my $subscription = Koha::Subscriptions->find( $subscriptionid );
 
-    my $serial = $schema->resultset('Serial')->create(
+    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,
+            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,
+            notes             => $notes,
+            routingnotes      => $routingnotes
         }
-    );
+    )->store();
 
-    my $dbh   = C4::Context->dbh;
     my $serialid = $serial->id();
 
-    my $query = qq|
-        SELECT missinglist,recievedlist
-        FROM   subscriptionhistory
-        WHERE  subscriptionid=?
-    |;
-    my $sth = $dbh->prepare($query);
-    $sth->execute($subscriptionid);
-    my ( $missinglist, $recievedlist ) = $sth->fetchrow;
+    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);
+        ### 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 ( grep {/^$status$/} ( MISSING_STATUSES ) ) {
-        $missinglist .= "; $serialseq" unless (index($missinglist,$serialseq)>0);
+    if ( grep { $_ eq $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 );
+
+    $subscription_history->recievedlist($recievedlist);
+    $subscription_history->missinglist($missinglist);
+    $subscription_history->store();
+
     return $serialid;
 }
 
@@ -1712,26 +1766,6 @@ sub HasSubscriptionExpired {
     return 0;    # Notice that you'll never get here.
 }
 
-=head2 SetDistributedto
-
-SetDistributedto($distributedto,$subscriptionid);
-This function update the value of distributedto for a subscription given on input arg.
-
-=cut
-
-sub SetDistributedto {
-    my ( $distributedto, $subscriptionid ) = @_;
-    my $dbh   = C4::Context->dbh;
-    my $query = qq|
-        UPDATE subscription
-        SET    distributedto=?
-        WHERE  subscriptionid=?
-    |;
-    my $sth = $dbh->prepare($query);
-    $sth->execute( $distributedto, $subscriptionid );
-    return;
-}
-
 =head2 DelSubscription
 
 DelSubscription($subscriptionid)
@@ -1743,13 +1777,11 @@ sub DelSubscription {
     my ($subscriptionid) = @_;
     my $dbh = C4::Context->dbh;
     $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});
-    }
+    Koha::AdditionalFieldValues->search({
+        'field.tablename' => 'subscription',
+        'me.record_id' => $subscriptionid,
+    }, { join => 'field' })->delete;
 
     logaction( "SERIAL", "DELETE", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
 }
@@ -1836,7 +1868,7 @@ sub GetLateOrMissingIssues {
                 serialid,      aqbooksellerid,        name,
                 biblio.title,  biblioitems.issn,      planneddate,    serialseq,
                 serial.status, serial.subscriptionid, claimdate, claims_count,
-                subscription.branchcode
+                subscription.branchcode, serial.publisheddate
             FROM      serial
                 LEFT JOIN subscription  ON serial.subscriptionid=subscription.subscriptionid
                 LEFT JOIN biblio        ON subscription.biblionumber=biblio.biblionumber
@@ -1854,7 +1886,7 @@ sub GetLateOrMissingIssues {
             serialid,      aqbooksellerid,         name,
             biblio.title,  planneddate,           serialseq,
                 serial.status, serial.subscriptionid, claimdate, claims_count,
-                subscription.branchcode
+                subscription.branchcode, serial.publisheddate
             FROM serial
                 LEFT JOIN subscription ON serial.subscriptionid=subscription.subscriptionid
                 LEFT JOIN biblio ON subscription.biblionumber=biblio.biblionumber
@@ -1879,11 +1911,9 @@ sub GetLateOrMissingIssues {
         }
         $line->{"status".$line->{status}}   = 1;
 
-        my $additional_field_values = Koha::AdditionalField->fetch_all_values({
-            record_id => $line->{subscriptionid},
-            tablename => 'subscription'
-        });
-        %$line = ( %$line, additional_fields => $additional_field_values->{$line->{subscriptionid}} );
+        my $subscription_object = Koha::Subscriptions->find($line->{subscriptionid});
+        $line->{additional_fields} = { map { $_->field->name => $_->value }
+            $subscription_object->additional_field_values->as_list };
 
         push @issuelist, $line;
     }
@@ -1901,42 +1931,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;
-}
-
-=head2 getsupplierbyserialid
-
-$result = getsupplierbyserialid($serialid)
-
-this function is used to find the supplier id given a serial id
-
-return :
-hashref containing serialid, subscriptionid, and aqbooksellerid
-
-=cut
-
-sub getsupplierbyserialid {
-    my ($serialid) = @_;
-    my $dbh        = C4::Context->dbh;
-    my $sth        = $dbh->prepare(
-        "SELECT serialid, serial.subscriptionid, aqbooksellerid
-         FROM serial 
-            LEFT JOIN subscription ON serial.subscriptionid = subscription.subscriptionid
-            WHERE serialid = ?
-        "
-    );
-    $sth->execute($serialid);
-    my $line   = $sth->fetchrow_hashref;
-    my $result = $line->{'aqbooksellerid'};
-    return $result;
+            claims_count = claims_count + 1,
+            status = ?
+        WHERE serialid in (| . join( q|,|, (q|?|) x @$serialids ) . q|)|,
+        {}, CLAIMED, @$serialids );
 }
 
 =head2 check_routing
@@ -1957,7 +1964,7 @@ sub check_routing {
     my $sth              = $dbh->prepare(
         "SELECT count(routingid) routingids FROM subscription LEFT JOIN subscriptionroutinglist 
                               ON subscription.subscriptionid = subscriptionroutinglist.subscriptionid
-                              WHERE subscription.subscriptionid = ? ORDER BY ranking ASC
+                              WHERE subscription.subscriptionid = ? GROUP BY routingid ORDER BY ranking ASC
                               "
     );
     $sth->execute($subscriptionid);
@@ -2185,7 +2192,7 @@ sub abouttoexpire {
         my $expirationdate = GetExpirationDate($subscriptionid);
 
         my ($res) = $dbh->selectrow_array('select max(planneddate) from serial where subscriptionid = ?', undef, $subscriptionid);
-        my $nextdate = GetNextDate($subscription, $res);
+        my $nextdate = GetNextDate($subscription, $res, $frequency);
 
         # only compare dates if both dates exist.
         if ($nextdate and $expirationdate) {
@@ -2197,61 +2204,16 @@ sub abouttoexpire {
             }
         }
 
-    } elsif ($subscription->{numberlength}>0) {
+    } elsif ( $subscription->{numberlength} && $subscription->{numberlength}>0) {
         return (countissuesfrom($subscriptionid,$subscription->{'startdate'}) >=$subscription->{numberlength}-1);
     }
 
     return 0;
 }
 
-sub in_array {    # used in next sub down
-    my ( $val, @elements ) = @_;
-    foreach my $elem (@elements) {
-        if ( $val == $elem ) {
-            return 1;
-        }
-    }
-    return 0;
-}
-
-=head2 GetSubscriptionsFromBorrower
-
-($count,@routinglist) = GetSubscriptionsFromBorrower($borrowernumber)
-
-this gets the info from subscriptionroutinglist for each $subscriptionid
-
-return :
-a count of the serial subscription routing lists to which a patron belongs,
-with the titles of those serial subscriptions as an array. Each element of the array
-contains a hash_ref with subscriptionID and title of subscription.
-
-=cut
-
-sub GetSubscriptionsFromBorrower {
-    my ($borrowernumber) = @_;
-    my $dbh              = C4::Context->dbh;
-    my $sth              = $dbh->prepare(
-        "SELECT subscription.subscriptionid, biblio.title
-            FROM subscription
-            JOIN biblio ON biblio.biblionumber = subscription.biblionumber
-            JOIN subscriptionroutinglist USING (subscriptionid)
-            WHERE subscriptionroutinglist.borrowernumber = ? ORDER BY title ASC
-                               "
-    );
-    $sth->execute($borrowernumber);
-    my @routinglist;
-    my $count = 0;
-    while ( my $line = $sth->fetchrow_hashref ) {
-        $count++;
-        push( @routinglist, $line );
-    }
-    return ( $count, @routinglist );
-}
-
-
 =head2 GetFictiveIssueNumber
 
-$issueno = GetFictiveIssueNumber($subscription, $publishedate);
+$issueno = GetFictiveIssueNumber($subscription, $publishedate, $frequency);
 
 Get the position of the issue published at $publisheddate, considering the
 first issue (at firstacquidate) is at position 1, the next is at position 2, etc...
@@ -2262,135 +2224,166 @@ depending on how many rows are in serial table.
 The issue number calculation is based on subscription frequency, first acquisition
 date, and $publisheddate.
 
+Returns undef when called for irregular frequencies.
+
+The routine is used to skip irregularities when calculating the next issue
+date (in GetNextDate) or the next issue number (in GetNextSeq).
+
 =cut
 
 sub GetFictiveIssueNumber {
-    my ($subscription, $publisheddate) = @_;
+    my ($subscription, $publisheddate, $frequency) = @_;
 
-    my $frequency = GetSubscriptionFrequency($subscription->{'periodicity'});
     my $unit = $frequency->{unit} ? lc $frequency->{'unit'} : undef;
-    my $issueno = 0;
-
-    if($unit) {
-        my ($year, $month, $day) = split /-/, $publisheddate;
-        my ($fa_year, $fa_month, $fa_day) = split /-/, $subscription->{'firstacquidate'};
-        my $wkno;
-        my $delta;
-
-        if($unit eq 'day') {
-            $delta = Delta_Days($fa_year, $fa_month, $fa_day, $year, $month, $day);
-        } elsif($unit eq 'week') {
-            ($wkno, $year) = Week_of_Year($year, $month, $day);
-            my ($fa_wkno, $fa_yr) = Week_of_Year($fa_year, $fa_month, $fa_day);
-            $delta = ($fa_yr == $year) ? ($wkno - $fa_wkno) : ( ($year-$fa_yr-1)*52 + (52-$fa_wkno+$wkno) );
-        } elsif($unit eq 'month') {
-            $delta = ($fa_year == $year)
-                   ? ($month - $fa_month)
-                   : ( ($year-$fa_year-1)*12 + (12-$fa_month+$month) );
-        } elsif($unit eq 'year') {
-            $delta = $year - $fa_year;
-        }
-        if($frequency->{'unitsperissue'} == 1) {
-            $issueno = $delta * $frequency->{'issuesperunit'} + $subscription->{'countissuesperunit'};
-        } else {
-            # Assuming issuesperunit == 1
-            $issueno = int( ($delta + $frequency->{'unitsperissue'}) / $frequency->{'unitsperissue'} );
-        }
+    return if !$unit;
+    my $issueno;
+
+    my ( $year, $month, $day ) = split /-/, $publisheddate;
+    my ( $fa_year, $fa_month, $fa_day ) = split /-/, $subscription->{'firstacquidate'};
+    my $delta = _delta_units( [$fa_year, $fa_month, $fa_day], [$year, $month, $day], $unit );
+
+    if( $frequency->{'unitsperissue'} == 1 ) {
+        $issueno = $delta * $frequency->{'issuesperunit'} + $subscription->{'countissuesperunit'};
+    } else { # issuesperunit == 1
+        $issueno = 1 + int( $delta / $frequency->{'unitsperissue'} );
     }
     return $issueno;
 }
 
+sub _delta_units {
+    my ( $date1, $date2, $unit ) = @_;
+    # date1 and date2 are array refs in the form [ yy, mm, dd ]
+
+    if( $unit eq 'day' ) {
+        return Delta_Days( @$date1, @$date2 );
+    } elsif( $unit eq 'week' ) {
+        return int( Delta_Days( @$date1, @$date2 ) / 7 );
+    }
+
+    # In case of months or years, this is a wrapper around N_Delta_YMD.
+    # Note that N_Delta_YMD returns 29 days between e.g. 22-2-72 and 22-3-72
+    # while we expect 1 month.
+    my @delta = N_Delta_YMD( @$date1, @$date2 );
+    if( $delta[2] > 27 ) {
+        # Check if we could add a month
+        my @jump = Add_Delta_YM( @$date1, $delta[0], 1 + $delta[1] );
+        if( Delta_Days( @jump, @$date2 ) >= 0 ) {
+            $delta[1]++;
+        }
+    }
+    if( $delta[1] >= 12 ) {
+        $delta[0]++;
+        $delta[1] -= 12;
+    }
+    # if unit is year, we only return full years
+    return $unit eq 'month' ? $delta[0] * 12 + $delta[1] : $delta[0];
+}
+
 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 {
+    my @newissue; # ( yy, mm, dd )
+    # We do not need $delta_days here, since it would be zero where used
+
+    if( $freqdata->{issuesperunit} == 1 ) {
+        # Add full days
+        @newissue = Add_Delta_Days(
+            $year, $month, $day, $freqdata->{"unitsperissue"} );
+    } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
+        # Add zero days
+        @newissue = ( $year, $month, $day );
         $subscription->{countissuesperunit}++;
+    } else {
+        # We finished a cycle of issues within a unit.
+        # No subtraction of zero needed, just add one day
+        @newissue = Add_Delta_Days( $year, $month, $day, 1 );
+        $subscription->{countissuesperunit} = 1;
     }
-
-    return ($year, $month, $day);
+    return @newissue;
 }
 
 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});
+    my @newissue; # ( yy, mm, dd )
+    my $delta_days = int( 7 / $freqdata->{issuesperunit} );
 
-    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);
+    if( $freqdata->{issuesperunit} == 1 ) {
+        # Add full weeks (of 7 days)
+        @newissue = Add_Delta_Days(
+            $year, $month, $day, 7 * $freqdata->{"unitsperissue"} );
+    } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
+        # Add rounded number of days based on frequency.
+        @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
         $subscription->{countissuesperunit}++;
+    } else {
+        # We finished a cycle of issues within a unit.
+        # Subtract delta * (issues - 1), add 1 week
+        @newissue = Add_Delta_Days( $year, $month, $day,
+            -$delta_days * ($freqdata->{issuesperunit} - 1) );
+        @newissue = Add_Delta_Days( @newissue, 7 );
+        $subscription->{countissuesperunit} = 1;
     }
-
-    return ($year, $month, $day);
+    return @newissue;
 }
 
 sub _get_next_date_month {
     my ($subscription, $freqdata, $year, $month, $day) = @_;
 
-    my $fa_day;
-    (undef, undef, $fa_day) = split /-/, $subscription->{firstacquidate};
+    my @newissue; # ( yy, mm, dd )
+    my $delta_days = int( 30 / $freqdata->{issuesperunit} );
 
-    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);
+    if( $freqdata->{issuesperunit} == 1 ) {
+        # Add full months
+        @newissue = Add_Delta_YM(
+            $year, $month, $day, 0, $freqdata->{"unitsperissue"} );
+    } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
+        # Add rounded number of days based on frequency.
+        @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
         $subscription->{countissuesperunit}++;
+    } else {
+        # We finished a cycle of issues within a unit.
+        # Subtract delta * (issues - 1), add 1 month
+        @newissue = Add_Delta_Days( $year, $month, $day,
+            -$delta_days * ($freqdata->{issuesperunit} - 1) );
+        @newissue = Add_Delta_YM( @newissue, 0, 1 );
+        $subscription->{countissuesperunit} = 1;
     }
-
-    return ($year, $month, $day);
+    return @newissue;
 }
 
 sub _get_next_date_year {
     my ($subscription, $freqdata, $year, $month, $day) = @_;
 
-    my ($fa_year, $fa_month, $fa_day) = split /-/, $subscription->{firstacquidate};
+    my @newissue; # ( yy, mm, dd )
+    my $delta_days = int( 365 / $freqdata->{issuesperunit} );
 
-    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);
+    if( $freqdata->{issuesperunit} == 1 ) {
+        # Add full years
+        @newissue = Add_Delta_YM( $year, $month, $day, $freqdata->{"unitsperissue"}, 0 );
+    } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
+        # Add rounded number of days based on frequency.
+        @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
         $subscription->{countissuesperunit}++;
+    } else {
+        # We finished a cycle of issues within a unit.
+        # Subtract delta * (issues - 1), add 1 year
+        @newissue = Add_Delta_Days( $year, $month, $day, -$delta_days * ($freqdata->{issuesperunit} - 1) );
+        @newissue = Add_Delta_YM( @newissue, 1, 0 );
+        $subscription->{countissuesperunit} = 1;
     }
-
-    return ($year, $month, $day);
+    return @newissue;
 }
 
 =head2 GetNextDate
 
-$resultdate = GetNextDate($publisheddate,$subscription)
+$resultdate = GetNextDate($publisheddate,$subscription,$freqdata,$updatecount)
 
 this function it takes the publisheddate and will return the next issue's date
 and will skip dates if there exists an irregularity.
 $publisheddate has to be an ISO date
-$subscription is a hashref containing at least 'periodicity', 'firstacquidate', 'irregularity', and 'countissuesperunit'
+$subscription is a hashref containing at least 'firstacquidate', 'irregularity', and 'countissuesperunit'
+$frequency is a hashref containing frequency informations
 $updatecount is a boolean value which, when set to true, update the 'countissuesperunit' in database
 - eg if periodicity is monthly and $publisheddate is 2007-02-10 but if March and April is to be
 skipped then the returned date will be 2007-05-10
@@ -2403,11 +2396,10 @@ Return undef if subscription is irregular
 =cut
 
 sub GetNextDate {
-    my ( $subscription, $publisheddate, $updatecount ) = @_;
+    my ( $subscription, $publisheddate, $freqdata, $updatecount ) = @_;
 
     return unless $subscription and $publisheddate;
 
-    my $freqdata = GetSubscriptionFrequency($subscription->{'periodicity'});
 
     if ($freqdata->{'unit'}) {
         my ( $year, $month, $day ) = split /-/, $publisheddate;
@@ -2427,7 +2419,7 @@ sub GetNextDate {
 
         # Get the 'fictive' next issue number
         # It is used to check if next issue is an irregular issue.
-        my $issueno = GetFictiveIssueNumber($subscription, $publisheddate) + 1;
+        my $issueno = GetFictiveIssueNumber($subscription, $publisheddate, $freqdata) + 1;
 
         # Then get the next date
         my $unit = lc $freqdata->{'unit'};
@@ -2490,11 +2482,13 @@ sub GetNextDate {
 _numeration returns the string corresponding to $value in the num_type
 num_type can take :
     -dayname
+    -dayabrv
     -monthname
+    -monthabrv
     -season
-=cut
+    -seasonabrv
 
-#'
+=cut
 
 sub _numeration {
     my ($value, $num_type, $locale) = @_;
@@ -2502,7 +2496,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(
@@ -2511,19 +2505,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->format_cldr( "LLLL" )
+            : $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;
     }
@@ -2531,29 +2533,12 @@ sub _numeration {
     return $string;
 }
 
-=head2 is_barcode_in_use
+=head2 CloseSubscription
 
-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
+Close a subscription given a subscriptionid
 
 =cut
 
-sub is_barcode_in_use {
-    my $barcode = shift;
-    my $dbh       = C4::Context->dbh;
-    my $occurrences = $dbh->selectall_arrayref(
-        'SELECT itemnumber from items where barcode = ?',
-        {}, $barcode
-
-    );
-
-    return @{$occurrences};
-}
-
-=head2 CloseSubscription
-Close a subscription given a subscriptionid
-=cut
 sub CloseSubscription {
     my ( $subscriptionid ) = @_;
     return unless $subscriptionid;
@@ -2576,8 +2561,11 @@ sub CloseSubscription {
 }
 
 =head2 ReopenSubscription
+
 Reopen a subscription given a subscriptionid
+
 =cut
+
 sub ReopenSubscription {
     my ( $subscriptionid ) = @_;
     return unless $subscriptionid;
@@ -2693,6 +2681,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__