Bug 10403: (follow-up) fix test to use vendor created earlier during test
[koha.git] / C4 / Serials.pm
index b8b53ed..6d188e7 100644 (file)
@@ -18,14 +18,18 @@ package C4::Serials;
 # with Koha; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
-use strict;
-use warnings;
+use Modern::Perl;
+
+use C4::Auth qw(haspermission);
+use C4::Context;
 use C4::Dates qw(format_date format_date_in_iso);
 use Date::Calc qw(:all);
-use POSIX qw(strftime);
+use POSIX qw(strftime setlocale LC_TIME);
 use C4::Biblio;
 use C4::Log;    # logaction
 use C4::Debug;
+use C4::Serials::Frequency;
+use C4::Serials::Numberpattern;
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 
@@ -39,8 +43,9 @@ BEGIN {
       &SearchSubscriptions
       &GetFullSubscriptionsFromBiblionumber   &GetFullSubscription &ModSubscriptionHistory
       &HasSubscriptionStrictlyExpired &HasSubscriptionExpired &GetExpirationDate &abouttoexpire
+      &GetSubscriptionHistoryFromSubscriptionId
 
-      &GetNextSeq         &NewIssue           &ItemizeSerials    &GetSerials
+      &GetNextSeq &GetSeq &NewIssue           &ItemizeSerials    &GetSerials
       &GetLatestSerials   &ModSerialStatus    &GetNextDate       &GetSerials2
       &ReNewSubscription  &GetLateIssues      &GetLateOrMissingIssues
       &GetSerialInformation                   &AddItem2Serial
@@ -118,6 +123,9 @@ name,title,planneddate,serialseq,serial.subscriptionid from tables : subscriptio
 
 sub GetLateIssues {
     my ($supplierid) = @_;
+
+    return unless ($supplierid);
+
     my $dbh = C4::Context->dbh;
     my $sth;
     if ($supplierid) {
@@ -150,10 +158,8 @@ sub GetLateIssues {
     }
     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;
+        $line->{title} = "" if $last_title and $line->{title} eq $last_title;
         $last_title = $line->{title} if ( $line->{title} );
         $line->{planneddate} = format_date( $line->{planneddate} );
         push @issuelist, $line;
@@ -163,20 +169,29 @@ sub GetLateIssues {
 
 =head2 GetSubscriptionHistoryFromSubscriptionId
 
-$sth = GetSubscriptionHistoryFromSubscriptionId()
-this function prepares the SQL request and returns the statement handle
-After this function, don't forget to execute it by using $sth->execute($subscriptionid)
+$history = GetSubscriptionHistoryFromSubscriptionId($subscriptionid);
+
+This function returns the subscription history as a hashref
 
 =cut
 
 sub GetSubscriptionHistoryFromSubscriptionId {
+    my ($subscriptionid) = @_;
+
+    return unless $subscriptionid;
+
     my $dbh   = C4::Context->dbh;
     my $query = qq|
         SELECT *
         FROM   subscriptionhistory
         WHERE  subscriptionid = ?
     |;
-    return $dbh->prepare($query);
+    my $sth = $dbh->prepare($query);
+    $sth->execute($subscriptionid);
+    my $results = $sth->fetchrow_hashref;
+    $sth->finish;
+
+    return $results;
 }
 
 =head2 GetSerialStatusFromSerialId
@@ -215,15 +230,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 = ?
     |;
@@ -262,6 +269,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;
 }
 
@@ -275,6 +283,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 );
@@ -292,6 +303,9 @@ 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 = "
@@ -320,15 +334,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
@@ -336,16 +342,12 @@ 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 );
+    return $subscription;
 }
 
 =head2 GetFullSubscription
