3 # Copyright 2000-2002 Katipo Communications
4 # Parts Copyright 2010 Biblibre
6 # This file is part of Koha.
8 # Koha is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # Koha is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with Koha; if not, see <http://www.gnu.org/licenses>.
33 use POSIX qw( strftime );
34 use Scalar::Util qw( looks_like_number );
37 use C4::Auth qw( haspermission );
38 use C4::Biblio qw( GetMarcFromKohaField ModBiblio );
40 use C4::Log qw( logaction ); # logaction
41 use C4::Serials::Frequency qw( GetSubscriptionFrequency );
42 use C4::Serials::Numberpattern;
43 use Koha::AdditionalFieldValues;
46 use Koha::SharedContent;
47 use Koha::Subscription::Histories;
48 use Koha::Subscriptions;
49 use Koha::Suggestions;
50 use Koha::TemplateUtils qw( process_tt );
58 MISSING_NEVER_RECIEVED => 41,
59 MISSING_SOLD_OUT => 42,
60 MISSING_DAMAGED => 43,
68 use constant MISSING_STATUSES => (
69 MISSING, MISSING_NEVER_RECIEVED,
70 MISSING_SOLD_OUT, MISSING_DAMAGED,
74 our (@ISA, @EXPORT_OK);
79 NewSubscription ModSubscription DelSubscription
80 GetSubscription CountSubscriptionFromBiblionumber GetSubscriptionsFromBiblionumber
82 GetFullSubscriptionsFromBiblionumber GetFullSubscription ModSubscriptionHistory
83 HasSubscriptionStrictlyExpired HasSubscriptionExpired GetExpirationDate abouttoexpire
85 GetSubscriptionHistoryFromSubscriptionId
87 GetNextSeq GetSeq NewIssue GetSerials
88 GetLatestSerials ModSerialStatus GetNextDate
89 CloseSubscription ReopenSubscription
90 subscriptionCurrentlyOnOrder
91 can_claim_subscription can_edit_subscription can_show_subscription
93 GetSubscriptionLength ReNewSubscription GetLateOrMissingIssues
94 GetSerialInformation AddItem2Serial
95 PrepareSerialsData GetNextExpected ModNextExpected
96 GetSubscriptionIrregularities
99 GetSuppliersWithLateIssues
100 getroutinglist delroutingmember addroutingmember
102 check_routing updateClaim
113 C4::Serials - Serials Module Functions
121 Functions for handling subscriptions, claims routing etc.
126 =head2 GetSuppliersWithLateIssues
128 $supplierlist = GetSuppliersWithLateIssues()
130 this function get all suppliers with late issues.
133 an array_ref of suppliers each entry is a hash_ref containing id and name
134 the array is in name order
138 sub GetSuppliersWithLateIssues {
139 my $dbh = C4::Context->dbh;
140 my $statuses = join(',', ( LATE, MISSING_STATUSES, CLAIMED ) );
142 SELECT DISTINCT id, name
144 LEFT JOIN serial ON serial.subscriptionid=subscription.subscriptionid
145 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
148 (planneddate < now() AND serial.status=1)
149 OR serial.STATUS IN ( $statuses )
151 AND subscription.closed = 0
153 return $dbh->selectall_arrayref($query, { Slice => {} });
156 =head2 GetSubscriptionHistoryFromSubscriptionId
158 $history = GetSubscriptionHistoryFromSubscriptionId($subscriptionid);
160 This function returns the subscription history as a hashref
164 sub GetSubscriptionHistoryFromSubscriptionId {
165 my ($subscriptionid) = @_;
167 return unless $subscriptionid;
169 my $dbh = C4::Context->dbh;
172 FROM subscriptionhistory
173 WHERE subscriptionid = ?
175 my $sth = $dbh->prepare($query);
176 $sth->execute($subscriptionid);
177 my $results = $sth->fetchrow_hashref;
183 =head2 GetSerialInformation
185 $data = GetSerialInformation($serialid);
186 returns a hash_ref containing :
187 items : items marcrecord (can be an array)
189 subscription table field
190 + information about subscription expiration
194 sub GetSerialInformation {
196 my $dbh = C4::Context->dbh;
198 SELECT serial.*, serial.notes as sernotes, serial.status as serstatus,subscription.*,subscription.subscriptionid as subsid
199 FROM serial LEFT JOIN subscription ON subscription.subscriptionid=serial.subscriptionid
202 my $rq = $dbh->prepare($query);
203 $rq->execute($serialid);
204 my $data = $rq->fetchrow_hashref;
206 # create item information if we have serialsadditems for this subscription
207 if ( $data->{'serialsadditems'} ) {
208 my $queryitem = $dbh->prepare("SELECT itemnumber from serialitems where serialid=?");
209 $queryitem->execute($serialid);
210 my $itemnumbers = $queryitem->fetchall_arrayref( [0] );
212 if ( scalar(@$itemnumbers) > 0 ) {
213 foreach my $itemnum (@$itemnumbers) {
215 #It is ASSUMED that GetMarcItem ALWAYS WORK...
216 #Maybe GetMarcItem should return values on failure
217 my $itemprocessed = C4::Items::PrepareItemrecordDisplay( $data->{'biblionumber'}, $itemnum->[0], $data );
218 $itemprocessed->{'itemnumber'} = $itemnum->[0];
219 $itemprocessed->{'itemid'} = $itemnum->[0];
220 $itemprocessed->{'serialid'} = $serialid;
221 $itemprocessed->{'biblionumber'} = $data->{'biblionumber'};
222 push @{ $data->{'items'} }, $itemprocessed;
225 my $itemprocessed = C4::Items::PrepareItemrecordDisplay( $data->{'biblionumber'}, '', $data );
226 $itemprocessed->{'itemid'} = "N$serialid";
227 $itemprocessed->{'serialid'} = $serialid;
228 $itemprocessed->{'biblionumber'} = $data->{'biblionumber'};
229 $itemprocessed->{'countitems'} = 0;
230 push @{ $data->{'items'} }, $itemprocessed;
233 $data->{ "status" . $data->{'serstatus'} } = 1;
234 $data->{'subscriptionexpired'} = HasSubscriptionExpired( $data->{'subscriptionid'} ) && $data->{'status'} == 1;
235 $data->{'abouttoexpire'} = abouttoexpire( $data->{'subscriptionid'} );
236 $data->{cannotedit} = not can_edit_subscription( $data );
240 =head2 AddItem2Serial
242 $rows = AddItem2Serial($serialid,$itemnumber);
243 Adds an itemnumber to Serial record
244 returns the number of rows affected
249 my ( $serialid, $itemnumber ) = @_;
251 return unless ($serialid and $itemnumber);
253 my $dbh = C4::Context->dbh;
254 my $rq = $dbh->prepare("INSERT INTO `serialitems` SET serialid=? , itemnumber=?");
255 $rq->execute( $serialid, $itemnumber );
259 =head2 GetSubscription
261 $subs = GetSubscription($subscriptionid)
262 this function returns the subscription which has $subscriptionid as id.
264 a hashref. This hash contains
265 subscription, subscriptionhistory, aqbooksellers.name, biblio.title
269 sub GetSubscription {
270 my ($subscriptionid) = @_;
271 my $dbh = C4::Context->dbh;
273 SELECT subscription.*,
274 subscriptionhistory.*,
275 aqbooksellers.name AS aqbooksellername,
276 biblio.title AS bibliotitle,
277 biblio.subtitle AS bibliosubtitle,
278 subscription.biblionumber as bibnum
280 LEFT JOIN subscriptionhistory ON subscription.subscriptionid=subscriptionhistory.subscriptionid
281 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid=aqbooksellers.id
282 LEFT JOIN biblio ON biblio.biblionumber=subscription.biblionumber
283 WHERE subscription.subscriptionid = ?
286 my $sth = $dbh->prepare($query);
287 $sth->execute($subscriptionid);
288 my $subscription = $sth->fetchrow_hashref;
290 return unless $subscription;
292 $subscription->{cannotedit} = not can_edit_subscription( $subscription );
294 if ( my $mana_id = $subscription->{mana_id} ) {
295 my $mana_subscription = Koha::SharedContent::get_entity_by_id(
296 'subscription', $mana_id, {usecomments => 1});
297 $subscription->{comments} = $mana_subscription->{data}->{comments};
300 return $subscription;
303 =head2 GetFullSubscription
305 $array_ref = GetFullSubscription($subscriptionid)
306 this function reads the serial table.
310 sub GetFullSubscription {
311 my ($subscriptionid) = @_;
313 return unless ($subscriptionid);
315 my $dbh = C4::Context->dbh;
317 SELECT serial.serialid,
320 serial.publisheddate,
321 serial.publisheddatetext,
323 serial.notes as notes,
324 year(IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate)) as year,
325 aqbooksellers.name as aqbooksellername,
326 biblio.title as bibliotitle,
327 subscription.branchcode AS branchcode,
328 subscription.subscriptionid AS subscriptionid
330 LEFT JOIN subscription ON
331 (serial.subscriptionid=subscription.subscriptionid )
332 LEFT JOIN aqbooksellers on subscription.aqbooksellerid=aqbooksellers.id
333 LEFT JOIN biblio on biblio.biblionumber=subscription.biblionumber
334 WHERE serial.subscriptionid = ?
336 IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate) DESC,
337 serial.subscriptionid
339 my $sth = $dbh->prepare($query);
340 $sth->execute($subscriptionid);
341 my $subscriptions = $sth->fetchall_arrayref( {} );
342 if (scalar @$subscriptions) {
343 my $cannotedit = not can_edit_subscription( $subscriptions->[0] );
344 for my $subscription ( @$subscriptions ) {
345 $subscription->{cannotedit} = $cannotedit;
349 return $subscriptions;
352 =head2 PrepareSerialsData
354 $array_ref = PrepareSerialsData($serialinfomation)
355 where serialinformation is a hashref array
359 sub PrepareSerialsData {
362 return unless ($lines);
369 my $previousnote = "";
371 foreach my $subs (@{$lines}) {
372 $subs->{ "status" . $subs->{'status'} } = 1;
373 if ( grep { $_ == $subs->{status} } ( EXPECTED, LATE, MISSING_STATUSES, CLAIMED ) ) {
374 $subs->{"checked"} = 1;
377 if ( $subs->{'year'} && $subs->{'year'} ne "" ) {
378 $year = $subs->{'year'};
382 if ( $tmpresults{$year} ) {
383 push @{ $tmpresults{$year}->{'serials'} }, $subs;
385 $tmpresults{$year} = {
387 'aqbooksellername' => $subs->{'aqbooksellername'},
388 'bibliotitle' => $subs->{'bibliotitle'},
389 'serials' => [$subs],
394 foreach my $key ( sort { $b cmp $a } keys %tmpresults ) {
395 push @res, $tmpresults{$key};
400 =head2 GetSubscriptionsFromBiblionumber
402 $array_ref = GetSubscriptionsFromBiblionumber($biblionumber)
403 this function get the subscription list. it reads the subscription table.
405 reference to an array of subscriptions which have the biblionumber given on input arg.
406 each element of this array is a hashref containing
407 startdate, histstartdate,opacnote,missinglist,recievedlist,periodicity,status & enddate
411 sub GetSubscriptionsFromBiblionumber {
412 my ($biblionumber) = @_;
414 return unless ($biblionumber);
416 my $dbh = C4::Context->dbh;
418 SELECT subscription.*,
420 subscriptionhistory.*,
421 aqbooksellers.name AS aqbooksellername,
422 biblio.title AS bibliotitle
424 LEFT JOIN subscriptionhistory ON subscription.subscriptionid=subscriptionhistory.subscriptionid
425 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid=aqbooksellers.id
426 LEFT JOIN biblio ON biblio.biblionumber=subscription.biblionumber
427 LEFT JOIN branches ON branches.branchcode=subscription.branchcode
428 WHERE subscription.biblionumber = ?
430 my $sth = $dbh->prepare($query);
431 $sth->execute($biblionumber);
433 while ( my $subs = $sth->fetchrow_hashref ) {
434 $subs->{opacnote} //= "";
435 $subs->{ "periodicity" . $subs->{periodicity} } = 1;
436 $subs->{ "numberpattern" . $subs->{numberpattern} } = 1;
437 $subs->{ "status" . $subs->{'status'} } = 1;
439 $subs->{'abouttoexpire'} = abouttoexpire( $subs->{'subscriptionid'} );
440 $subs->{'subscriptionexpired'} = HasSubscriptionExpired( $subs->{'subscriptionid'} );
441 $subs->{cannotedit} = not can_edit_subscription( $subs );
447 =head2 GetFullSubscriptionsFromBiblionumber
449 $array_ref = GetFullSubscriptionsFromBiblionumber($biblionumber)
450 this function reads the serial table.
454 sub GetFullSubscriptionsFromBiblionumber {
455 my ($biblionumber) = @_;
456 my $dbh = C4::Context->dbh;
458 SELECT serial.serialid,
461 serial.publisheddate,
462 serial.publisheddatetext,
464 serial.notes as notes,
465 year(IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate)) as year,
466 biblio.title as bibliotitle,
467 subscription.branchcode AS branchcode,
468 subscription.subscriptionid AS subscriptionid,
469 subscription.location AS location
471 LEFT JOIN subscription ON
472 (serial.subscriptionid=subscription.subscriptionid)
473 LEFT JOIN aqbooksellers on subscription.aqbooksellerid=aqbooksellers.id
474 LEFT JOIN biblio on biblio.biblionumber=subscription.biblionumber
475 WHERE subscription.biblionumber = ?
477 IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate) DESC,
478 serial.subscriptionid
480 my $sth = $dbh->prepare($query);
481 $sth->execute($biblionumber);
482 my $subscriptions = $sth->fetchall_arrayref( {} );
483 if (scalar @$subscriptions) {
484 my $cannotedit = not can_edit_subscription( $subscriptions->[0] );
485 for my $subscription ( @$subscriptions ) {
486 $subscription->{cannotedit} = $cannotedit;
490 return $subscriptions;
493 =head2 SearchSubscriptions
495 @results = SearchSubscriptions($args);
497 This function returns a list of hashrefs, one for each subscription
498 that meets the conditions specified by the $args hashref.
500 The valid search fields are:
514 The expiration_date search field is special; it specifies the maximum
515 subscription expiration date.
519 sub SearchSubscriptions {
522 my $additional_fields = $args->{additional_fields} // [];
523 my $matching_record_ids_for_additional_fields = [];
524 if ( @$additional_fields ) {
525 my @subscriptions = Koha::Subscriptions->filter_by_additional_fields($additional_fields)->as_list;
527 return () unless @subscriptions;
529 $matching_record_ids_for_additional_fields = [ map {
536 subscription.notes AS publicnotes,
537 subscriptionhistory.*,
539 biblio.notes AS biblionotes,
546 aqbooksellers.name AS vendorname,
549 LEFT JOIN subscriptionhistory USING(subscriptionid)
550 LEFT JOIN biblio ON biblio.biblionumber = subscription.biblionumber
551 LEFT JOIN biblioitems ON biblioitems.biblionumber = subscription.biblionumber
552 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
554 $query .= q| WHERE 1|;
557 if( $args->{biblionumber} ) {
558 push @where_strs, "biblio.biblionumber = ?";
559 push @where_args, $args->{biblionumber};
562 if( $args->{title} ){
563 my @words = split / /, $args->{title};
565 foreach my $word (@words) {
566 push @strs, "biblio.title LIKE ?";
567 push @args, "%$word%";
570 push @where_strs, '(' . join (' AND ', @strs) . ')';
571 push @where_args, @args;
575 push @where_strs, "biblioitems.issn LIKE ?";
576 push @where_args, "%$args->{issn}%";
579 push @where_strs, "biblioitems.ean LIKE ?";
580 push @where_args, "%$args->{ean}%";
582 if ( $args->{callnumber} ) {
583 push @where_strs, "subscription.callnumber LIKE ?";
584 push @where_args, "%$args->{callnumber}%";
586 if( $args->{publisher} ){
587 push @where_strs, "biblioitems.publishercode LIKE ?";
588 push @where_args, "%$args->{publisher}%";
590 if( $args->{bookseller} ){
591 push @where_strs, "aqbooksellers.name LIKE ?";
592 push @where_args, "%$args->{bookseller}%";
594 if( $args->{branch} ){
595 push @where_strs, "subscription.branchcode = ?";
596 push @where_args, "$args->{branch}";
598 if ( $args->{location} ) {
599 push @where_strs, "subscription.location = ?";
600 push @where_args, "$args->{location}";
602 if ( $args->{expiration_date} ) {
603 push @where_strs, "subscription.enddate <= ?";
604 push @where_args, "$args->{expiration_date}";
606 if( defined $args->{closed} ){
607 push @where_strs, "subscription.closed = ?";
608 push @where_args, "$args->{closed}";
612 $query .= ' AND ' . join(' AND ', @where_strs);
614 if ( @$additional_fields ) {
615 $query .= ' AND subscriptionid IN ('
616 . join( ', ', @$matching_record_ids_for_additional_fields )
620 $query .= " ORDER BY " . $args->{orderby} if $args->{orderby};
622 my $dbh = C4::Context->dbh;
623 my $sth = $dbh->prepare($query);
624 $sth->execute(@where_args);
625 my $results = $sth->fetchall_arrayref( {} );
627 for my $subscription ( @$results ) {
628 $subscription->{cannotedit} = not can_edit_subscription( $subscription );
629 $subscription->{cannotdisplay} = not can_show_subscription( $subscription );
631 my $subscription_object = Koha::Subscriptions->find($subscription->{subscriptionid});
632 $subscription->{additional_fields} = { map { $_->field->name => $_->value }
633 $subscription_object->additional_field_values->as_list };
643 ($totalissues,@serials) = GetSerials($subscriptionid);
644 this function gets every serial not arrived for a given subscription
645 as well as the number of issues registered in the database (all types)
646 this number is used to see if a subscription can be deleted (=it must have only 1 issue)
648 FIXME: We should return \@serials.
653 my ( $subscriptionid, $count ) = @_;
655 return unless $subscriptionid;
657 my $dbh = C4::Context->dbh;
659 # status = 2 is "arrived"
661 $count = 5 unless ($count);
663 my $statuses = join( ',', ( ARRIVED, MISSING_STATUSES, NOT_ISSUED ) );
664 my $query = "SELECT serialid,serialseq, status, publisheddate,
665 publisheddatetext, planneddate,notes, routingnotes
667 WHERE subscriptionid = ? AND status NOT IN ( $statuses )
668 ORDER BY IF(publisheddate IS NULL,planneddate,publisheddate) DESC";
669 my $sth = $dbh->prepare($query);
670 $sth->execute($subscriptionid);
672 while ( my $line = $sth->fetchrow_hashref ) {
673 $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
674 push @serials, $line;
677 # OK, now add the last 5 issues arrives/missing
678 $query = "SELECT serialid,serialseq, status, planneddate, publisheddate,
679 publisheddatetext, notes, routingnotes
681 WHERE subscriptionid = ?
682 AND status IN ( $statuses )
683 ORDER BY IF(publisheddate IS NULL,planneddate,publisheddate) DESC
685 $sth = $dbh->prepare($query);
686 $sth->execute($subscriptionid);
687 while ( ( my $line = $sth->fetchrow_hashref ) && $counter < $count ) {
689 $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
691 push @serials, $line;
694 $query = "SELECT count(*) FROM serial WHERE subscriptionid=?";
695 $sth = $dbh->prepare($query);
696 $sth->execute($subscriptionid);
697 my ($totalissues) = $sth->fetchrow;
698 return ( $totalissues, @serials );
703 @serials = GetSerials2($subscriptionid,$statuses);
704 this function returns every serial waited for a given subscription
705 as well as the number of issues registered in the database (all types)
706 this number is used to see if a subscription can be deleted (=it must have only 1 issue)
708 $statuses is an arrayref of statuses and is mandatory.
713 my ( $subscription, $statuses ) = @_;
715 return unless ($subscription and @$statuses);
717 my $dbh = C4::Context->dbh;
719 SELECT serialid,serialseq, status, planneddate, publisheddate,
720 publisheddatetext, notes, routingnotes
722 WHERE subscriptionid=?
724 . q| AND status IN (| . join( ",", ('?') x @$statuses ) . q|)|
726 ORDER BY publisheddate,serialid DESC
728 my $sth = $dbh->prepare($query);
729 $sth->execute( $subscription, @$statuses );
732 while ( my $line = $sth->fetchrow_hashref ) {
733 $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
734 push @serials, $line;
739 =head2 GetLatestSerials
741 \@serials = GetLatestSerials($subscriptionid,$limit)
742 get the $limit's latest serials arrived or missing for a given subscription
744 a ref to an array which contains all of the latest serials stored into a hash.
748 sub GetLatestSerials {
749 my ( $subscriptionid, $limit ) = @_;
751 return unless ($subscriptionid and $limit);
753 my $dbh = C4::Context->dbh;
755 my $statuses = join( ',', ( ARRIVED, MISSING_STATUSES ) );
756 my $strsth = "SELECT serialid,serialseq, status, planneddate, publisheddate, publisheddatetext, notes
758 WHERE subscriptionid = ?
759 AND status IN ($statuses)
760 ORDER BY publisheddate DESC LIMIT 0,$limit
762 my $sth = $dbh->prepare($strsth);
763 $sth->execute($subscriptionid);
765 while ( my $line = $sth->fetchrow_hashref ) {
766 $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
767 push @serials, $line;
773 =head2 GetPreviousSerialid
775 $serialid = GetPreviousSerialid($subscriptionid, $nth)
776 get the $nth's previous serial for the given subscriptionid
782 sub GetPreviousSerialid {
783 my ( $subscriptionid, $nth ) = @_;
785 my $dbh = C4::Context->dbh;
789 my $strsth = "SELECT serialid
791 WHERE subscriptionid = ?
793 ORDER BY serialid DESC LIMIT $nth,1
795 my $sth = $dbh->prepare($strsth);
796 $sth->execute($subscriptionid);
798 my $line = $sth->fetchrow_hashref;
799 $return = $line->{'serialid'} if ($line);
807 $nextseq, $newlastvalue1, $newlastvalue2, $newlastvalue3,
808 $newinnerloop1, $newinnerloop2, $newinnerloop3
809 ) = GetNextSeq( $subscription, $pattern, $frequency, $planneddate );
811 $subscription is a hashref containing all the attributes of the table
813 $pattern is a hashref containing all the attributes of the table
814 'subscription_numberpatterns'.
815 $frequency is a hashref containing all the attributes of the table 'subscription_frequencies'
816 $planneddate is a date string in iso format.
817 This function get the next issue for the subscription given on input arg
822 my ($subscription, $pattern, $frequency, $planneddate) = @_;
824 return unless ($subscription and $pattern);
826 my ( $newlastvalue1, $newlastvalue2, $newlastvalue3,
827 $newinnerloop1, $newinnerloop2, $newinnerloop3 );
830 if ($subscription->{'skip_serialseq'}) {
831 my @irreg = split /;/, $subscription->{'irregularity'};
833 my $irregularities = {};
834 $irregularities->{$_} = 1 foreach(@irreg);
835 my $issueno = GetFictiveIssueNumber($subscription, $planneddate, $frequency) + 1;
836 while($irregularities->{$issueno}) {
843 my $numberingmethod = $pattern->{numberingmethod};
845 if ($numberingmethod) {
846 $calculated = $numberingmethod;
847 my $locale = $subscription->{locale};
848 $newlastvalue1 = $subscription->{lastvalue1} || 0;
849 $newlastvalue2 = $subscription->{lastvalue2} || 0;
850 $newlastvalue3 = $subscription->{lastvalue3} || 0;
851 $newinnerloop1 = $subscription->{innerloop1} || 0;
852 $newinnerloop2 = $subscription->{innerloop2} || 0;
853 $newinnerloop3 = $subscription->{innerloop3} || 0;
856 $calc{$_} = 1 if ($numberingmethod =~ /\{$_\}/);
859 for(my $i = 0; $i < $count; $i++) {
861 # check if we have to increase the new value.
863 if ($newinnerloop1 >= $pattern->{every1}) {
865 $newlastvalue1 += $pattern->{add1};
867 # reset counter if needed.
868 $newlastvalue1 = $pattern->{setto1} if ($newlastvalue1 > $pattern->{whenmorethan1});
871 # check if we have to increase the new value.
873 if ($newinnerloop2 >= $pattern->{every2}) {
875 $newlastvalue2 += $pattern->{add2};
877 # reset counter if needed.
878 $newlastvalue2 = $pattern->{setto2} if ($newlastvalue2 > $pattern->{whenmorethan2});
881 # check if we have to increase the new value.
883 if ($newinnerloop3 >= $pattern->{every3}) {
885 $newlastvalue3 += $pattern->{add3};
887 # reset counter if needed.
888 $newlastvalue3 = $pattern->{setto3} if ($newlastvalue3 > $pattern->{whenmorethan3});
892 my $newlastvalue1string = _numeration( $newlastvalue1, $pattern->{numbering1}, $locale );
893 $calculated =~ s/\{X\}/$newlastvalue1string/g;
896 my $newlastvalue2string = _numeration( $newlastvalue2, $pattern->{numbering2}, $locale );
897 $calculated =~ s/\{Y\}/$newlastvalue2string/g;
900 my $newlastvalue3string = _numeration( $newlastvalue3, $pattern->{numbering3}, $locale );
901 $calculated =~ s/\{Z\}/$newlastvalue3string/g;
907 $newlastvalue1, $newlastvalue2, $newlastvalue3,
908 $newinnerloop1, $newinnerloop2, $newinnerloop3);
913 $calculated = GetSeq($subscription, $pattern)
914 $subscription is a hashref containing all the attributes of the table 'subscription'
915 $pattern is a hashref containing all the attributes of the table 'subscription_numberpatterns'
916 this function transforms {X},{Y},{Z} to 150,0,0 for example.
918 the sequence in string format
923 my ($subscription, $pattern) = @_;
925 return unless ($subscription and $pattern);
927 my $locale = $subscription->{locale};
929 my $calculated = $pattern->{numberingmethod};
931 my $newlastvalue1 = $subscription->{'lastvalue1'} || 0;
932 $newlastvalue1 = _numeration($newlastvalue1, $pattern->{numbering1}, $locale) if ($pattern->{numbering1}); # reset counter if needed.
933 $calculated =~ s/\{X\}/$newlastvalue1/g;
935 my $newlastvalue2 = $subscription->{'lastvalue2'} || 0;
936 $newlastvalue2 = _numeration($newlastvalue2, $pattern->{numbering2}, $locale) if ($pattern->{numbering2}); # reset counter if needed.
937 $calculated =~ s/\{Y\}/$newlastvalue2/g;
939 my $newlastvalue3 = $subscription->{'lastvalue3'} || 0;
940 $newlastvalue3 = _numeration($newlastvalue3, $pattern->{numbering3}, $locale) if ($pattern->{numbering3}); # reset counter if needed.
941 $calculated =~ s/\{Z\}/$newlastvalue3/g;
945 =head2 GetExpirationDate
947 $enddate = GetExpirationDate($subscriptionid, [$startdate])
949 this function return the next expiration date for a subscription given on input args.
956 sub GetExpirationDate {
957 my ( $subscriptionid, $startdate ) = @_;
959 return unless ($subscriptionid);
961 my $dbh = C4::Context->dbh;
962 my $subscription = GetSubscription($subscriptionid);
965 # we don't do the same test if the subscription is based on X numbers or on X weeks/months
966 $enddate = $startdate || $subscription->{startdate};
967 my @date = split( /-/, $enddate );
969 return if ( scalar(@date) != 3 || not check_date(@date) );
971 my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
972 if ( $frequency and $frequency->{unit} ) {
975 if ( my $length = $subscription->{numberlength} ) {
977 #calculate the date of the last issue.
978 for ( my $i = 1 ; $i <= $length ; $i++ ) {
979 $enddate = GetNextDate( $subscription, $enddate, $frequency );
981 } elsif ( $subscription->{monthlength} ) {
982 if ( $$subscription{startdate} ) {
983 my @enddate = Add_Delta_YM( $date[0], $date[1], $date[2], 0, $subscription->{monthlength} );
984 $enddate = sprintf( "%04d-%02d-%02d", $enddate[0], $enddate[1], $enddate[2] );
986 } elsif ( $subscription->{weeklength} ) {
987 if ( $$subscription{startdate} ) {
988 my @date = split( /-/, $subscription->{startdate} );
989 my @enddate = Add_Delta_Days( $date[0], $date[1], $date[2], $subscription->{weeklength} * 7 );
990 $enddate = sprintf( "%04d-%02d-%02d", $enddate[0], $enddate[1], $enddate[2] );
993 $enddate = $subscription->{enddate};
997 return $subscription->{enddate};
1001 =head2 CountSubscriptionFromBiblionumber
1003 $subscriptionsnumber = CountSubscriptionFromBiblionumber($biblionumber)
1004 this returns a count of the subscriptions for a given biblionumber
1006 the number of subscriptions
1010 sub CountSubscriptionFromBiblionumber {
1011 my ($biblionumber) = @_;
1013 return unless ($biblionumber);
1015 my $dbh = C4::Context->dbh;
1016 my $query = "SELECT count(*) FROM subscription WHERE biblionumber=?";
1017 my $sth = $dbh->prepare($query);
1018 $sth->execute($biblionumber);
1019 my $subscriptionsnumber = $sth->fetchrow;
1020 return $subscriptionsnumber;
1023 =head2 ModSubscriptionHistory
1025 ModSubscriptionHistory($subscriptionid,$histstartdate,$enddate,$recievedlist,$missinglist,$opacnote,$librariannote);
1027 this function modifies the history of a subscription. Put your new values on input arg.
1028 returns the number of rows affected
1032 sub ModSubscriptionHistory {
1033 my ( $subscriptionid, $histstartdate, $enddate, $receivedlist, $missinglist, $opacnote, $librariannote ) = @_;
1035 return unless ($subscriptionid);
1037 my $dbh = C4::Context->dbh;
1038 my $query = "UPDATE subscriptionhistory
1039 SET histstartdate=?,histenddate=?,recievedlist=?,missinglist=?,opacnote=?,librariannote=?
1040 WHERE subscriptionid=?
1042 my $sth = $dbh->prepare($query);
1043 $receivedlist =~ s/^; // if $receivedlist;
1044 $missinglist =~ s/^; // if $missinglist;
1045 $opacnote =~ s/^; // if $opacnote;
1046 $sth->execute( $histstartdate, $enddate, $receivedlist, $missinglist, $opacnote, $librariannote, $subscriptionid );
1050 =head2 ModSerialStatus
1052 ModSerialStatus($serialid, $serialseq, $planneddate, $publisheddate,
1053 $publisheddatetext, $status, $notes);
1055 This function modify the serial status. Serial status is a number.(eg 2 is "arrived")
1056 Note : if we change from "waited" to something else,then we will have to create a new "waited" entry
1060 sub ModSerialStatus {
1061 my ($serialid, $serialseq, $planneddate, $publisheddate, $publisheddatetext,
1062 $status, $notes) = @_;
1064 return unless ($serialid);
1066 #It is a usual serial
1067 # 1st, get previous status :
1068 my $dbh = C4::Context->dbh;
1069 my $query = "SELECT serial.subscriptionid,serial.status,subscription.periodicity,serial.routingnotes
1070 FROM serial, subscription
1071 WHERE serial.subscriptionid=subscription.subscriptionid
1073 my $sth = $dbh->prepare($query);
1074 $sth->execute($serialid);
1075 my ( $subscriptionid, $oldstatus, $periodicity, $routingnotes ) = $sth->fetchrow;
1076 my $frequency = GetSubscriptionFrequency($periodicity);
1078 # change status & update subscriptionhistory
1080 if ( $status == DELETED ) {
1081 DelIssue( { 'serialid' => $serialid, 'subscriptionid' => $subscriptionid, 'serialseq' => $serialseq } );
1085 SET serialseq = ?, publisheddate = ?, publisheddatetext = ?,
1086 planneddate = ?, status = ?, notes = ?, routingnotes = ?
1089 $sth = $dbh->prepare($query);
1090 $sth->execute( $serialseq, $publisheddate, $publisheddatetext,
1091 $planneddate, $status, $notes, $routingnotes, $serialid );
1092 $query = "SELECT * FROM subscription WHERE subscriptionid = ?";
1093 $sth = $dbh->prepare($query);
1094 $sth->execute($subscriptionid);
1095 my $val = $sth->fetchrow_hashref;
1096 unless ( $val->{manualhistory} ) {
1097 $query = "SELECT missinglist,recievedlist FROM subscriptionhistory WHERE subscriptionid=?";
1098 $sth = $dbh->prepare($query);
1099 $sth->execute($subscriptionid);
1100 my ( $missinglist, $recievedlist ) = $sth->fetchrow;
1102 if ( $status == ARRIVED || ($oldstatus == ARRIVED && $status != ARRIVED) ) {
1103 $recievedlist = _handle_seqno($serialseq, $recievedlist);
1106 # in case serial has been previously marked as missing
1107 if (grep /$status/, (EXPECTED, ARRIVED, LATE, CLAIMED)) {
1108 $missinglist = _handle_seqno($serialseq, $missinglist, 'REMOVE');
1111 $missinglist = _handle_seqno($serialseq, $missinglist) if grep { $_ == $status } MISSING_STATUSES;
1112 $missinglist .= "; not issued $serialseq" if $status == NOT_ISSUED and not _handle_seqno($serialseq, $missinglist, 'CHECK');
1114 $query = "UPDATE subscriptionhistory SET recievedlist=?, missinglist=? WHERE subscriptionid=?";
1115 $sth = $dbh->prepare($query);
1116 $recievedlist =~ s/^; //;
1117 $missinglist =~ s/^; //;
1118 $sth->execute( $recievedlist, $missinglist, $subscriptionid );
1122 # create new expected entry if needed (ie : was "expected" and has changed)
1123 my $otherIssueExpected = scalar findSerialsByStatus(EXPECTED, $subscriptionid);
1124 if ( !$otherIssueExpected && $oldstatus == EXPECTED && $status != EXPECTED ) {
1125 my $subscription = GetSubscription($subscriptionid);
1126 my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
1127 my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
1131 $newserialseq, $newlastvalue1, $newlastvalue2, $newlastvalue3,
1132 $newinnerloop1, $newinnerloop2, $newinnerloop3
1134 = GetNextSeq( $subscription, $pattern, $frequency, $publisheddate );
1136 # next date (calculated from actual date & frequency parameters)
1137 my $nextpublisheddate = GetNextDate($subscription, $publisheddate, $frequency, 1);
1138 my $nextpubdate = $nextpublisheddate;
1139 $query = "UPDATE subscription SET lastvalue1=?, lastvalue2=?, lastvalue3=?, innerloop1=?, innerloop2=?, innerloop3=?
1140 WHERE subscriptionid = ?";
1141 $sth = $dbh->prepare($query);
1142 $sth->execute( $newlastvalue1, $newlastvalue2, $newlastvalue3, $newinnerloop1, $newinnerloop2, $newinnerloop3, $subscriptionid );
1143 my $newnote = C4::Context->preference('PreserveSerialNotes') ? $notes : "";
1144 NewIssue( $newserialseq, $subscriptionid, $subscription->{'biblionumber'}, 1, $nextpubdate, $nextpubdate, undef, $newnote, $routingnotes );
1145 # check if an alert must be sent... (= a letter is defined & status became "arrived"
1146 if ( $subscription->{letter} && $status == ARRIVED && $oldstatus != ARRIVED ) {
1147 require C4::Letters;
1148 C4::Letters::SendAlerts( 'issue', $serialid, $subscription->{letter} );
1156 # Adds or removes seqno from list when needed; returns list
1157 # Or checks and returns true when present
1159 my ( $seq, $list, $op ) = @_; # op = ADD | REMOVE | CHECK (default: ADD)
1161 $seq_r =~ s/([()])/\\$1/g; # Adjust disturbing parentheses for regex, maybe extend in future
1163 if( !$op or $op eq 'ADD' ) {
1164 $list .= "; $seq" if $list !~ /(^|;)\s*$seq_r(?=;|$)/;
1165 } elsif( $op eq 'REMOVE' ) {
1166 $list=~ s/(^|;)\s*(not issued )?$seq_r(?=;|$)//g;
1168 return $list =~ /(^|;)\s*$seq_r(?=;|$)/ ? 1 : q{};
1173 =head2 GetNextExpected
1175 $nextexpected = GetNextExpected($subscriptionid)
1177 Get the planneddate for the current expected issue of the subscription.
1183 planneddate => ISO date
1188 sub GetNextExpected {
1189 my ($subscriptionid) = @_;
1191 my $dbh = C4::Context->dbh;
1195 WHERE subscriptionid = ?
1199 my $sth = $dbh->prepare($query);
1201 # Each subscription has only one 'expected' issue.
1202 $sth->execute( $subscriptionid, EXPECTED );
1203 my $nextissue = $sth->fetchrow_hashref;
1204 if ( !$nextissue ) {
1208 WHERE subscriptionid = ?
1209 ORDER BY publisheddate DESC
1212 $sth = $dbh->prepare($query);
1213 $sth->execute($subscriptionid);
1214 $nextissue = $sth->fetchrow_hashref;
1216 foreach(qw/planneddate publisheddate/) {
1217 # or should this default to 1st Jan ???
1218 $nextissue->{$_} //= strftime( '%Y-%m-%d', localtime );
1224 =head2 ModNextExpected
1226 ModNextExpected($subscriptionid,$date)
1228 Update the planneddate for the current expected issue of the subscription.
1229 This will modify all future prediction results.
1231 C<$date> is an ISO date.
1237 sub ModNextExpected {
1238 my ( $subscriptionid, $date ) = @_;
1239 my $dbh = C4::Context->dbh;
1241 #FIXME: Would expect to only set planneddate, but we set both on new issue creation, so updating it here
1242 my $sth = $dbh->prepare('UPDATE serial SET planneddate=?,publisheddate=? WHERE subscriptionid=? AND status=?');
1244 # Each subscription has only one 'expected' issue.
1245 $sth->execute( $date, $date, $subscriptionid, EXPECTED );
1250 =head2 GetSubscriptionIrregularities
1254 =item @irreg = &GetSubscriptionIrregularities($subscriptionid);
1255 get the list of irregularities for a subscription
1261 sub GetSubscriptionIrregularities {
1262 my $subscriptionid = shift;
1264 return unless $subscriptionid;
1266 my $dbh = C4::Context->dbh;
1270 WHERE subscriptionid = ?
1272 my $sth = $dbh->prepare($query);
1273 $sth->execute($subscriptionid);
1275 my ($result) = $sth->fetchrow_array;
1276 my @irreg = split /;/, $result;
1281 =head2 ModSubscription
1283 this function modifies a subscription. Put all new values on input args.
1284 returns the number of rows affected
1288 sub ModSubscription {
1290 $auser, $branchcode, $aqbooksellerid, $cost, $aqbudgetid, $startdate,
1291 $periodicity, $firstacquidate, $irregularity, $numberpattern, $locale,
1292 $numberlength, $weeklength, $monthlength, $lastvalue1, $innerloop1,
1293 $lastvalue2, $innerloop2, $lastvalue3, $innerloop3, $status,
1294 $biblionumber, $callnumber, $notes, $letter, $manualhistory,
1295 $internalnotes, $serialsadditems, $staffdisplaycount, $opacdisplaycount,
1296 $graceperiod, $location, $enddate, $subscriptionid, $skip_serialseq,
1297 $itemtype, $previousitemtype, $mana_id, $ccode, $published_on_template
1300 my $subscription = Koha::Subscriptions->find($subscriptionid);
1303 librarian => $auser,
1304 branchcode => $branchcode,
1305 aqbooksellerid => $aqbooksellerid,
1307 aqbudgetid => $aqbudgetid,
1308 biblionumber => $biblionumber,
1309 startdate => $startdate,
1310 periodicity => $periodicity,
1311 numberlength => $numberlength,
1312 weeklength => $weeklength,
1313 monthlength => $monthlength,
1314 lastvalue1 => $lastvalue1,
1315 innerloop1 => $innerloop1,
1316 lastvalue2 => $lastvalue2,
1317 innerloop2 => $innerloop2,
1318 lastvalue3 => $lastvalue3,
1319 innerloop3 => $innerloop3,
1323 firstacquidate => $firstacquidate,
1324 irregularity => $irregularity,
1325 numberpattern => $numberpattern,
1327 callnumber => $callnumber,
1328 manualhistory => $manualhistory,
1329 internalnotes => $internalnotes,
1330 serialsadditems => $serialsadditems,
1331 staffdisplaycount => $staffdisplaycount,
1332 opacdisplaycount => $opacdisplaycount,
1333 graceperiod => $graceperiod,
1334 location => $location,
1335 enddate => $enddate,
1336 skip_serialseq => $skip_serialseq,
1337 itemtype => $itemtype,
1338 previousitemtype => $previousitemtype,
1339 mana_id => $mana_id,
1341 published_on_template => $published_on_template,
1344 # FIXME Must be $subscription->serials
1345 # FIXME We shouldn't need serial.subscription (instead use serial->subscription->biblionumber)
1346 Koha::Serials->search({ subscriptionid => $subscriptionid })->update({ biblionumber => $biblionumber });
1348 logaction( "SERIAL", "MODIFY", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1350 $subscription->discard_changes;
1351 return $subscription;
1354 =head2 NewSubscription
1356 $subscriptionid = &NewSubscription($auser,branchcode,$aqbooksellerid,$cost,$aqbudgetid,$biblionumber,
1357 $startdate,$periodicity,$numberlength,$weeklength,$monthlength,
1358 $lastvalue1,$innerloop1,$lastvalue2,$innerloop2,$lastvalue3,$innerloop3,
1359 $status, $notes, $letter, $firstacquidate, $irregularity, $numberpattern,
1360 $locale, $callnumber, $manualhistory, $internalnotes, $serialsadditems,
1361 $staffdisplaycount, $opacdisplaycount, $graceperiod, $location, $enddate,
1362 $skip_serialseq, $itemtype, $previousitemtype);
1364 Create a new subscription with value given on input args.
1367 the id of this new subscription
1371 sub NewSubscription {
1373 $auser, $branchcode, $aqbooksellerid, $cost, $aqbudgetid, $biblionumber,
1374 $startdate, $periodicity, $numberlength, $weeklength, $monthlength,
1375 $lastvalue1, $innerloop1, $lastvalue2, $innerloop2, $lastvalue3,
1376 $innerloop3, $status, $notes, $letter, $firstacquidate, $irregularity,
1377 $numberpattern, $locale, $callnumber, $manualhistory, $internalnotes,
1378 $serialsadditems, $staffdisplaycount, $opacdisplaycount, $graceperiod,
1379 $location, $enddate, $skip_serialseq, $itemtype, $previousitemtype, $mana_id, $ccode,
1380 $published_on_template,
1382 my $dbh = C4::Context->dbh;
1384 my $subscription = Koha::Subscription->new(
1386 librarian => $auser,
1387 branchcode => $branchcode,
1388 aqbooksellerid => $aqbooksellerid,
1390 aqbudgetid => $aqbudgetid,
1391 biblionumber => $biblionumber,
1392 startdate => $startdate,
1393 periodicity => $periodicity,
1394 numberlength => $numberlength,
1395 weeklength => $weeklength,
1396 monthlength => $monthlength,
1397 lastvalue1 => $lastvalue1,
1398 innerloop1 => $innerloop1,
1399 lastvalue2 => $lastvalue2,
1400 innerloop2 => $innerloop2,
1401 lastvalue3 => $lastvalue3,
1402 innerloop3 => $innerloop3,
1406 firstacquidate => $firstacquidate,
1407 irregularity => $irregularity,
1408 numberpattern => $numberpattern,
1410 callnumber => $callnumber,
1411 manualhistory => $manualhistory,
1412 internalnotes => $internalnotes,
1413 serialsadditems => $serialsadditems,
1414 staffdisplaycount => $staffdisplaycount,
1415 opacdisplaycount => $opacdisplaycount,
1416 graceperiod => $graceperiod,
1417 location => $location,
1418 enddate => $enddate,
1419 skip_serialseq => $skip_serialseq,
1420 itemtype => $itemtype,
1421 previousitemtype => $previousitemtype,
1422 mana_id => $mana_id,
1424 published_on_template => $published_on_template,
1427 $subscription->discard_changes;
1428 my $subscriptionid = $subscription->subscriptionid;
1429 my ( $query, $sth );
1431 $enddate = GetExpirationDate( $subscriptionid, $startdate );
1435 WHERE subscriptionid=?
1437 $sth = $dbh->prepare($query);
1438 $sth->execute( $enddate, $subscriptionid );
1441 # then create the 1st expected number
1443 INSERT INTO subscriptionhistory
1444 (biblionumber, subscriptionid, histstartdate, missinglist, recievedlist)
1445 VALUES (?,?,?, '', '')
1447 $sth = $dbh->prepare($query);
1448 $sth->execute( $biblionumber, $subscriptionid, $startdate);
1450 # reread subscription to get a hash (for calculation of the 1st issue number)
1451 $subscription = GetSubscription($subscriptionid); # We should not do that
1452 my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
1454 # calculate issue number
1455 my $serialseq = GetSeq($subscription, $pattern) || q{};
1459 serialseq => $serialseq,
1460 serialseq_x => $subscription->{'lastvalue1'},
1461 serialseq_y => $subscription->{'lastvalue2'},
1462 serialseq_z => $subscription->{'lastvalue3'},
1463 subscriptionid => $subscriptionid,
1464 biblionumber => $biblionumber,
1466 planneddate => $firstacquidate,
1467 publisheddate => $firstacquidate,
1471 logaction( "SERIAL", "ADD", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1473 #set serial flag on biblio if not already set.
1474 my $biblio = Koha::Biblios->find( $biblionumber );
1475 if ( $biblio and !$biblio->serial ) {
1476 my $record = $biblio->metadata->record;
1477 my ( $tag, $subf ) = GetMarcFromKohaField( 'biblio.serial' );
1479 eval { $record->field($tag)->update( $subf => 1 ); };
1481 ModBiblio( $record, $biblionumber, $biblio->frameworkcode );
1483 return $subscriptionid;
1486 =head2 GetSubscriptionLength
1488 my ($numberlength, $weeklength, $monthlength) = GetSubscriptionLength( $subtype, $sublength );
1490 This function calculates the subscription length.
1494 sub GetSubscriptionLength {
1495 my ($subtype, $length) = @_;
1497 return unless looks_like_number($length);
1501 $subtype eq 'issues' ? $length : 0,
1502 $subtype eq 'weeks' ? $length : 0,
1503 $subtype eq 'months' ? $length : 0,
1508 =head2 ReNewSubscription
1510 ReNewSubscription($params);
1512 $params is a hashref with the following keys: subscriptionid, user, startdate, numberlength, weeklength, monthlength, note, branchcode
1514 this function renew a subscription with values given on input args.
1518 sub ReNewSubscription {
1519 my ( $params ) = @_;
1520 my $subscriptionid = $params->{subscriptionid};
1521 my $user = $params->{user};
1522 my $startdate = $params->{startdate};
1523 my $numberlength = $params->{numberlength};
1524 my $weeklength = $params->{weeklength};
1525 my $monthlength = $params->{monthlength};
1526 my $note = $params->{note};
1527 my $branchcode = $params->{branchcode};
1529 my $dbh = C4::Context->dbh;
1530 my $subscription = GetSubscription($subscriptionid);
1534 LEFT JOIN biblioitems ON biblio.biblionumber=biblioitems.biblionumber
1535 WHERE biblio.biblionumber=?
1537 my $sth = $dbh->prepare($query);
1538 $sth->execute( $subscription->{biblionumber} );
1539 my $biblio = $sth->fetchrow_hashref;
1541 if ( C4::Context->preference("RenewSerialAddsSuggestion") ) {
1542 Koha::Suggestion->new(
1544 'suggestedby' => $user,
1545 'title' => $subscription->{bibliotitle},
1546 'author' => $biblio->{author},
1547 'publishercode' => $biblio->{publishercode},
1549 'biblionumber' => $subscription->{biblionumber},
1550 'branchcode' => $branchcode,
1555 $numberlength ||= 0; # Should not we raise an exception instead?
1558 # renew subscription
1561 SET startdate=?,numberlength=?,weeklength=?,monthlength=?,reneweddate=NOW()
1562 WHERE subscriptionid=?
1564 $sth = $dbh->prepare($query);
1565 $sth->execute( $startdate, $numberlength, $weeklength, $monthlength, $subscriptionid );
1566 my $enddate = GetExpirationDate($subscriptionid);
1570 WHERE subscriptionid=?
1572 $sth = $dbh->prepare($query);
1573 $sth->execute( $enddate, $subscriptionid );
1575 logaction( "SERIAL", "RENEW", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1581 NewIssue($serialseq,$subscriptionid,$biblionumber,$status, $planneddate, $publisheddate, $notes, $routingnotes)
1583 Create a new issue stored on the database.
1584 Note : we have to update the recievedlist and missinglist on subscriptionhistory for this subscription.
1585 returns the serial id
1590 my ( $serialseq, $subscriptionid, $biblionumber, $status, $planneddate,
1591 $publisheddate, $publisheddatetext, $notes, $routingnotes ) = @_;
1592 ### FIXME biblionumber CAN be provided by subscriptionid. So Do we STILL NEED IT ?
1594 return unless ($subscriptionid);
1596 my $schema = Koha::Database->new()->schema();
1598 my $subscription = Koha::Subscriptions->find( $subscriptionid );
1600 if ( my $template = $subscription->published_on_template ) {
1601 $publisheddatetext = process_tt(
1604 subscription => $subscription,
1605 serialseq => $serialseq,
1606 serialseq_x => $subscription->lastvalue1(),
1607 serialseq_y => $subscription->lastvalue2(),
1608 serialseq_z => $subscription->lastvalue3(),
1609 subscriptionid => $subscriptionid,
1610 biblionumber => $biblionumber,
1612 planneddate => $planneddate,
1613 publisheddate => $publisheddate,
1614 publisheddatetext => $publisheddatetext,
1616 routingnotes => $routingnotes,
1621 my $serial = Koha::Serial->new(
1623 serialseq => $serialseq,
1624 serialseq_x => $subscription->lastvalue1(),
1625 serialseq_y => $subscription->lastvalue2(),
1626 serialseq_z => $subscription->lastvalue3(),
1627 subscriptionid => $subscriptionid,
1628 biblionumber => $biblionumber,
1630 planneddate => $planneddate,
1631 publisheddate => $publisheddate,
1632 publisheddatetext => $publisheddatetext,
1634 routingnotes => $routingnotes,
1638 my $serialid = $serial->id();
1640 my $subscription_history = Koha::Subscription::Histories->find($subscriptionid);
1641 my $missinglist = $subscription_history->missinglist();
1642 my $recievedlist = $subscription_history->recievedlist();
1644 if ( $status == ARRIVED ) {
1645 ### TODO Add a feature that improves recognition and description.
1646 ### As such count (serialseq) i.e. : N18,2(N19),N20
1647 ### Would use substr and index But be careful to previous presence of ()
1648 $recievedlist .= "; $serialseq" unless ( index( $recievedlist, $serialseq ) > 0 );
1650 if ( grep { $_ eq $status } (MISSING_STATUSES) ) {
1651 $missinglist .= "; $serialseq" unless ( index( $missinglist, $serialseq ) > 0 );
1654 $recievedlist =~ s/^; //;
1655 $missinglist =~ s/^; //;
1657 $subscription_history->recievedlist($recievedlist);
1658 $subscription_history->missinglist($missinglist);
1659 $subscription_history->store();
1664 =head2 HasSubscriptionStrictlyExpired
1666 1 or 0 = HasSubscriptionStrictlyExpired($subscriptionid)
1668 the subscription has stricly expired when today > the end subscription date
1671 1 if true, 0 if false, -1 if the expiration date is not set.
1675 sub HasSubscriptionStrictlyExpired {
1677 # Getting end of subscription date
1678 my ($subscriptionid) = @_;
1680 return unless ($subscriptionid);
1682 my $dbh = C4::Context->dbh;
1683 my $subscription = GetSubscription($subscriptionid);
1684 my $expirationdate = $subscription->{enddate} || GetExpirationDate($subscriptionid);
1686 # If the expiration date is set
1687 if ( $expirationdate != 0 ) {
1688 my ( $endyear, $endmonth, $endday ) = split( '-', $expirationdate );
1690 # Getting today's date
1691 my ( $nowyear, $nowmonth, $nowday ) = Today();
1693 # if today's date > expiration date, then the subscription has stricly expired
1694 if ( Delta_Days( $nowyear, $nowmonth, $nowday, $endyear, $endmonth, $endday ) < 0 ) {
1701 # There are some cases where the expiration date is not set
1702 # As we can't determine if the subscription has expired on a date-basis,
1708 =head2 HasSubscriptionExpired
1710 $has_expired = HasSubscriptionExpired($subscriptionid)
1712 the subscription has expired when the next issue to arrive is out of subscription limit.
1715 0 if the subscription has not expired
1716 1 if the subscription has expired
1717 2 if has subscription does not have a valid expiration date set
1721 sub HasSubscriptionExpired {
1722 my ($subscriptionid) = @_;
1724 return unless ($subscriptionid);
1726 my $dbh = C4::Context->dbh;
1727 my $subscription = GetSubscription($subscriptionid);
1728 my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
1729 if ( $frequency and $frequency->{unit} ) {
1730 my $expirationdate = $subscription->{enddate} || GetExpirationDate($subscriptionid);
1731 if (!defined $expirationdate) {
1732 $expirationdate = q{};
1735 SELECT max(planneddate)
1737 WHERE subscriptionid=?
1739 my $sth = $dbh->prepare($query);
1740 $sth->execute($subscriptionid);
1741 my ($res) = $sth->fetchrow;
1742 if (!$res || $res=~m/^0000/) {
1745 my @res = split( /-/, $res );
1746 my @endofsubscriptiondate = split( /-/, $expirationdate );
1747 return 2 if ( scalar(@res) != 3 || scalar(@endofsubscriptiondate) != 3 || not check_date(@res) || not check_date(@endofsubscriptiondate) );
1749 if ( ( @endofsubscriptiondate && Delta_Days( $res[0], $res[1], $res[2], $endofsubscriptiondate[0], $endofsubscriptiondate[1], $endofsubscriptiondate[2] ) <= 0 )
1754 if ( $subscription->{'numberlength'} ) {
1755 my $countreceived = countissuesfrom( $subscriptionid, $subscription->{'startdate'} );
1756 return 1 if ( $countreceived > $subscription->{'numberlength'} );
1762 return 0; # Notice that you'll never get here.
1765 =head2 DelSubscription
1767 DelSubscription($subscriptionid)
1768 this function deletes subscription which has $subscriptionid as id.
1772 sub DelSubscription {
1773 my ($subscriptionid) = @_;
1774 my $dbh = C4::Context->dbh;
1775 $dbh->do("DELETE FROM subscription WHERE subscriptionid=?", undef, $subscriptionid);
1777 Koha::AdditionalFieldValues->search({
1778 'field.tablename' => 'subscription',
1779 'me.record_id' => $subscriptionid,
1780 }, { join => 'field' })->delete;
1782 logaction( "SERIAL", "DELETE", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1787 DelIssue($serialseq,$subscriptionid)
1788 this function deletes an issue which has $serialseq and $subscriptionid given on input arg.
1790 returns the number of rows affected
1795 my ($dataissue) = @_;
1796 my $dbh = C4::Context->dbh;
1797 ### TODO Add itemdeletion. Would need to get itemnumbers. Should be in a pref ?
1802 AND subscriptionid= ?
1804 my $mainsth = $dbh->prepare($query);
1805 $mainsth->execute( $dataissue->{'serialid'}, $dataissue->{'subscriptionid'} );
1807 #Delete element from subscription history
1808 $query = "SELECT * FROM subscription WHERE subscriptionid = ?";
1809 my $sth = $dbh->prepare($query);
1810 $sth->execute( $dataissue->{'subscriptionid'} );
1811 my $val = $sth->fetchrow_hashref;
1812 unless ( $val->{manualhistory} ) {
1814 SELECT * FROM subscriptionhistory
1815 WHERE subscriptionid= ?
1817 my $sth = $dbh->prepare($query);
1818 $sth->execute( $dataissue->{'subscriptionid'} );
1819 my $data = $sth->fetchrow_hashref;
1820 my $serialseq = $dataissue->{'serialseq'};
1821 $data->{'missinglist'} =~ s/\b$serialseq\b//;
1822 $data->{'recievedlist'} =~ s/\b$serialseq\b//;
1823 my $strsth = "UPDATE subscriptionhistory SET " . join( ",", map { join( "=", $_, $dbh->quote( $data->{$_} ) ) } keys %$data ) . " WHERE subscriptionid=?";
1824 $sth = $dbh->prepare($strsth);
1825 $sth->execute( $dataissue->{'subscriptionid'} );
1828 return $mainsth->rows;
1831 =head2 GetLateOrMissingIssues
1833 @issuelist = GetLateMissingIssues($supplierid,$serialid)
1835 this function selects missing issues on database - where serial.status = MISSING* or serial.status = LATE or planneddate<now
1838 the issuelist as an array of hash refs. Each element of this array contains
1839 name,title,planneddate,serialseq,serial.subscriptionid from tables : subscription, serial & biblio
1843 sub GetLateOrMissingIssues {
1844 my ( $supplierid, $serialid, $order ) = @_;
1846 return unless ( $supplierid or $serialid );
1848 my $dbh = C4::Context->dbh;
1853 $byserial = "and serialid = " . $serialid;
1856 $order .= ", title";
1860 my $missing_statuses_string = join ',', (MISSING_STATUSES);
1862 $sth = $dbh->prepare(
1864 serialid, aqbooksellerid, name,
1865 biblio.title, biblioitems.issn, planneddate, serialseq,
1866 serial.status, serial.subscriptionid, claimdate, claims_count,
1867 subscription.branchcode, serial.publisheddate
1869 LEFT JOIN subscription ON serial.subscriptionid=subscription.subscriptionid
1870 LEFT JOIN biblio ON subscription.biblionumber=biblio.biblionumber
1871 LEFT JOIN biblioitems ON subscription.biblionumber=biblioitems.biblionumber
1872 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
1873 WHERE subscription.subscriptionid = serial.subscriptionid
1874 AND (serial.STATUS IN ($missing_statuses_string) OR ((planneddate < now() AND serial.STATUS = ?) OR serial.STATUS = ? OR serial.STATUS = ?))
1875 AND subscription.aqbooksellerid=$supplierid
1880 $sth = $dbh->prepare(
1882 serialid, aqbooksellerid, name,
1883 biblio.title, planneddate, serialseq,
1884 serial.status, serial.subscriptionid, claimdate, claims_count,
1885 subscription.branchcode, serial.publisheddate
1887 LEFT JOIN subscription ON serial.subscriptionid=subscription.subscriptionid
1888 LEFT JOIN biblio ON subscription.biblionumber=biblio.biblionumber
1889 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
1890 WHERE subscription.subscriptionid = serial.subscriptionid
1891 AND (serial.STATUS IN ($missing_statuses_string) OR ((planneddate < now() AND serial.STATUS = ?) OR serial.STATUS = ? OR serial.STATUS = ?))
1896 $sth->execute( EXPECTED, LATE, CLAIMED );
1898 while ( my $line = $sth->fetchrow_hashref ) {
1899 $line->{"status".$line->{status}} = 1;
1901 my $subscription_object = Koha::Subscriptions->find($line->{subscriptionid});
1902 $line->{additional_fields} = { map { $_->field->name => $_->value }
1903 $subscription_object->additional_field_values->as_list };
1905 push @issuelist, $line;
1912 &updateClaim($serialid)
1914 this function updates the time when a claim is issued for late/missing items
1916 called from claims.pl file
1921 my ($serialids) = @_;
1922 return unless $serialids;
1923 unless ( ref $serialids ) {
1924 $serialids = [ $serialids ];
1927 foreach my $serialid(@$serialids) {
1928 my $serial = Koha::Serials->find($serialid);
1930 C4::Serials::ModSerialStatus(
1933 $serial->planneddate,
1934 $serial->publisheddate,
1935 $serial->publisheddatetext,
1936 C4::Serials->CLAIMED,
1941 my $dbh = C4::Context->dbh;
1944 SET claimdate = NOW(),
1945 claims_count = claims_count + 1,
1947 WHERE serialid in (| . join( q|,|, (q|?|) x @$serialids ) . q|)|,
1948 {}, CLAIMED, @$serialids );
1951 =head2 check_routing
1953 $result = &check_routing($subscriptionid)
1955 this function checks to see if a serial has a routing list and returns the count of routingid
1956 used to show either an 'add' or 'edit' link
1961 my ($subscriptionid) = @_;
1963 return unless ($subscriptionid);
1965 my $dbh = C4::Context->dbh;
1966 my $sth = $dbh->prepare(
1967 "SELECT count(routingid) routingids FROM subscription LEFT JOIN subscriptionroutinglist
1968 ON subscription.subscriptionid = subscriptionroutinglist.subscriptionid
1969 WHERE subscription.subscriptionid = ? GROUP BY routingid ORDER BY ranking ASC
1972 $sth->execute($subscriptionid);
1973 my $line = $sth->fetchrow_hashref;
1974 my $result = $line->{'routingids'};
1978 =head2 addroutingmember
1980 addroutingmember($borrowernumber,$subscriptionid)
1982 this function takes a borrowernumber and subscriptionid and adds the member to the
1983 routing list for that serial subscription and gives them a rank on the list
1984 of either 1 or highest current rank + 1
1988 sub addroutingmember {
1989 my ( $borrowernumber, $subscriptionid ) = @_;
1991 return unless ($borrowernumber and $subscriptionid);
1994 my $dbh = C4::Context->dbh;
1995 my $sth = $dbh->prepare( "SELECT max(ranking) rank FROM subscriptionroutinglist WHERE subscriptionid = ?" );
1996 $sth->execute($subscriptionid);
1997 while ( my $line = $sth->fetchrow_hashref ) {
1998 if ( $line->{'rank'} > 0 ) {
1999 $rank = $line->{'rank'} + 1;
2004 $sth = $dbh->prepare( "INSERT INTO subscriptionroutinglist (subscriptionid,borrowernumber,ranking) VALUES (?,?,?)" );
2005 $sth->execute( $subscriptionid, $borrowernumber, $rank );
2008 =head2 reorder_members
2010 reorder_members($subscriptionid,$routingid,$rank)
2012 this function is used to reorder the routing list
2014 it takes the routingid of the member one wants to re-rank and the rank it is to move to
2015 - it gets all members on list puts their routingid's into an array
2016 - removes the one in the array that is $routingid
2017 - then reinjects $routingid at point indicated by $rank
2018 - then update the database with the routingids in the new order
2022 sub reorder_members {
2023 my ( $subscriptionid, $routingid, $rank ) = @_;
2024 my $dbh = C4::Context->dbh;
2025 my $sth = $dbh->prepare( "SELECT * FROM subscriptionroutinglist WHERE subscriptionid = ? ORDER BY ranking ASC" );
2026 $sth->execute($subscriptionid);
2028 while ( my $line = $sth->fetchrow_hashref ) {
2029 push( @result, $line->{'routingid'} );
2032 # To find the matching index
2034 my $key = -1; # to allow for 0 being a valid response
2035 for ( $i = 0 ; $i < @result ; $i++ ) {
2036 if ( $routingid == $result[$i] ) {
2037 $key = $i; # save the index
2042 # if index exists in array then move it to new position
2043 if ( $key > -1 && $rank > 0 ) {
2044 my $new_rank = $rank - 1; # $new_rank is what you want the new index to be in the array
2045 my $moving_item = splice( @result, $key, 1 );
2046 splice( @result, $new_rank, 0, $moving_item );
2048 for ( my $j = 0 ; $j < @result ; $j++ ) {
2049 my $sth = $dbh->prepare( "UPDATE subscriptionroutinglist SET ranking = '" . ( $j + 1 ) . "' WHERE routingid = '" . $result[$j] . "'" );
2055 =head2 delroutingmember
2057 delroutingmember($routingid,$subscriptionid)
2059 this function either deletes one member from routing list if $routingid exists otherwise
2060 deletes all members from the routing list
2064 sub delroutingmember {
2066 # if $routingid exists then deletes that row otherwise deletes all with $subscriptionid
2067 my ( $routingid, $subscriptionid ) = @_;
2068 my $dbh = C4::Context->dbh;
2070 my $sth = $dbh->prepare("DELETE FROM subscriptionroutinglist WHERE routingid = ?");
2071 $sth->execute($routingid);
2072 reorder_members( $subscriptionid, $routingid );
2074 my $sth = $dbh->prepare("DELETE FROM subscriptionroutinglist WHERE subscriptionid = ?");
2075 $sth->execute($subscriptionid);
2080 =head2 getroutinglist
2082 @routinglist = getroutinglist($subscriptionid)
2084 this gets the info from the subscriptionroutinglist for $subscriptionid
2087 the routinglist as an array. Each element of the array contains a hash_ref containing
2088 routingid - a unique id, borrowernumber, ranking, and biblionumber of subscription
2092 sub getroutinglist {
2093 my ($subscriptionid) = @_;
2094 my $dbh = C4::Context->dbh;
2095 my $sth = $dbh->prepare(
2096 'SELECT routingid, borrowernumber, ranking, biblionumber
2098 JOIN subscriptionroutinglist ON subscription.subscriptionid = subscriptionroutinglist.subscriptionid
2099 WHERE subscription.subscriptionid = ? ORDER BY ranking ASC'
2101 $sth->execute($subscriptionid);
2102 my $routinglist = $sth->fetchall_arrayref({});
2103 return @{$routinglist};
2106 =head2 countissuesfrom
2108 $result = countissuesfrom($subscriptionid,$startdate)
2110 Returns a count of serial rows matching the given subsctiptionid
2111 with published date greater than startdate
2115 sub countissuesfrom {
2116 my ( $subscriptionid, $startdate ) = @_;
2117 my $dbh = C4::Context->dbh;
2121 WHERE subscriptionid=?
2122 AND serial.publisheddate>?
2124 my $sth = $dbh->prepare($query);
2125 $sth->execute( $subscriptionid, $startdate );
2126 my ($countreceived) = $sth->fetchrow;
2127 return $countreceived;
2132 $result = CountIssues($subscriptionid)
2134 Returns a count of serial rows matching the given subsctiptionid
2139 my ($subscriptionid) = @_;
2140 my $dbh = C4::Context->dbh;
2144 WHERE subscriptionid=?
2146 my $sth = $dbh->prepare($query);
2147 $sth->execute($subscriptionid);
2148 my ($countreceived) = $sth->fetchrow;
2149 return $countreceived;
2154 $result = HasItems($subscriptionid)
2156 returns a count of items from serial matching the subscriptionid
2161 my ($subscriptionid) = @_;
2162 my $dbh = C4::Context->dbh;
2164 SELECT COUNT(serialitems.itemnumber)
2166 LEFT JOIN serialitems USING(serialid)
2167 WHERE subscriptionid=? AND serialitems.serialid IS NOT NULL
2169 my $sth=$dbh->prepare($query);
2170 $sth->execute($subscriptionid);
2171 my ($countitems)=$sth->fetchrow_array();
2175 =head2 abouttoexpire
2177 $result = abouttoexpire($subscriptionid)
2179 this function alerts you to the penultimate issue for a serial subscription
2181 returns 1 - if this is the penultimate issue
2187 my ($subscriptionid) = @_;
2188 my $dbh = C4::Context->dbh;
2189 my $subscription = GetSubscription($subscriptionid);
2190 my $per = $subscription->{'periodicity'};
2191 my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($per);
2192 if ($frequency and $frequency->{unit}){
2194 my $expirationdate = GetExpirationDate($subscriptionid);
2196 my ($res) = $dbh->selectrow_array('select max(planneddate) from serial where subscriptionid = ?', undef, $subscriptionid);
2197 my $nextdate = GetNextDate($subscription, $res, $frequency);
2199 # only compare dates if both dates exist.
2200 if ($nextdate and $expirationdate) {
2201 if(Date::Calc::Delta_Days(
2202 split( /-/, $nextdate ),
2203 split( /-/, $expirationdate )
2209 } elsif ( $subscription->{numberlength} && $subscription->{numberlength}>0) {
2210 return (countissuesfrom($subscriptionid,$subscription->{'startdate'}) >=$subscription->{numberlength}-1);
2216 =head2 GetFictiveIssueNumber
2218 $issueno = GetFictiveIssueNumber($subscription, $publishedate, $frequency);
2220 Get the position of the issue published at $publisheddate, considering the
2221 first issue (at firstacquidate) is at position 1, the next is at position 2, etc...
2222 This issuenumber doesn't take into account irregularities, so, for instance, if the 3rd
2223 issue is declared as 'irregular' (will be skipped at receipt), the next issue number
2224 will be 4, not 3. It's why it is called 'fictive'. It is NOT a serial seq, and is not
2225 depending on how many rows are in serial table.
2226 The issue number calculation is based on subscription frequency, first acquisition
2227 date, and $publisheddate.
2229 Returns undef when called for irregular frequencies.
2231 The routine is used to skip irregularities when calculating the next issue
2232 date (in GetNextDate) or the next issue number (in GetNextSeq).
2236 sub GetFictiveIssueNumber {
2237 my ($subscription, $publisheddate, $frequency) = @_;
2239 my $unit = $frequency->{unit} ? lc $frequency->{'unit'} : undef;
2243 my ( $year, $month, $day ) = split /-/, $publisheddate;
2244 my ( $fa_year, $fa_month, $fa_day ) = split /-/, $subscription->{'firstacquidate'};
2245 my $delta = _delta_units( [$fa_year, $fa_month, $fa_day], [$year, $month, $day], $unit );
2247 if( $frequency->{'unitsperissue'} == 1 ) {
2248 $issueno = $delta * $frequency->{'issuesperunit'} + $subscription->{'countissuesperunit'};
2249 } else { # issuesperunit == 1
2250 $issueno = 1 + int( $delta / $frequency->{'unitsperissue'} );
2256 my ( $date1, $date2, $unit ) = @_;
2257 # date1 and date2 are array refs in the form [ yy, mm, dd ]
2259 if( $unit eq 'day' ) {
2260 return Delta_Days( @$date1, @$date2 );
2261 } elsif( $unit eq 'week' ) {
2262 return int( Delta_Days( @$date1, @$date2 ) / 7 );
2265 # In case of months or years, this is a wrapper around N_Delta_YMD.
2266 # Note that N_Delta_YMD returns 29 days between e.g. 22-2-72 and 22-3-72
2267 # while we expect 1 month.
2268 my @delta = N_Delta_YMD( @$date1, @$date2 );
2269 if( $delta[2] > 27 ) {
2270 # Check if we could add a month
2271 my @jump = Add_Delta_YM( @$date1, $delta[0], 1 + $delta[1] );
2272 if( Delta_Days( @jump, @$date2 ) >= 0 ) {
2276 if( $delta[1] >= 12 ) {
2280 # if unit is year, we only return full years
2281 return $unit eq 'month' ? $delta[0] * 12 + $delta[1] : $delta[0];
2284 sub _get_next_date_day {
2285 my ($subscription, $freqdata, $year, $month, $day) = @_;
2287 my @newissue; # ( yy, mm, dd )
2288 # We do not need $delta_days here, since it would be zero where used
2290 if( $freqdata->{issuesperunit} == 1 ) {
2292 @newissue = Add_Delta_Days(
2293 $year, $month, $day, $freqdata->{"unitsperissue"} );
2294 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2296 @newissue = ( $year, $month, $day );
2297 $subscription->{countissuesperunit}++;
2299 # We finished a cycle of issues within a unit.
2300 # No subtraction of zero needed, just add one day
2301 @newissue = Add_Delta_Days( $year, $month, $day, 1 );
2302 $subscription->{countissuesperunit} = 1;
2307 sub _get_next_date_week {
2308 my ($subscription, $freqdata, $year, $month, $day) = @_;
2310 my @newissue; # ( yy, mm, dd )
2311 my $delta_days = int( 7 / $freqdata->{issuesperunit} );
2313 if( $freqdata->{issuesperunit} == 1 ) {
2314 # Add full weeks (of 7 days)
2315 @newissue = Add_Delta_Days(
2316 $year, $month, $day, 7 * $freqdata->{"unitsperissue"} );
2317 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2318 # Add rounded number of days based on frequency.
2319 @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
2320 $subscription->{countissuesperunit}++;
2322 # We finished a cycle of issues within a unit.
2323 # Subtract delta * (issues - 1), add 1 week
2324 @newissue = Add_Delta_Days( $year, $month, $day,
2325 -$delta_days * ($freqdata->{issuesperunit} - 1) );
2326 @newissue = Add_Delta_Days( @newissue, 7 );
2327 $subscription->{countissuesperunit} = 1;
2332 sub _get_next_date_month {
2333 my ($subscription, $freqdata, $year, $month, $day) = @_;
2335 my @newissue; # ( yy, mm, dd )
2336 my $delta_days = int( 30 / $freqdata->{issuesperunit} );
2338 if( $freqdata->{issuesperunit} == 1 ) {
2340 @newissue = Add_Delta_YM(
2341 $year, $month, $day, 0, $freqdata->{"unitsperissue"} );
2342 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2343 # Add rounded number of days based on frequency.
2344 @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
2345 $subscription->{countissuesperunit}++;
2347 # We finished a cycle of issues within a unit.
2348 # Subtract delta * (issues - 1), add 1 month
2349 @newissue = Add_Delta_Days( $year, $month, $day,
2350 -$delta_days * ($freqdata->{issuesperunit} - 1) );
2351 @newissue = Add_Delta_YM( @newissue, 0, 1 );
2352 $subscription->{countissuesperunit} = 1;
2357 sub _get_next_date_year {
2358 my ($subscription, $freqdata, $year, $month, $day) = @_;
2360 my @newissue; # ( yy, mm, dd )
2361 my $delta_days = int( 365 / $freqdata->{issuesperunit} );
2363 if( $freqdata->{issuesperunit} == 1 ) {
2365 @newissue = Add_Delta_YM( $year, $month, $day, $freqdata->{"unitsperissue"}, 0 );
2366 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2367 # Add rounded number of days based on frequency.
2368 @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
2369 $subscription->{countissuesperunit}++;
2371 # We finished a cycle of issues within a unit.
2372 # Subtract delta * (issues - 1), add 1 year
2373 @newissue = Add_Delta_Days( $year, $month, $day, -$delta_days * ($freqdata->{issuesperunit} - 1) );
2374 @newissue = Add_Delta_YM( @newissue, 1, 0 );
2375 $subscription->{countissuesperunit} = 1;
2382 $resultdate = GetNextDate($publisheddate,$subscription,$freqdata,$updatecount)
2384 this function it takes the publisheddate and will return the next issue's date
2385 and will skip dates if there exists an irregularity.
2386 $publisheddate has to be an ISO date
2387 $subscription is a hashref containing at least 'firstacquidate', 'irregularity', and 'countissuesperunit'
2388 $frequency is a hashref containing frequency informations
2389 $updatecount is a boolean value which, when set to true, update the 'countissuesperunit' in database
2390 - eg if periodicity is monthly and $publisheddate is 2007-02-10 but if March and April is to be
2391 skipped then the returned date will be 2007-05-10
2394 $resultdate - then next date in the sequence (ISO date)
2396 Return undef if subscription is irregular
2401 my ( $subscription, $publisheddate, $freqdata, $updatecount ) = @_;
2403 return unless $subscription and $publisheddate;
2406 if ($freqdata->{'unit'}) {
2407 my ( $year, $month, $day ) = split /-/, $publisheddate;
2409 # Process an irregularity Hash
2410 # Suppose that irregularities are stored in a string with this structure
2411 # irreg1;irreg2;irreg3
2412 # where irregX is the number of issue which will not be received
2413 # (the first issue takes the number 1, the 2nd the number 2 and so on)
2415 if ( $subscription->{irregularity} ) {
2416 my @irreg = split /;/, $subscription->{'irregularity'} ;
2417 foreach my $irregularity (@irreg) {
2418 $irregularities{$irregularity} = 1;
2422 # Get the 'fictive' next issue number
2423 # It is used to check if next issue is an irregular issue.
2424 my $issueno = GetFictiveIssueNumber($subscription, $publisheddate, $freqdata) + 1;
2426 # Then get the next date
2427 my $unit = lc $freqdata->{'unit'};
2428 if ($unit eq 'day') {
2429 while ($irregularities{$issueno}) {
2430 ($year, $month, $day) = _get_next_date_day($subscription,
2431 $freqdata, $year, $month, $day);
2434 ($year, $month, $day) = _get_next_date_day($subscription, $freqdata,
2435 $year, $month, $day);
2437 elsif ($unit eq 'week') {
2438 while ($irregularities{$issueno}) {
2439 ($year, $month, $day) = _get_next_date_week($subscription,
2440 $freqdata, $year, $month, $day);
2443 ($year, $month, $day) = _get_next_date_week($subscription,
2444 $freqdata, $year, $month, $day);
2446 elsif ($unit eq 'month') {
2447 while ($irregularities{$issueno}) {
2448 ($year, $month, $day) = _get_next_date_month($subscription,
2449 $freqdata, $year, $month, $day);
2452 ($year, $month, $day) = _get_next_date_month($subscription,
2453 $freqdata, $year, $month, $day);
2455 elsif ($unit eq 'year') {
2456 while ($irregularities{$issueno}) {
2457 ($year, $month, $day) = _get_next_date_year($subscription,
2458 $freqdata, $year, $month, $day);
2461 ($year, $month, $day) = _get_next_date_year($subscription,
2462 $freqdata, $year, $month, $day);
2466 my $dbh = C4::Context->dbh;
2469 SET countissuesperunit = ?
2470 WHERE subscriptionid = ?
2472 my $sth = $dbh->prepare($query);
2473 $sth->execute($subscription->{'countissuesperunit'}, $subscription->{'subscriptionid'});
2476 return sprintf("%04d-%02d-%02d", $year, $month, $day);
2482 $string = &_numeration($value,$num_type,$locale);
2484 _numeration returns the string corresponding to $value in the num_type
2496 my ($value, $num_type, $locale) = @_;
2501 if ( $num_type =~ /^dayname$/ or $num_type =~ /^dayabrv$/ ) {
2502 # 1970-11-01 was a Sunday
2503 $value = $value % 7;
2504 my $dt = DateTime->new(
2510 $string = $num_type =~ /^dayname$/
2511 ? $dt->strftime("%A")
2512 : $dt->strftime("%a");
2513 } elsif ( $num_type =~ /^monthname$/ or $num_type =~ /^monthabrv$/ ) {
2514 $value = $value % 12;
2515 my $dt = DateTime->new(
2517 month => $value + 1,
2520 $string = $num_type =~ /^monthname$/
2521 ? $dt->format_cldr( "LLLL" )
2522 : $dt->strftime("%b");
2523 } elsif ( $num_type =~ /^season$/ ) {
2524 my @seasons= qw( Spring Summer Fall Winter );
2525 $value = $value % 4;
2526 $string = $seasons[$value];
2527 } elsif ( $num_type =~ /^seasonabrv$/ ) {
2528 my @seasonsabrv= qw( Spr Sum Fal Win );
2529 $value = $value % 4;
2530 $string = $seasonsabrv[$value];
2538 =head2 CloseSubscription
2540 Close a subscription given a subscriptionid
2544 sub CloseSubscription {
2545 my ( $subscriptionid ) = @_;
2546 return unless $subscriptionid;
2547 my $dbh = C4::Context->dbh;
2548 my $sth = $dbh->prepare( q{
2551 WHERE subscriptionid = ?
2553 $sth->execute( $subscriptionid );
2555 # Set status = missing when status = stopped
2556 $sth = $dbh->prepare( q{
2559 WHERE subscriptionid = ?
2562 $sth->execute( STOPPED, $subscriptionid, EXPECTED );
2565 =head2 ReopenSubscription
2567 Reopen a subscription given a subscriptionid
2571 sub ReopenSubscription {
2572 my ( $subscriptionid ) = @_;
2573 return unless $subscriptionid;
2574 my $dbh = C4::Context->dbh;
2575 my $sth = $dbh->prepare( q{
2578 WHERE subscriptionid = ?
2580 $sth->execute( $subscriptionid );
2582 # Set status = expected when status = stopped
2583 $sth = $dbh->prepare( q{
2586 WHERE subscriptionid = ?
2589 $sth->execute( EXPECTED, $subscriptionid, STOPPED );
2592 =head2 subscriptionCurrentlyOnOrder
2594 $bool = subscriptionCurrentlyOnOrder( $subscriptionid );
2596 Return 1 if subscription is currently on order else 0.
2600 sub subscriptionCurrentlyOnOrder {
2601 my ( $subscriptionid ) = @_;
2602 my $dbh = C4::Context->dbh;
2604 SELECT COUNT(*) FROM aqorders
2605 WHERE subscriptionid = ?
2606 AND datereceived IS NULL
2607 AND datecancellationprinted IS NULL
2609 my $sth = $dbh->prepare( $query );
2610 $sth->execute($subscriptionid);
2611 return $sth->fetchrow_array;
2614 =head2 can_claim_subscription
2616 $can = can_claim_subscription( $subscriptionid[, $userid] );
2618 Return 1 if the subscription can be claimed by the current logged user (or a given $userid), else 0.
2622 sub can_claim_subscription {
2623 my ( $subscription, $userid ) = @_;
2624 return _can_do_on_subscription( $subscription, $userid, 'claim_serials' );
2627 =head2 can_edit_subscription
2629 $can = can_edit_subscription( $subscriptionid[, $userid] );
2631 Return 1 if the subscription can be edited by the current logged user (or a given $userid), else 0.
2635 sub can_edit_subscription {
2636 my ( $subscription, $userid ) = @_;
2637 return _can_do_on_subscription( $subscription, $userid, 'edit_subscription' );
2640 =head2 can_show_subscription
2642 $can = can_show_subscription( $subscriptionid[, $userid] );
2644 Return 1 if the subscription can be shown by the current logged user (or a given $userid), else 0.
2648 sub can_show_subscription {
2649 my ( $subscription, $userid ) = @_;
2650 return _can_do_on_subscription( $subscription, $userid, '*' );
2653 sub _can_do_on_subscription {
2654 my ( $subscription, $userid, $permission ) = @_;
2655 return 0 unless C4::Context->userenv;
2656 my $flags = C4::Context->userenv->{flags};
2657 $userid ||= C4::Context->userenv->{'id'};
2659 if ( C4::Context->preference('IndependentBranches') ) {
2661 if C4::Context->IsSuperLibrarian()
2663 C4::Auth::haspermission( $userid, { serials => 'superserials' } )
2665 C4::Auth::haspermission( $userid,
2666 { serials => $permission } )
2667 and ( not defined $subscription->{branchcode}
2668 or $subscription->{branchcode} eq ''
2669 or $subscription->{branchcode} eq
2670 C4::Context->userenv->{'branch'} )
2675 if C4::Context->IsSuperLibrarian()
2677 C4::Auth::haspermission( $userid, { serials => 'superserials' } )
2678 or C4::Auth::haspermission(
2679 $userid, { serials => $permission }
2686 =head2 findSerialsByStatus
2688 @serials = findSerialsByStatus($status, $subscriptionid);
2690 Returns an array of serials matching a given status and subscription id.
2694 sub findSerialsByStatus {
2695 my ( $status, $subscriptionid ) = @_;
2696 my $dbh = C4::Context->dbh;
2697 my $query = q| SELECT * from serial
2699 AND subscriptionid = ?
2701 my $serials = $dbh->selectall_arrayref( $query, { Slice => {} }, $status, $subscriptionid );
2710 Koha Development Team <http://koha-community.org/>