@@ -357,6 +359,9 @@ sub GetSubscription {
 
 sub GetFullSubscription {
     my ($subscriptionid) = @_;
+
+    return unless ($subscriptionid);
+
     my $dbh              = C4::Context->dbh;
     my $query            = qq|
   SELECT    serial.serialid,
@@ -369,15 +374,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 )
@@ -391,7 +388,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
@@ -403,6 +404,9 @@ sub GetFullSubscription {
 
 sub PrepareSerialsData {
     my ($lines) = @_;
+
+    return unless ($lines);
+
     my %tmpresults;
     my $year;
     my @res;
@@ -419,9 +423,6 @@ sub PrepareSerialsData {
             if (!defined $subs->{$datefield} or $subs->{$datefield}=~m/^00/) {
                 $subs->{$datefield} = 'XXX';
             }
-            else {
-                $subs->{$datefield} = format_date( $subs->{$datefield}  );
-            }
         }
         $subs->{ "status" . $subs->{'status'} } = 1;
         $subs->{"checked"}                      = $subs->{'status'} =~ /1|3|4|7/;
@@ -462,6 +463,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.*,
@@ -489,13 +493,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} = '';
@@ -504,6 +501,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;
@@ -529,16 +527,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)
@@ -551,7 +540,11 @@ sub GetFullSubscriptionsFromBiblionumber {
           |;
     my $sth = $dbh->prepare($query);
     $sth->execute($biblionumber);
-    return $sth->fetchall_arrayref( {} );
+    my $subscriptions = $sth->fetchall_arrayref( {} );
+    for my $subscription ( @$subscriptions ) {
+        $subscription->{cannotedit} = not can_edit_subscription( $subscription );
+    }
+    return $subscriptions;
 }
 
 =head2 GetSubscriptions
@@ -570,7 +563,7 @@ sub GetSubscriptions {
     my $dbh = C4::Context->dbh;
     my $sth;
     my $sql = qq(
-            SELECT subscription.*, subscriptionhistory.*, biblio.title,biblioitems.issn,biblio.biblionumber
+            SELECT subscriptionhistory.*, subscription.*, biblio.title,biblioitems.issn,biblio.biblionumber
             FROM   subscription
             LEFT JOIN subscriptionhistory USING(subscriptionid)
             LEFT JOIN biblio ON biblio.biblionumber = subscription.biblionumber
@@ -626,19 +619,11 @@ sub GetSubscriptions {
     $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;
-    }
-    return @results;
+    my $subscriptions = $sth->fetchall_arrayref( {} );
+    for my $subscription ( @$subscriptions ) {
+        $subscription->{cannotedit} = not can_edit_subscription( $subscription );
+    }
+    return @$subscriptions;
 }
 
 =head2 SearchSubscriptions
@@ -657,7 +642,14 @@ sub SearchSubscriptions {
     my ( $args ) = @_;
 
     my $query = qq{
-        SELECT subscription.*, subscriptionhistory.*, biblio.*, biblioitems.issn
+        SELECT
+            subscription.notes AS publicnotes,
+            subscription.*,
+            subscriptionhistory.*,
+            biblio.notes AS biblionotes,
+            biblio.title,
+            biblio.author,
+            biblioitems.issn
         FROM subscription
             LEFT JOIN subscriptionhistory USING(subscriptionid)
             LEFT JOIN biblio ON biblio.biblionumber = subscription.biblionumber
@@ -716,6 +708,14 @@ sub SearchSubscriptions {
     my $results = $sth->fetchall_arrayref( {} );
     $sth->finish;
 
+    for my $subscription ( @$results ) {
+        $subscription->{cannotedit} = not can_edit_subscription( $subscription );
+        $subscription->{cannotdisplay} =
+            ( C4::Context->preference("IndependentBranches") &&
+              C4::Context->userenv &&
+              $subscription->{branchcode} ne C4::Context->userenv->{'branch'} ) ? 1 : 0;
+    }
+
     return @$results;
 }
 
@@ -733,6 +733,9 @@ FIXME: We should return \@serials.
 
 sub GetSerials {
     my ( $subscriptionid, $count ) = @_;
+
+    return unless $subscriptionid;
+
     my $dbh = C4::Context->dbh;
 
     # status = 2 is "arrived"
@@ -799,6 +802,9 @@ this number is used to see if a subscription can be deleted (=it must have only
 
 sub GetSerials2 {
     my ( $subscription, $status ) = @_;
+
+    return unless ($subscription and $status);
+
     my $dbh   = C4::Context->dbh;
     my $query = qq|
                  SELECT   serialid,serialseq, status, planneddate, publisheddate,notes, routingnotes
@@ -838,10 +844,13 @@ 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 $strsth = "SELECT   serialid,serialseq, status, planneddate, publisheddate, notes
                         FROM     serial
                         WHERE    subscriptionid = ?
                         AND      (status =2 or status=4)
@@ -853,6 +862,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;
     }
 
@@ -869,7 +879,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);
@@ -878,128 +891,140 @@ 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) =@_;
-#     my ($calculated,$newlastvalue1,$newlastvalue2,$newlastvalue3,$newinnerloop1,$newinnerloop2,$newinnerloop3);
-#     $calculated = $val->{numberingmethod};
-# # calculate the (expected) value of the next issue recieved.
-#     $newlastvalue1 = $val->{lastvalue1};
-# # check if we have to increase the new value.
-#     $newinnerloop1 = $val->{innerloop1}+1;
-#     $newinnerloop1=0 if ($newinnerloop1 >= $val->{every1});
-#     $newlastvalue1 += $val->{add1} if ($newinnerloop1<1); # <1 to be true when 0 or empty.
-#     $newlastvalue1=$val->{setto1} if ($newlastvalue1>$val->{whenmorethan1}); # reset counter if needed.
-#     $calculated =~ s/\{X\}/$newlastvalue1/g;
-#
-#     $newlastvalue2 = $val->{lastvalue2};
-# # check if we have to increase the new value.
-#     $newinnerloop2 = $val->{innerloop2}+1;
-#     $newinnerloop2=0 if ($newinnerloop2 >= $val->{every2});
-#     $newlastvalue2 += $val->{add2} if ($newinnerloop2<1); # <1 to be true when 0 or empty.
-#     $newlastvalue2=$val->{setto2} if ($newlastvalue2>$val->{whenmorethan2}); # reset counter if needed.
-#     $calculated =~ s/\{Y\}/$newlastvalue2/g;
-#
-#     $newlastvalue3 = $val->{lastvalue3};
-# # check if we have to increase the new value.
-#     $newinnerloop3 = $val->{innerloop3}+1;
-#     $newinnerloop3=0 if ($newinnerloop3 >= $val->{every3});
-#     $newlastvalue3 += $val->{add3} if ($newinnerloop3<1); # <1 to be true when 0 or empty.
-#     $newlastvalue3=$val->{setto3} if ($newlastvalue3>$val->{whenmorethan3}); # reset counter if needed.
-#     $calculated =~ s/\{Z\}/$newlastvalue3/g;
-#     return ($calculated,$newlastvalue1,$newlastvalue2,$newlastvalue3,$newinnerloop1,$newinnerloop2,$newinnerloop3);
-# }
-
 sub GetNextSeq {
-    my ($val) = @_;
-    my ( $calculated, $newlastvalue1, $newlastvalue2, $newlastvalue3, $newinnerloop1, $newinnerloop2, $newinnerloop3 );
-    my $pattern          = $val->{numberpattern};
-    my @seasons          = ( 'nothing', 'Winter', 'Spring', 'Summer', 'Autumn' );
-    my @southern_seasons = ( '', 'Summer', 'Autumn', 'Winter', 'Spring' );
-    $calculated    = $val->{numberingmethod};
-    $newlastvalue1 = $val->{lastvalue1};
-    $newlastvalue2 = $val->{lastvalue2};
-    $newlastvalue3 = $val->{lastvalue3};
-    $newlastvalue1 = $val->{lastvalue1};
-
-    # check if we have to increase the new value.
-    $newinnerloop1 = $val->{innerloop1} + 1;
-    $newinnerloop1 = 0 if ( $newinnerloop1 >= $val->{every1} );
-    $newlastvalue1 += $val->{add1} if ( $newinnerloop1 < 1 );    # <1 to be true when 0 or empty.
-    $newlastvalue1 = $val->{setto1} if ( $newlastvalue1 > $val->{whenmorethan1} );    # reset counter if needed.
-    $calculated =~ s/\{X\}/$newlastvalue1/g;
-
-    $newlastvalue2 = $val->{lastvalue2};
-
-    # check if we have to increase the new value.
-    $newinnerloop2 = $val->{innerloop2} + 1;
-    $newinnerloop2 = 0 if ( $newinnerloop2 >= $val->{every2} );
-    $newlastvalue2 += $val->{add2} if ( $newinnerloop2 < 1 );                         # <1 to be true when 0 or empty.
-    $newlastvalue2 = $val->{setto2} if ( $newlastvalue2 > $val->{whenmorethan2} );    # reset counter if needed.
-    if ( $pattern == 6 ) {
-        if ( $val->{hemisphere} == 2 ) {
-            my $newlastvalue2seq = $southern_seasons[$newlastvalue2];
-            $calculated =~ s/\{Y\}/$newlastvalue2seq/g;
-        } else {
-            my $newlastvalue2seq = $seasons[$newlastvalue2];
-            $calculated =~ s/\{Y\}/$newlastvalue2seq/g;
+    my ($subscription, $pattern, $planneddate) = @_;
+
+    return unless ($subscription and $pattern);
+
+    my ( $newlastvalue1, $newlastvalue2, $newlastvalue3,
+    $newinnerloop1, $newinnerloop2, $newinnerloop3 );
+    my $count = 1;
+
+    if ($subscription->{'skip_serialseq'}) {
+        my @irreg = split /;/, $subscription->{'irregularity'};
+        if(@irreg > 0) {
+            my $irregularities = {};
+            $irregularities->{$_} = 1 foreach(@irreg);
+            my $issueno = GetFictiveIssueNumber($subscription, $planneddate) + 1;
+            while($irregularities->{$issueno}) {
+                $count++;
+                $issueno++;
+            }
         }
-    } else {
-        $calculated =~ s/\{Y\}/$newlastvalue2/g;
     }
 
-    $newlastvalue3 = $val->{lastvalue3};
+    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 =~ /\{$_\}/);
+        }
 
-    # check if we have to increase the new value.
-    $newinnerloop3 = $val->{innerloop3} + 1;
-    $newinnerloop3 = 0 if ( $newinnerloop3 >= $val->{every3} );
-    $newlastvalue3 += $val->{add3} if ( $newinnerloop3 < 1 );    # <1 to be true when 0 or empty.
-    $newlastvalue3 = $val->{setto3} if ( $newlastvalue3 > $val->{whenmorethan3} );    # reset counter if needed.
-    $calculated =~ s/\{Z\}/$newlastvalue3/g;
+        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});
+            }
+        }
+        if($calc{'X'}) {
+            my $newlastvalue1string = _numeration( $newlastvalue1, $pattern->{numbering1}, $locale );
+            $calculated =~ s/\{X\}/$newlastvalue1string/g;
+        }
+        if($calc{'Y'}) {
+            my $newlastvalue2string = _numeration( $newlastvalue2, $pattern->{numbering2}, $locale );
+            $calculated =~ s/\{Y\}/$newlastvalue2string/g;
+        }
+        if($calc{'Z'}) {
+            my $newlastvalue3string = _numeration( $newlastvalue3, $pattern->{numbering3}, $locale );
+            $calculated =~ s/\{Z\}/$newlastvalue3string/g;
+        }
+    }
 
-    return ( $calculated, $newlastvalue1, $newlastvalue2, $newlastvalue3, $newinnerloop1, $newinnerloop2, $newinnerloop3 );
+    return ($calculated,
+            $newlastvalue1, $newlastvalue2, $newlastvalue3,
+            $newinnerloop1, $newinnerloop2, $newinnerloop3);
 }
 
 =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 $pattern = $val->{numberpattern};
-    my @seasons          = ( 'nothing', 'Winter', 'Spring', 'Summer', 'Autumn' );
-    my @southern_seasons = ( '',        'Summer', 'Autumn', 'Winter', 'Spring' );
-    my $calculated       = $val->{numberingmethod};
-    my $x                = $val->{'lastvalue1'};
-    $calculated =~ s/\{X\}/$x/g;
-    my $newlastvalue2 = $val->{'lastvalue2'};
-
-    if ( $pattern == 6 ) {
-        if ( $val->{hemisphere} == 2 ) {
-            my $newlastvalue2seq = $southern_seasons[$newlastvalue2];
-            $calculated =~ s/\{Y\}/$newlastvalue2seq/g;
-        } else {
-            my $newlastvalue2seq = $seasons[$newlastvalue2];
-            $calculated =~ s/\{Y\}/$newlastvalue2seq/g;
-        }
-    } else {
-        $calculated =~ s/\{Y\}/$newlastvalue2/g;
-    }
-    my $z = $val->{'lastvalue3'};
-    $calculated =~ s/\{Z\}/$z/g;
+    my ($subscription, $pattern) = @_;
+
+    return unless ($subscription and $pattern);
+
+    my $locale = $subscription->{locale};
+
+    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 = $subscription->{'lastvalue2'} || 0;
+    $newlastvalue2 = _numeration($newlastvalue2, $pattern->{numbering2}, $locale) if ($pattern->{numbering2}); # reset counter if needed.
+    $calculated =~ s/\{Y\}/$newlastvalue2/g;
+
+    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;
 }
 
@@ -1016,6 +1041,9 @@ the enddate or undef
 
 sub GetExpirationDate {
     my ( $subscriptionid, $startdate ) = @_;
+
+    return unless ($subscriptionid);
+
     my $dbh          = C4::Context->dbh;
     my $subscription = GetSubscription($subscriptionid);
     my $enddate;
@@ -1024,14 +1052,15 @@ sub GetExpirationDate {
     $enddate = $startdate || $subscription->{startdate};
     my @date = split( /-/, $enddate );
     return if ( scalar(@date) != 3 || not check_date(@date) );
-    if ( ( $subscription->{periodicity} % 16 ) > 0 ) {
+    my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
+    if ( $frequency and $frequency->{unit} ) {
 
         # If Not Irregular
         if ( my $length = $subscription->{numberlength} ) {
 
             #calculate the date of the last issue.
             for ( my $i = 1 ; $i <= $length ; $i++ ) {
-                $enddate = GetNextDate( $enddate, $subscription );
+                $enddate = GetNextDate( $subscription, $enddate );
             }
         } elsif ( $subscription->{monthlength} ) {
             if ( $$subscription{startdate} ) {
@@ -1044,10 +1073,12 @@ sub GetExpirationDate {
                 my @enddate = Add_Delta_Days( $date[0], $date[1], $date[2], $subscription->{weeklength} * 7 );
                 $enddate = sprintf( "%04d-%02d-%02d", $enddate[0], $enddate[1], $enddate[2] );
             }
+        } else {
+            $enddate = $subscription->{enddate};
         }
         return $enddate;
     } else {
-        return;
+        return $subscription->{enddate};
     }
 }
 
@@ -1062,6 +1093,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);
@@ -1080,20 +1114,67 @@ returns the number of rows affected
 =cut
 
 sub ModSubscriptionHistory {
-    my ( $subscriptionid, $histstartdate, $enddate, $recievedlist, $missinglist, $opacnote, $librariannote ) = @_;
+    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=?
                     WHERE subscriptionid=?
                 ";
     my $sth = $dbh->prepare($query);
-    $recievedlist =~ s/^; //;
-    $missinglist  =~ s/^; //;
-    $opacnote     =~ s/^; //;
-    $sth->execute( $histstartdate, $enddate, $recievedlist, $missinglist, $opacnote, $librariannote, $subscriptionid );
+    $receivedlist =~ s/^; // if $receivedlist;
+    $missinglist  =~ s/^; // if $missinglist;
+    $opacnote     =~ s/^; // if $opacnote;
+    $sth->execute( $histstartdate, $enddate, $receivedlist, $missinglist, $opacnote, $librariannote, $subscriptionid );
     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)
@@ -1106,22 +1187,31 @@ 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 :
     my $dbh   = C4::Context->dbh;
-    my $query = "SELECT subscriptionid,status FROM serial WHERE  serialid=?";
+    my $query = "SELECT serial.subscriptionid,serial.status,subscription.periodicity
+        FROM serial, subscription
+        WHERE serial.subscriptionid=subscription.subscriptionid
+            AND serialid=?";
     my $sth   = $dbh->prepare($query);
     $sth->execute($serialid);
-    my ( $subscriptionid, $oldstatus ) = $sth->fetchrow;
+    my ( $subscriptionid, $oldstatus, $periodicity ) = $sth->fetchrow;
+    my $frequency = GetSubscriptionFrequency($periodicity);
 
     # change status & update subscriptionhistory
     my $val;
     if ( $status == 6 ) {
-        DelIssue( {'serialid'=>$serialid, 'subscriptionid'=>$subscriptionid,'serialseq'=>$serialseq} );
-    }
-    else {
-        my $query =
-'UPDATE serial SET serialseq=?,publisheddate=?,planneddate=?,status=?,notes=? WHERE  serialid = ?';
+        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 );
         $query = "SELECT * FROM   subscription WHERE  subscriptionid = ?";
@@ -1129,58 +1219,45 @@ sub ModSerialStatus {
         $sth->execute($subscriptionid);
         my $val = $sth->fetchrow_hashref;
         unless ( $val->{manualhistory} ) {
-            $query = "SELECT missinglist,recievedlist FROM subscriptionhistory WHERE  subscriptionid=?";
-            $sth   = $dbh->prepare($query);
-            $sth->execute($subscriptionid);
-            my ( $missinglist, $recievedlist ) = $sth->fetchrow;
-            if ( $status == 2 ) {
-
-                $recievedlist .= "; $serialseq"
-                  unless ( index( "$recievedlist", "$serialseq" ) >= 0 );
+            if ( $status == 2 || ($oldstatus == 2 && $status != 2) ) {
+                  _update_receivedlist($subscriptionid);
+            }
+            if($status == 4 || $status == 5
+              || ($oldstatus == 4 && $status != 4)
+              || ($oldstatus == 5 && $status != 5)) {
+                _update_missinglist($subscriptionid);
             }
-
-            #         warn "missinglist : $missinglist serialseq :$serialseq, ".index("$missinglist","$serialseq");
-            $missinglist .= "; $serialseq"
-              if ( $status == 4
-                and not index( "$missinglist", "$serialseq" ) >= 0 );
-            $missinglist .= "; not issued $serialseq"
-              if ( $status == 5
-                and index( "$missinglist", "$serialseq" ) >= 0 );
-            $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 = "SELECT * FROM   subscription 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});
 
         # next issue number
         my (
             $newserialseq,  $newlastvalue1, $newlastvalue2, $newlastvalue3,
             $newinnerloop1, $newinnerloop2, $newinnerloop3
-        ) = GetNextSeq($val);
+          )
+          = GetNextSeq( $subscription, $pattern, $publisheddate );
 
         # next date (calculated from actual date & frequency parameters)
-        my $nextpublisheddate = GetNextDate( $publisheddate, $val );
-        NewIssue( $newserialseq, $subscriptionid, $val->{'biblionumber'}, 1, $nextpublisheddate, $nextpublisheddate );
+        my $nextpublisheddate = GetNextDate($subscription, $publisheddate, 1);
+        my $nextpubdate = $nextpublisheddate;
+        NewIssue( $newserialseq, $subscriptionid, $subscription->{'biblionumber'}, 1, $nextpubdate, $nextpubdate );
         $query = "UPDATE subscription SET lastvalue1=?, lastvalue2=?, lastvalue3=?, innerloop1=?, innerloop2=?, innerloop3=?
                     WHERE  subscriptionid = ?";
         $sth = $dbh->prepare($query);
         $sth->execute( $newlastvalue1, $newlastvalue2, $newlastvalue3, $newinnerloop1, $newinnerloop2, $newinnerloop3, $subscriptionid );
 
-# check if an alert must be sent... (= a letter is defined & status became "arrived"
-        if ( $val->{letter} && $status == 2 && $oldstatus != 2 ) {
+        # check if an alert must be sent... (= a letter is defined & status became "arrived"
+        if ( $subscription->{letter} && $status == 2 && $oldstatus != 2 ) {
             require C4::Letters;
-            C4::Letters::SendAlerts( 'issue', $val->{subscriptionid}, $val->{letter} );
+            C4::Letters::SendAlerts( 'issue', $subscription->{subscriptionid}, $subscription->{letter} );
         }
     }
+
     return;
 }
 
@@ -1194,31 +1271,50 @@ returns a hashref:
 
 $nextexepected = {
     serialid => int
-    planneddate => C4::Dates object
+    planneddate => ISO date
     }
 
 =cut
 
 sub GetNextExpected {
     my ($subscriptionid) = @_;
-    my $dbh              = C4::Context->dbh;
-    my $sth              = $dbh->prepare('SELECT serialid, planneddate FROM serial WHERE subscriptionid=? AND status=?');
+
+    my $dbh = C4::Context->dbh;
+    my $query = qq{
+        SELECT *
+        FROM serial
+        WHERE subscriptionid = ?
+          AND status = ?
+        LIMIT 1
+    };
+    my $sth = $dbh->prepare($query);
 
     # Each subscription has only one 'expected' issue, with serial.status==1.
     $sth->execute( $subscriptionid, 1 );
-    my ( $nextissue ) = $sth->fetchrow_hashref;
-    if( !$nextissue){
-         $sth = $dbh->prepare('SELECT serialid,planneddate FROM serial WHERE subscriptionid  = ? ORDER BY planneddate DESC LIMIT 1');
-         $sth->execute( $subscriptionid );  
-         $nextissue = $sth->fetchrow_hashref;       
+    my $nextissue = $sth->fetchrow_hashref;
+    if ( !$nextissue ) {
+        $query = qq{
+            SELECT *
+            FROM serial
+            WHERE subscriptionid = ?
+            ORDER BY publisheddate DESC
+            LIMIT 1
+        };
+        $sth = $dbh->prepare($query);
+        $sth->execute($subscriptionid);
+        $nextissue = $sth->fetchrow_hashref;
     }
-    if (!defined $nextissue->{planneddate}) {
-        # or should this default to 1st Jan ???
-        $nextissue->{planneddate} = strftime('%Y-%m-%d',localtime);
+    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;
     }
-    $nextissue->{planneddate} = C4::Dates->new($nextissue->{planneddate},'iso');
-    return $nextissue;
 
+    return $nextissue;
 }
 
 =head2 ModNextExpected
@@ -1228,7 +1324,7 @@ ModNextExpected($subscriptionid,$date)
 Update the planneddate for the current expected issue of the subscription.
 This will modify all future prediction results.  
 
-C<$date> is a C4::Dates object.
+C<$date> is an ISO date.
 
 returns 0
 
@@ -1242,11 +1338,42 @@ sub ModNextExpected {
     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->output('iso'), $date->output('iso'), $subscriptionid, 1 );
+    $sth->execute( $date, $date, $subscriptionid, 1 );
     return 0;
 
 }
 
+=head2 GetSubscriptionIrregularities
+
+=over 4
+
+=item @irreg = &GetSubscriptionIrregularities($subscriptionid);
+get the list of irregularities for a subscription
+
+=back
+
+=cut
+
+sub GetSubscriptionIrregularities {
+    my $subscriptionid = shift;
+
+    return unless $subscriptionid;
+
+    my $dbh = C4::Context->dbh;
+    my $query = qq{
+        SELECT irregularity
+        FROM subscription
+        WHERE subscriptionid = ?
+    };
+    my $sth = $dbh->prepare($query);
+    $sth->execute($subscriptionid);
+
+    my ($result) = $sth->fetchrow_array;
+    my @irreg = split /;/, $result;
+
+    return @irreg;
+}
+
 =head2 ModSubscription
 
 this function modifies a subscription. Put all new values on input args.
@@ -1255,43 +1382,41 @@ returns the number of rows affected
 =cut
 
 sub ModSubscription {
-    my ($auser,           $branchcode,      $aqbooksellerid,    $cost,             $aqbudgetid,    $startdate,   $periodicity,   $firstacquidate,
-        $dow,             $irregularity,    $numberpattern,     $numberlength,     $weeklength,    $monthlength, $add1,          $every1,
-        $whenmorethan1,   $setto1,          $lastvalue1,        $innerloop1,       $add2,          $every2,      $whenmorethan2, $setto2,
-        $lastvalue2,      $innerloop2,      $add3,              $every3,           $whenmorethan3, $setto3,      $lastvalue3,    $innerloop3,
-        $numberingmethod, $status,          $biblionumber,      $callnumber,       $notes,         $letter,      $hemisphere,    $manualhistory,
-        $internalnotes,   $serialsadditems, $staffdisplaycount, $opacdisplaycount, $graceperiod,   $location,    $enddate,       $subscriptionid
+    my (
+    $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,
+    $internalnotes, $serialsadditems, $staffdisplaycount, $opacdisplaycount,
+    $graceperiod, $location, $enddate, $subscriptionid, $skip_serialseq
     ) = @_;
 
-    #     warn $irregularity;
     my $dbh   = C4::Context->dbh;
     my $query = "UPDATE subscription
-                    SET librarian=?, branchcode=?,aqbooksellerid=?,cost=?,aqbudgetid=?,startdate=?,
-                        periodicity=?,firstacquidate=?,dow=?,irregularity=?, numberpattern=?, numberlength=?,weeklength=?,monthlength=?,
-                        add1=?,every1=?,whenmorethan1=?,setto1=?,lastvalue1=?,innerloop1=?,
-                        add2=?,every2=?,whenmorethan2=?,setto2=?,lastvalue2=?,innerloop2=?,
-                        add3=?,every3=?,whenmorethan3=?,setto3=?,lastvalue3=?,innerloop3=?,
-                        numberingmethod=?, status=?, biblionumber=?, callnumber=?, notes=?, 
-                                               letter=?, hemisphere=?,manualhistory=?,internalnotes=?,serialsadditems=?,
-                                               staffdisplaycount = ?,opacdisplaycount = ?, graceperiod = ?, location = ?
-                                               ,enddate=?
-                    WHERE subscriptionid = ?";
-
-    #warn "query :".$query;
+        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,
-        $dow,             "$irregularity", $numberpattern,  $numberlength,
-        $weeklength,      $monthlength,    $add1,           $every1,
-        $whenmorethan1,   $setto1,         $lastvalue1,     $innerloop1,
-        $add2,            $every2,         $whenmorethan2,  $setto2,
-        $lastvalue2,      $innerloop2,     $add3,           $every3,
-        $whenmorethan3,   $setto3,         $lastvalue3,     $innerloop3,
-        $numberingmethod, $status,         $biblionumber,   $callnumber,
-        $notes, $letter, $hemisphere, ( $manualhistory ? $manualhistory : 0 ),
+        $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,           $subscriptionid
+        $graceperiod,     $location,       $enddate,        $skip_serialseq,
+        $subscriptionid
     );
     my $rows = $sth->rows;
 
@@ -1302,12 +1427,11 @@ sub ModSubscription {
 =head2 NewSubscription
 
 $subscriptionid = &NewSubscription($auser,branchcode,$aqbooksellerid,$cost,$aqbudgetid,$biblionumber,
-    $startdate,$periodicity,$dow,$numberlength,$weeklength,$monthlength,
-    $add1,$every1,$whenmorethan1,$setto1,$lastvalue1,$innerloop1,
-    $add2,$every2,$whenmorethan2,$setto2,$lastvalue2,$innerloop2,
-    $add3,$every3,$whenmorethan3,$setto3,$lastvalue3,$innerloop3,
-    $numberingmethod, $status, $notes, $serialsadditems,
-    $staffdisplaycount, $opacdisplaycount, $graceperiod, $location, $enddate);
+    $startdate,$periodicity,$numberlength,$weeklength,$monthlength,
+    $lastvalue1,$innerloop1,$lastvalue2,$innerloop2,$lastvalue3,$innerloop3,
+    $status, $notes, $letter, $firstacquidate, $irregularity, $numberpattern,
+    $callnumber, $hemisphere, $manualhistory, $internalnotes, $serialsadditems,
+    $staffdisplaycount, $opacdisplaycount, $graceperiod, $location, $enddate, $skip_serialseq);
 
 Create a new subscription with value given on input args.
 
@@ -1317,42 +1441,44 @@ the id of this new subscription
 =cut
 
 sub NewSubscription {
-    my ($auser,         $branchcode,      $aqbooksellerid,    $cost,             $aqbudgetid,    $biblionumber, $startdate,       $periodicity,
-        $dow,           $numberlength,    $weeklength,        $monthlength,      $add1,          $every1,       $whenmorethan1,   $setto1,
-        $lastvalue1,    $innerloop1,      $add2,              $every2,           $whenmorethan2, $setto2,       $lastvalue2,      $innerloop2,
-        $add3,          $every3,          $whenmorethan3,     $setto3,           $lastvalue3,    $innerloop3,   $numberingmethod, $status,
-        $notes,         $letter,          $firstacquidate,    $irregularity,     $numberpattern, $callnumber,   $hemisphere,      $manualhistory,
-        $internalnotes, $serialsadditems, $staffdisplaycount, $opacdisplaycount, $graceperiod,   $location,     $enddate
+    my (
+    $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 $dbh = C4::Context->dbh;
 
     #save subscription (insert into database)
     my $query = qq|
         INSERT INTO subscription
-            (librarian,branchcode,aqbooksellerid,cost,aqbudgetid,biblionumber,
-            startdate,periodicity,dow,numberlength,weeklength,monthlength,
-            add1,every1,whenmorethan1,setto1,lastvalue1,innerloop1,
-            add2,every2,whenmorethan2,setto2,lastvalue2,innerloop2,
-            add3,every3,whenmorethan3,setto3,lastvalue3,innerloop3,
-            numberingmethod, status, notes, letter,firstacquidate,irregularity,
-            numberpattern, callnumber, hemisphere,manualhistory,internalnotes,serialsadditems,
-            staffdisplaycount,opacdisplaycount,graceperiod,location,enddate)
-        VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
+            (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,
-        $dow,           $numberlength,    $weeklength,        $monthlength,      $add1,          $every1,       $whenmorethan1,   $setto1,
-        $lastvalue1,    $innerloop1,      $add2,              $every2,           $whenmorethan2, $setto2,       $lastvalue2,      $innerloop2,
-        $add3,          $every3,          $whenmorethan3,     $setto3,           $lastvalue3,    $innerloop3,   $numberingmethod, "$status",
-        $notes,         $letter,          $firstacquidate,    $irregularity,     $numberpattern, $callnumber,   $hemisphere,      $manualhistory,
-        $internalnotes, $serialsadditems, $staffdisplaycount, $opacdisplaycount, $graceperiod,   $location,     $enddate
+        $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'};
-    unless ($enddate){
-       $enddate = GetExpirationDate($subscriptionid,$startdate);
-        $query = q|
+    unless ($enddate) {
+        $enddate = GetExpirationDate( $subscriptionid, $startdate );
+        $query = qq|
             UPDATE subscription
             SET    enddate=?
             WHERE  subscriptionid=?
@@ -1360,7 +1486,8 @@ sub NewSubscription {
         $sth = $dbh->prepare($query);
         $sth->execute( $enddate, $subscriptionid );
     }
-    #then create the 1st waited number
+
+    # then create the 1st expected number
     $query = qq(
         INSERT INTO subscriptionhistory
             (biblionumber, subscriptionid, histstartdate,  opacnote, librariannote)
@@ -1370,17 +1497,11 @@ sub NewSubscription {
     $sth->execute( $biblionumber, $subscriptionid, $startdate, $notes, $internalnotes );
 
     # reread subscription to get a hash (for calculation of the 1st issue number)
-    $query = qq(
-        SELECT *
-        FROM   subscription
-        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);
     $query = qq|
         INSERT INTO serial
             (serialseq,subscriptionid,biblionumber,status, planneddate, publisheddate)
@@ -1393,7 +1514,7 @@ sub NewSubscription {
 
     #set serial flag on biblio if not already set.
     my $bib = GetBiblio($biblionumber);
-    if ( !$bib->{'serial'} ) {
+    if ( $bib and !$bib->{'serial'} ) {
         my $record = GetMarcBiblio($biblionumber);
         my ( $tag, $subf ) = GetMarcFromKohaField( 'biblio.serial', $bib->{'frameworkcode'} );
         if ($tag) {
@@ -1482,6 +1603,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
@@ -1534,6 +1657,9 @@ return :
 
 sub ItemizeSerials {
     my ( $serialid, $info ) = @_;
+
+    return unless ($serialid);
+
     my $now = POSIX::strftime( "%Y-%m-%d", localtime );
 
     my $dbh   = C4::Context->dbh;
@@ -1668,6 +1794,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);
@@ -1709,9 +1838,13 @@ return :
 
 sub HasSubscriptionExpired {
     my ($subscriptionid) = @_;
+
+    return unless ($subscriptionid);
+
     my $dbh              = C4::Context->dbh;
     my $subscription     = GetSubscription($subscriptionid);
-    if ( ( $subscription->{periodicity} % 16 ) > 0 ) {
+    my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
+    if ( $frequency and $frequency->{unit} ) {
         my $expirationdate = $subscription->{enddate} || GetExpirationDate($subscriptionid);
         if (!defined $expirationdate) {
             $expirationdate = q{};
@@ -1735,6 +1868,7 @@ sub HasSubscriptionExpired {
             || ( !$res ) );
         return 0;
     } else {
+        # Irregular
         if ( $subscription->{'numberlength'} ) {
             my $countreceived = countissuesfrom( $subscriptionid, $subscription->{'startdate'} );
             return 1 if ( $countreceived > $subscription->{'numberlength'} );
@@ -1844,6 +1978,9 @@ 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 = '';
@@ -1918,6 +2055,9 @@ called when a missing issue is found from the serials-recieve.pl file
 
 sub removeMissingIssue {
     my ( $sequence, $subscriptionid ) = @_;
+
+    return unless ($sequence and $subscriptionid);
+
     my $dbh = C4::Context->dbh;
     my $sth = $dbh->prepare("SELECT * FROM subscriptionhistory WHERE subscriptionid = ?");
     $sth->execute($subscriptionid);
@@ -2003,6 +2143,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 
@@ -2028,6 +2171,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 = ?" );
@@ -2226,29 +2372,17 @@ sub abouttoexpire {
     my $dbh              = C4::Context->dbh;
     my $subscription     = GetSubscription($subscriptionid);
     my $per = $subscription->{'periodicity'};
-    if ($per && $per % 16 > 0){
-        my $expirationdate   = GetExpirationDate($subscriptionid);
+    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 @res;
-        if (defined $res) {
-            @res=split (/-/,$res);
-            @res=Date::Calc::Today if ($res[0]*$res[1]==0);
-        } else { # default an undefined value
-            @res=Date::Calc::Today;
+        my $nextdate = GetNextDate($subscription, $res);
+        if(Date::Calc::Delta_Days(
+            split( /-/, $nextdate ),
+            split( /-/, $expirationdate )
+        ) <= 0) {
+            return 1;
         }
-        my @endofsubscriptiondate=split(/-/,$expirationdate);
-        my @per_list = (0, 7, 7, 14, 21, 31, 62, 93, 93, 190, 365, 730, 0, 124, 0, 0);
-        my @datebeforeend;
-        @datebeforeend = Add_Delta_Days(  $endofsubscriptiondate[0],$endofsubscriptiondate[1],$endofsubscriptiondate[2],
-            - (3 * $per_list[$per])) if (@endofsubscriptiondate && $endofsubscriptiondate[0]*$endofsubscriptiondate[1]*$endofsubscriptiondate[2]);
-        return 1 if ( @res &&
-            (@datebeforeend &&
-                Delta_Days($res[0],$res[1],$res[2],
-                    $datebeforeend[0],$datebeforeend[1],$datebeforeend[2]) <= 0) &&
-            (@endofsubscriptiondate &&
-                Delta_Days($res[0],$res[1],$res[2],
-                    $endofsubscriptiondate[0],$endofsubscriptiondate[1],$endofsubscriptiondate[2]) >= 0) );
-        return 0;
     } elsif ($subscription->{numberlength}>0) {
         return (countissuesfrom($subscriptionid,$subscription->{'startdate'}) >=$subscription->{numberlength}-1);
     }
@@ -2299,170 +2433,277 @@ sub GetSubscriptionsFromBorrower {
     return ( $count, @routinglist );
 }
 
-=head2 GetNextDate
-
-$resultdate = GetNextDate($planneddate,$subscription)
 
-this function it takes the planneddate and will return the next issue's date and will skip dates if there
-exists an irregularity
-- eg if periodicity is monthly and $planneddate is 2007-02-10 but if March and April is to be 
-skipped then the returned date will be 2007-05-10
+=head2 GetFictiveIssueNumber
 
-return :
-$resultdate - then next date in the sequence
+$issueno = GetFictiveIssueNumber($subscription, $publishedate);
 
-Return 0 if periodicity==0
+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...
+This issuenumber doesn't take into account irregularities, so, for instance, if the 3rd
+issue is declared as 'irregular' (will be skipped at receipt), the next issue number
+will be 4, not 3. It's why it is called 'fictive'. It is NOT a serial seq, and is not
+depending on how many rows are in serial table.
+The issue number calculation is based on subscription frequency, first acquisition
+date, and $publisheddate.
 
 =cut
 
-sub GetNextDate {
-    my ( $planneddate, $subscription ) = @_;
-    my @irreg = split( /\,/, $subscription->{irregularity} );
+sub GetFictiveIssueNumber {
+    my ($subscription, $publisheddate) = @_;
+
+    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 $issueno;
+}
 
-    #date supposed to be in ISO.
+=head2 GetNextDate
 
-    my ( $year, $month, $day ) = split( /-/, $planneddate );
-    $month = 1 unless ($month);
-    $day   = 1 unless ($day);
-    my @resultdate;
+$resultdate = GetNextDate($publisheddate,$subscription)
 
-    #       warn "DOW $dayofweek";
-    if ( $subscription->{periodicity} % 16 == 0 ) {    # 'without regularity' || 'irregular'
-        return 0;
-    }
+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'
+$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
 
-    #   daily : n / week
-    #   Since we're interpreting irregularity here as which days of the week to skip an issue,
-    #   renaming this pattern from 1/day to " n / week ".
-    if ( $subscription->{periodicity} == 1 ) {
-        my $dayofweek = eval { Day_of_Week( $year, $month, $day ) };
-        if ($@) { warn "year month day : $year $month $day $subscription->{subscriptionid} : $@"; }
-        else {
-            for ( my $i = 0 ; $i < @irreg ; $i++ ) {
-                $dayofweek = 0 if ( $dayofweek == 7 );
-                if ( in_array( ( $dayofweek + 1 ), @irreg ) ) {
-                    ( $year, $month, $day ) = Add_Delta_Days( $year, $month, $day, 1 );
-                    $dayofweek++;
-                }
-            }
-            @resultdate = Add_Delta_Days( $year, $month, $day, 1 );
-        }
-    }
+return :
+$resultdate - then next date in the sequence (ISO date)
 
-    #   1  week
-    if ( $subscription->{periodicity} == 2 ) {
-        my ( $wkno, $year ) = eval { Week_of_Year( $year, $month, $day ) };
-        if ($@) { warn "year month day : $year $month $day $subscription->{subscriptionid} : $@"; }
-        else {
-            for ( my $i = 0 ; $i < @irreg ; $i++ ) {
+Return $publisheddate if subscription is irregular
 
-                #FIXME: if two consecutive irreg, do we only skip one?
-                if ( $irreg[$i] == ( ( $wkno != 51 ) ? ( $wkno + 1 ) % 52 : 52 ) ) {
-                    ( $year, $month, $day ) = Add_Delta_Days( $year, $month, $day, 7 );
-                    $wkno = ( ( $wkno != 51 ) ? ( $wkno + 1 ) % 52 : 52 );
-                }
-            }
-            @resultdate = Add_Delta_Days( $year, $month, $day, 7 );
-        }
-    }
+=cut
 
-    #   1 / 2 weeks
-    if ( $subscription->{periodicity} == 3 ) {
-        my ( $wkno, $year ) = eval { Week_of_Year( $year, $month, $day ) };
-        if ($@) { warn "year month day : $year $month $day $subscription->{subscriptionid} : $@"; }
-        else {
-            for ( my $i = 0 ; $i < @irreg ; $i++ ) {
-                if ( $irreg[$i] == ( ( $wkno != 50 ) ? ( $wkno + 2 ) % 52 : 52 ) ) {
-                    ### BUGFIX was previously +1 ^
-                    ( $year, $month, $day ) = Add_Delta_Days( $year, $month, $day, 14 );
-                    $wkno = ( ( $wkno != 50 ) ? ( $wkno + 2 ) % 52 : 52 );
-                }
-            }
-            @resultdate = Add_Delta_Days( $year, $month, $day, 14 );
+sub GetNextDate {
+    my ( $subscription, $publisheddate, $updatecount ) = @_;
+
+    my $freqdata = GetSubscriptionFrequency($subscription->{'periodicity'});
+
+    if ($freqdata->{'unit'}) {
+        my ( $year, $month, $day ) = split /-/, $publisheddate;
+
+        # Process an irregularity Hash
+        # Suppose that irregularities are stored in a string with this structure
+        # 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;
         }
-    }
 
-    #   1 / 3 weeks
-    if ( $subscription->{periodicity} == 4 ) {
-        my ( $wkno, $year ) = eval { Week_of_Year( $year, $month, $day ) };
-        if ($@) { warn "année mois jour : $year $month $day $subscription->{subscriptionid} : $@"; }
-        else {
-            for ( my $i = 0 ; $i < @irreg ; $i++ ) {
-                if ( $irreg[$i] == ( ( $wkno != 49 ) ? ( $wkno + 3 ) % 52 : 52 ) ) {
-                    ( $year, $month, $day ) = Add_Delta_Days( $year, $month, $day, 21 );
-                    $wkno = ( ( $wkno != 49 ) ? ( $wkno + 3 ) % 52 : 52 );
+        # Get the 'fictive' next issue number
+        # It is used to check if next issue is an irregular issue.
+        my $issueno = GetFictiveIssueNumber($subscription, $publisheddate) + 1;
+
+        # Then get the next date
+        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'}++;
                 }
+                $issueno++;
+            }
+            if ($subscription->{'countissuesperunit'} + 1 > $freqdata->{'issuesperunit'}){
+                ($year,$month,$day) = Add_Delta_Days($year,$month, $day , $freqdata->{"unitsperissue"} );
+                $subscription->{'countissuesperunit'} = 1;
+            } else {
+                $subscription->{'countissuesperunit'}++;
             }
-            @resultdate = Add_Delta_Days( $year, $month, $day, 21 );
         }
-    }
-    my $tmpmonth = $month;
-    if ( $year && $month && $day ) {
-        if ( $subscription->{periodicity} == 5 ) {
-            for ( my $i = 0 ; $i < @irreg ; $i++ ) {
-                if ( $irreg[$i] == ( ( $tmpmonth != 11 ) ? ( $tmpmonth + 1 ) % 12 : 12 ) ) {
-                    ( $year, $month, $day ) = Add_Delta_YMD( $year, $month, $day, 0, 1, 0 );
-                    $tmpmonth = ( ( $tmpmonth != 11 ) ? ( $tmpmonth + 1 ) % 12 : 12 );
+        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'}++;
                 }
+                $issueno++;
             }
-            @resultdate = Add_Delta_YMD( $year, $month, $day, 0, 1, 0 );
-        }
-        if ( $subscription->{periodicity} == 6 ) {
-            for ( my $i = 0 ; $i < @irreg ; $i++ ) {
-                if ( $irreg[$i] == ( ( $tmpmonth != 10 ) ? ( $tmpmonth + 2 ) % 12 : 12 ) ) {
-                    ( $year, $month, $day ) = Add_Delta_YMD( $year, $month, $day, 0, 2, 0 );
-                    $tmpmonth = ( ( $tmpmonth != 10 ) ? ( $tmpmonth + 2 ) % 12 : 12 );
+            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'}++;
             }
-            @resultdate = Add_Delta_YMD( $year, $month, $day, 0, 2, 0 );
         }
-        if ( $subscription->{periodicity} == 7 ) {
-            for ( my $i = 0 ; $i < @irreg ; $i++ ) {
-                if ( $irreg[$i] == ( ( $tmpmonth != 9 ) ? ( $tmpmonth + 3 ) % 12 : 12 ) ) {
-                    ( $year, $month, $day ) = Add_Delta_YMD( $year, $month, $day, 0, 3, 0 );
-                    $tmpmonth = ( ( $tmpmonth != 9 ) ? ( $tmpmonth + 3 ) % 12 : 12 );
+        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'}++;
                 }
+                $issueno++;
             }
-            @resultdate = Add_Delta_YMD( $year, $month, $day, 0, 3, 0 );
-        }
-        if ( $subscription->{periodicity} == 8 ) {
-            for ( my $i = 0 ; $i < @irreg ; $i++ ) {
-                if ( $irreg[$i] == ( ( $tmpmonth != 9 ) ? ( $tmpmonth + 3 ) % 12 : 12 ) ) {
-                    ( $year, $month, $day ) = Add_Delta_YMD( $year, $month, $day, 0, 3, 0 );
-                    $tmpmonth = ( ( $tmpmonth != 9 ) ? ( $tmpmonth + 3 ) % 12 : 12 );
+            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'}++;
             }
-            @resultdate = Add_Delta_YMD( $year, $month, $day, 0, 3, 0 );
         }
-        if ( $subscription->{periodicity} == 13 ) {
-            for ( my $i = 0 ; $i < @irreg ; $i++ ) {
-                if ( $irreg[$i] == ( ( $tmpmonth != 8 ) ? ( $tmpmonth + 4 ) % 12 : 12 ) ) {
-                    ( $year, $month, $day ) = Add_Delta_YMD( $year, $month, $day, 0, 4, 0 );
-                    $tmpmonth = ( ( $tmpmonth != 8 ) ? ( $tmpmonth + 4 ) % 12 : 12 );
+        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'}++;
                 }
+                $issueno++;
             }
-            @resultdate = Add_Delta_YMD( $year, $month, $day, 0, 4, 0 );
-        }
-        if ( $subscription->{periodicity} == 9 ) {
-            for ( my $i = 0 ; $i < @irreg ; $i++ ) {
-                if ( $irreg[$i] == ( ( $tmpmonth != 9 ) ? ( $tmpmonth + 3 ) % 12 : 12 ) ) {
-                    ### BUFIX Seems to need more Than One ?
-                    ( $year, $month, $day ) = Add_Delta_YM( $year, $month, $day, 0, 6 );
-                    $tmpmonth = ( ( $tmpmonth != 6 ) ? ( $tmpmonth + 6 ) % 12 : 12 );
+            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'}++;
             }
-            @resultdate = Add_Delta_YM( $year, $month, $day, 0, 6 );
         }
-        if ( $subscription->{periodicity} == 10 ) {
-            @resultdate = Add_Delta_YM( $year, $month, $day, 1, 0 );
-        }
-        if ( $subscription->{periodicity} == 11 ) {
-            @resultdate = Add_Delta_YM( $year, $month, $day, 2, 0 );
+        if ($updatecount){
+            my $dbh = C4::Context->dbh;
+            my $query = qq{
+                UPDATE subscription
+                SET countissuesperunit = ?
+                WHERE subscriptionid = ?
+            };
+            my $sth = $dbh->prepare($query);
+            $sth->execute($subscription->{'countissuesperunit'}, $subscription->{'subscriptionid'});
         }
+        return sprintf("%04d-%02d-%02d", $year, $month, $day);
+    }
+    else {
+        return $publisheddate;
     }
-    my $resultdate = sprintf( "%04d-%02d-%02d", $resultdate[0], $resultdate[1], $resultdate[2] );
+}
+
+=head2 _numeration
+
+  $string = &_numeration($value,$num_type,$locale);
 
-    return "$resultdate";
+_numeration returns the string corresponding to $value in the num_type
+num_type can take :
+    -dayname
+    -monthname
+    -season
+=cut
+
+#'
+
+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);
+    }
+    return $string;
 }
 
 =head2 is_barcode_in_use
@@ -2555,6 +2796,33 @@ sub subscriptionCurrentlyOnOrder {
     return $sth->fetchrow_array;
 }
 
+=head2 can_edit_subscription
+
+    $can = can_edit_subscription( $subscriptionid[, $userid] );
+
+Return 1 if the subscription is editable by the current logged user (or a given $userid), else 0.
+
+=cut
+
+sub can_edit_subscription {
+    my ( $subscription, $userid ) = @_;
+    return 0 unless C4::Context->userenv;
+    my $flags = C4::Context->userenv->{flags};
+    $userid ||= C4::Context->userenv->{'id'};
+    my $independent_branches = C4::Context->preference('IndependentBranches');
+    return 1 unless $independent_branches;
+    if( $flags % 2 == 1 # superlibrarian
+        or C4::Auth::haspermission( $userid, {serials => 'superserials'}),
+        or C4::Auth::haspermission( $userid, {serials => 'edit_subscription'}),
+        or not defined $subscription->{branchcode}
+        or $subscription->{branchcode} eq ''
+        or $subscription->{branchcode} eq C4::Context->userenv->{'branch'}
+    ) {
+        return 1;
+    }
+     return 0;
+}
+
 1;
 __END__