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>.
23 use C4::Auth qw(haspermission);
26 use Date::Calc qw(:all);
27 use POSIX qw(strftime);
29 use C4::Log; # logaction
31 use C4::Serials::Frequency;
32 use C4::Serials::Numberpattern;
33 use Koha::AdditionalFieldValues;
36 use Koha::Subscriptions;
37 use Koha::Subscription::Histories;
38 use Koha::SharedContent;
39 use Scalar::Util qw( looks_like_number );
41 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
49 MISSING_NEVER_RECIEVED => 41,
50 MISSING_SOLD_OUT => 42,
51 MISSING_DAMAGED => 43,
59 use constant MISSING_STATUSES => (
60 MISSING, MISSING_NEVER_RECIEVED,
61 MISSING_SOLD_OUT, MISSING_DAMAGED,
69 &NewSubscription &ModSubscription &DelSubscription
70 &GetSubscription &CountSubscriptionFromBiblionumber &GetSubscriptionsFromBiblionumber
72 &GetFullSubscriptionsFromBiblionumber &GetFullSubscription &ModSubscriptionHistory
73 &HasSubscriptionStrictlyExpired &HasSubscriptionExpired &GetExpirationDate &abouttoexpire
74 &GetSubscriptionHistoryFromSubscriptionId
76 &GetNextSeq &GetSeq &NewIssue &GetSerials
77 &GetLatestSerials &ModSerialStatus &GetNextDate &GetSerials2
78 &GetSubscriptionLength &ReNewSubscription &GetLateOrMissingIssues
79 &GetSerialInformation &AddItem2Serial
80 &PrepareSerialsData &GetNextExpected &ModNextExpected
83 &GetSuppliersWithLateIssues
84 &getroutinglist &delroutingmember &addroutingmember
86 &check_routing &updateClaim
89 &subscriptionCurrentlyOnOrder
96 C4::Serials - Serials Module Functions
104 Functions for handling subscriptions, claims routing etc.
109 =head2 GetSuppliersWithLateIssues
111 $supplierlist = GetSuppliersWithLateIssues()
113 this function get all suppliers with late issues.
116 an array_ref of suppliers each entry is a hash_ref containing id and name
117 the array is in name order
121 sub GetSuppliersWithLateIssues {
122 my $dbh = C4::Context->dbh;
123 my $statuses = join(',', ( LATE, MISSING_STATUSES, CLAIMED ) );
125 SELECT DISTINCT id, name
127 LEFT JOIN serial ON serial.subscriptionid=subscription.subscriptionid
128 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
131 (planneddate < now() AND serial.status=1)
132 OR serial.STATUS IN ( $statuses )
134 AND subscription.closed = 0
136 return $dbh->selectall_arrayref($query, { Slice => {} });
139 =head2 GetSubscriptionHistoryFromSubscriptionId
141 $history = GetSubscriptionHistoryFromSubscriptionId($subscriptionid);
143 This function returns the subscription history as a hashref
147 sub GetSubscriptionHistoryFromSubscriptionId {
148 my ($subscriptionid) = @_;
150 return unless $subscriptionid;
152 my $dbh = C4::Context->dbh;
155 FROM subscriptionhistory
156 WHERE subscriptionid = ?
158 my $sth = $dbh->prepare($query);
159 $sth->execute($subscriptionid);
160 my $results = $sth->fetchrow_hashref;
166 =head2 GetSerialInformation
168 $data = GetSerialInformation($serialid);
169 returns a hash_ref containing :
170 items : items marcrecord (can be an array)
172 subscription table field
173 + information about subscription expiration
177 sub GetSerialInformation {
179 my $dbh = C4::Context->dbh;
181 SELECT serial.*, serial.notes as sernotes, serial.status as serstatus,subscription.*,subscription.subscriptionid as subsid
182 FROM serial LEFT JOIN subscription ON subscription.subscriptionid=serial.subscriptionid
185 my $rq = $dbh->prepare($query);
186 $rq->execute($serialid);
187 my $data = $rq->fetchrow_hashref;
189 # create item information if we have serialsadditems for this subscription
190 if ( $data->{'serialsadditems'} ) {
191 my $queryitem = $dbh->prepare("SELECT itemnumber from serialitems where serialid=?");
192 $queryitem->execute($serialid);
193 my $itemnumbers = $queryitem->fetchall_arrayref( [0] );
195 if ( scalar(@$itemnumbers) > 0 ) {
196 foreach my $itemnum (@$itemnumbers) {
198 #It is ASSUMED that GetMarcItem ALWAYS WORK...
199 #Maybe GetMarcItem should return values on failure
200 $debug and warn "itemnumber :$itemnum->[0], bibnum :" . $data->{'biblionumber'};
201 my $itemprocessed = C4::Items::PrepareItemrecordDisplay( $data->{'biblionumber'}, $itemnum->[0], $data );
202 $itemprocessed->{'itemnumber'} = $itemnum->[0];
203 $itemprocessed->{'itemid'} = $itemnum->[0];
204 $itemprocessed->{'serialid'} = $serialid;
205 $itemprocessed->{'biblionumber'} = $data->{'biblionumber'};
206 push @{ $data->{'items'} }, $itemprocessed;
209 my $itemprocessed = C4::Items::PrepareItemrecordDisplay( $data->{'biblionumber'}, '', $data );
210 $itemprocessed->{'itemid'} = "N$serialid";
211 $itemprocessed->{'serialid'} = $serialid;
212 $itemprocessed->{'biblionumber'} = $data->{'biblionumber'};
213 $itemprocessed->{'countitems'} = 0;
214 push @{ $data->{'items'} }, $itemprocessed;
217 $data->{ "status" . $data->{'serstatus'} } = 1;
218 $data->{'subscriptionexpired'} = HasSubscriptionExpired( $data->{'subscriptionid'} ) && $data->{'status'} == 1;
219 $data->{'abouttoexpire'} = abouttoexpire( $data->{'subscriptionid'} );
220 $data->{cannotedit} = not can_edit_subscription( $data );
224 =head2 AddItem2Serial
226 $rows = AddItem2Serial($serialid,$itemnumber);
227 Adds an itemnumber to Serial record
228 returns the number of rows affected
233 my ( $serialid, $itemnumber ) = @_;
235 return unless ($serialid and $itemnumber);
237 my $dbh = C4::Context->dbh;
238 my $rq = $dbh->prepare("INSERT INTO `serialitems` SET serialid=? , itemnumber=?");
239 $rq->execute( $serialid, $itemnumber );
243 =head2 GetSubscription
245 $subs = GetSubscription($subscriptionid)
246 this function returns the subscription which has $subscriptionid as id.
248 a hashref. This hash contains
249 subscription, subscriptionhistory, aqbooksellers.name, biblio.title
253 sub GetSubscription {
254 my ($subscriptionid) = @_;
255 my $dbh = C4::Context->dbh;
257 SELECT subscription.*,
258 subscriptionhistory.*,
259 aqbooksellers.name AS aqbooksellername,
260 biblio.title AS bibliotitle,
261 subscription.biblionumber as bibnum
263 LEFT JOIN subscriptionhistory ON subscription.subscriptionid=subscriptionhistory.subscriptionid
264 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid=aqbooksellers.id
265 LEFT JOIN biblio ON biblio.biblionumber=subscription.biblionumber
266 WHERE subscription.subscriptionid = ?
269 $debug and warn "query : $query\nsubsid :$subscriptionid";
270 my $sth = $dbh->prepare($query);
271 $sth->execute($subscriptionid);
272 my $subscription = $sth->fetchrow_hashref;
274 return unless $subscription;
276 $subscription->{cannotedit} = not can_edit_subscription( $subscription );
278 if ( my $mana_id = $subscription->{mana_id} ) {
279 my $mana_subscription = Koha::SharedContent::get_entity_by_id(
280 'subscription', $mana_id, {usecomments => 1});
281 $subscription->{comments} = $mana_subscription->{data}->{comments};
284 return $subscription;
287 =head2 GetFullSubscription
289 $array_ref = GetFullSubscription($subscriptionid)
290 this function reads the serial table.
294 sub GetFullSubscription {
295 my ($subscriptionid) = @_;
297 return unless ($subscriptionid);
299 my $dbh = C4::Context->dbh;
301 SELECT serial.serialid,
304 serial.publisheddate,
305 serial.publisheddatetext,
307 serial.notes as notes,
308 year(IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate)) as year,
309 aqbooksellers.name as aqbooksellername,
310 biblio.title as bibliotitle,
311 subscription.branchcode AS branchcode,
312 subscription.subscriptionid AS subscriptionid
314 LEFT JOIN subscription ON
315 (serial.subscriptionid=subscription.subscriptionid )
316 LEFT JOIN aqbooksellers on subscription.aqbooksellerid=aqbooksellers.id
317 LEFT JOIN biblio on biblio.biblionumber=subscription.biblionumber
318 WHERE serial.subscriptionid = ?
320 IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate) DESC,
321 serial.subscriptionid
323 $debug and warn "GetFullSubscription query: $query";
324 my $sth = $dbh->prepare($query);
325 $sth->execute($subscriptionid);
326 my $subscriptions = $sth->fetchall_arrayref( {} );
327 if (scalar @$subscriptions) {
328 my $cannotedit = not can_edit_subscription( $subscriptions->[0] );
329 for my $subscription ( @$subscriptions ) {
330 $subscription->{cannotedit} = $cannotedit;
334 return $subscriptions;
337 =head2 PrepareSerialsData
339 $array_ref = PrepareSerialsData($serialinfomation)
340 where serialinformation is a hashref array
344 sub PrepareSerialsData {
347 return unless ($lines);
354 my $previousnote = "";
356 foreach my $subs (@{$lines}) {
357 $subs->{ "status" . $subs->{'status'} } = 1;
358 if ( grep { $_ == $subs->{status} } ( EXPECTED, LATE, MISSING_STATUSES, CLAIMED ) ) {
359 $subs->{"checked"} = 1;
362 if ( $subs->{'year'} && $subs->{'year'} ne "" ) {
363 $year = $subs->{'year'};
367 if ( $tmpresults{$year} ) {
368 push @{ $tmpresults{$year}->{'serials'} }, $subs;
370 $tmpresults{$year} = {
372 'aqbooksellername' => $subs->{'aqbooksellername'},
373 'bibliotitle' => $subs->{'bibliotitle'},
374 'serials' => [$subs],
379 foreach my $key ( sort { $b cmp $a } keys %tmpresults ) {
380 push @res, $tmpresults{$key};
385 =head2 GetSubscriptionsFromBiblionumber
387 $array_ref = GetSubscriptionsFromBiblionumber($biblionumber)
388 this function get the subscription list. it reads the subscription table.
390 reference to an array of subscriptions which have the biblionumber given on input arg.
391 each element of this array is a hashref containing
392 startdate, histstartdate,opacnote,missinglist,recievedlist,periodicity,status & enddate
396 sub GetSubscriptionsFromBiblionumber {
397 my ($biblionumber) = @_;
399 return unless ($biblionumber);
401 my $dbh = C4::Context->dbh;
403 SELECT subscription.*,
405 subscriptionhistory.*,
406 aqbooksellers.name AS aqbooksellername,
407 biblio.title AS bibliotitle
409 LEFT JOIN subscriptionhistory ON subscription.subscriptionid=subscriptionhistory.subscriptionid
410 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid=aqbooksellers.id
411 LEFT JOIN biblio ON biblio.biblionumber=subscription.biblionumber
412 LEFT JOIN branches ON branches.branchcode=subscription.branchcode
413 WHERE subscription.biblionumber = ?
415 my $sth = $dbh->prepare($query);
416 $sth->execute($biblionumber);
418 while ( my $subs = $sth->fetchrow_hashref ) {
419 $subs->{startdate} = output_pref( { dt => dt_from_string( $subs->{startdate} ), dateonly => 1 } );
420 $subs->{histstartdate} = output_pref( { dt => dt_from_string( $subs->{histstartdate} ), dateonly => 1 } );
421 if ( defined $subs->{histenddate} ) {
422 $subs->{histenddate} = output_pref( { dt => dt_from_string( $subs->{histenddate} ), dateonly => 1 } );
424 $subs->{histenddate} = "";
426 $subs->{opacnote} //= "";
427 $subs->{ "periodicity" . $subs->{periodicity} } = 1;
428 $subs->{ "numberpattern" . $subs->{numberpattern} } = 1;
429 $subs->{ "status" . $subs->{'status'} } = 1;
431 if (not defined $subs->{enddate} ) {
432 $subs->{enddate} = '';
434 $subs->{enddate} = output_pref( { dt => dt_from_string( $subs->{enddate}), dateonly => 1 } );
436 $subs->{'abouttoexpire'} = abouttoexpire( $subs->{'subscriptionid'} );
437 $subs->{'subscriptionexpired'} = HasSubscriptionExpired( $subs->{'subscriptionid'} );
438 $subs->{cannotedit} = not can_edit_subscription( $subs );
444 =head2 GetFullSubscriptionsFromBiblionumber
446 $array_ref = GetFullSubscriptionsFromBiblionumber($biblionumber)
447 this function reads the serial table.
451 sub GetFullSubscriptionsFromBiblionumber {
452 my ($biblionumber) = @_;
453 my $dbh = C4::Context->dbh;
455 SELECT serial.serialid,
458 serial.publisheddate,
459 serial.publisheddatetext,
461 serial.notes as notes,
462 year(IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate)) as year,
463 biblio.title as bibliotitle,
464 subscription.branchcode AS branchcode,
465 subscription.subscriptionid AS subscriptionid
467 LEFT JOIN subscription ON
468 (serial.subscriptionid=subscription.subscriptionid)
469 LEFT JOIN aqbooksellers on subscription.aqbooksellerid=aqbooksellers.id
470 LEFT JOIN biblio on biblio.biblionumber=subscription.biblionumber
471 WHERE subscription.biblionumber = ?
473 IF(serial.publisheddate IS NULL,serial.planneddate,serial.publisheddate) DESC,
474 serial.subscriptionid
476 my $sth = $dbh->prepare($query);
477 $sth->execute($biblionumber);
478 my $subscriptions = $sth->fetchall_arrayref( {} );
479 if (scalar @$subscriptions) {
480 my $cannotedit = not can_edit_subscription( $subscriptions->[0] );
481 for my $subscription ( @$subscriptions ) {
482 $subscription->{cannotedit} = $cannotedit;
486 return $subscriptions;
489 =head2 SearchSubscriptions
491 @results = SearchSubscriptions($args);
493 This function returns a list of hashrefs, one for each subscription
494 that meets the conditions specified by the $args hashref.
496 The valid search fields are:
510 The expiration_date search field is special; it specifies the maximum
511 subscription expiration date.
515 sub SearchSubscriptions {
518 my $additional_fields = $args->{additional_fields} // [];
519 my $matching_record_ids_for_additional_fields = [];
520 if ( @$additional_fields ) {
521 my @subscriptions = Koha::Subscriptions->filter_by_additional_fields($additional_fields);
523 return () unless @subscriptions;
525 $matching_record_ids_for_additional_fields = [ map {
532 subscription.notes AS publicnotes,
533 subscriptionhistory.*,
535 biblio.notes AS biblionotes,
539 aqbooksellers.name AS vendorname,
542 LEFT JOIN subscriptionhistory USING(subscriptionid)
543 LEFT JOIN biblio ON biblio.biblionumber = subscription.biblionumber
544 LEFT JOIN biblioitems ON biblioitems.biblionumber = subscription.biblionumber
545 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
547 $query .= q| WHERE 1|;
550 if( $args->{biblionumber} ) {
551 push @where_strs, "biblio.biblionumber = ?";
552 push @where_args, $args->{biblionumber};
555 if( $args->{title} ){
556 my @words = split / /, $args->{title};
558 foreach my $word (@words) {
559 push @strs, "biblio.title LIKE ?";
560 push @args, "%$word%";
563 push @where_strs, '(' . join (' AND ', @strs) . ')';
564 push @where_args, @args;
568 push @where_strs, "biblioitems.issn LIKE ?";
569 push @where_args, "%$args->{issn}%";
572 push @where_strs, "biblioitems.ean LIKE ?";
573 push @where_args, "%$args->{ean}%";
575 if ( $args->{callnumber} ) {
576 push @where_strs, "subscription.callnumber LIKE ?";
577 push @where_args, "%$args->{callnumber}%";
579 if( $args->{publisher} ){
580 push @where_strs, "biblioitems.publishercode LIKE ?";
581 push @where_args, "%$args->{publisher}%";
583 if( $args->{bookseller} ){
584 push @where_strs, "aqbooksellers.name LIKE ?";
585 push @where_args, "%$args->{bookseller}%";
587 if( $args->{branch} ){
588 push @where_strs, "subscription.branchcode = ?";
589 push @where_args, "$args->{branch}";
591 if ( $args->{location} ) {
592 push @where_strs, "subscription.location = ?";
593 push @where_args, "$args->{location}";
595 if ( $args->{expiration_date} ) {
596 push @where_strs, "subscription.enddate <= ?";
597 push @where_args, "$args->{expiration_date}";
599 if( defined $args->{closed} ){
600 push @where_strs, "subscription.closed = ?";
601 push @where_args, "$args->{closed}";
605 $query .= ' AND ' . join(' AND ', @where_strs);
607 if ( @$additional_fields ) {
608 $query .= ' AND subscriptionid IN ('
609 . join( ', ', @$matching_record_ids_for_additional_fields )
613 $query .= " ORDER BY " . $args->{orderby} if $args->{orderby};
615 my $dbh = C4::Context->dbh;
616 my $sth = $dbh->prepare($query);
617 $sth->execute(@where_args);
618 my $results = $sth->fetchall_arrayref( {} );
620 for my $subscription ( @$results ) {
621 $subscription->{cannotedit} = not can_edit_subscription( $subscription );
622 $subscription->{cannotdisplay} = not can_show_subscription( $subscription );
624 my $subscription_object = Koha::Subscriptions->find($subscription->{subscriptionid});
625 $subscription->{additional_fields} = { map { $_->field->name => $_->value }
626 $subscription_object->additional_field_values->as_list };
636 ($totalissues,@serials) = GetSerials($subscriptionid);
637 this function gets every serial not arrived for a given subscription
638 as well as the number of issues registered in the database (all types)
639 this number is used to see if a subscription can be deleted (=it must have only 1 issue)
641 FIXME: We should return \@serials.
646 my ( $subscriptionid, $count ) = @_;
648 return unless $subscriptionid;
650 my $dbh = C4::Context->dbh;
652 # status = 2 is "arrived"
654 $count = 5 unless ($count);
656 my $statuses = join( ',', ( ARRIVED, MISSING_STATUSES, NOT_ISSUED ) );
657 my $query = "SELECT serialid,serialseq, status, publisheddate,
658 publisheddatetext, planneddate,notes, routingnotes
660 WHERE subscriptionid = ? AND status NOT IN ( $statuses )
661 ORDER BY IF(publisheddate IS NULL,planneddate,publisheddate) DESC";
662 my $sth = $dbh->prepare($query);
663 $sth->execute($subscriptionid);
665 while ( my $line = $sth->fetchrow_hashref ) {
666 $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
667 for my $datefield ( qw( planneddate publisheddate) ) {
668 if ($line->{$datefield} && $line->{$datefield}!~m/^00/) {
669 $line->{$datefield} = output_pref( { dt => dt_from_string( $line->{$datefield} ), dateonly => 1 } );
671 $line->{$datefield} = q{};
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
690 for my $datefield ( qw( planneddate publisheddate) ) {
691 if ($line->{$datefield} && $line->{$datefield}!~m/^00/) {
692 $line->{$datefield} = output_pref( { dt => dt_from_string( $line->{$datefield} ), dateonly => 1 } );
694 $line->{$datefield} = q{};
698 push @serials, $line;
701 $query = "SELECT count(*) FROM serial WHERE subscriptionid=?";
702 $sth = $dbh->prepare($query);
703 $sth->execute($subscriptionid);
704 my ($totalissues) = $sth->fetchrow;
705 return ( $totalissues, @serials );
710 @serials = GetSerials2($subscriptionid,$statuses);
711 this function returns every serial waited for a given subscription
712 as well as the number of issues registered in the database (all types)
713 this number is used to see if a subscription can be deleted (=it must have only 1 issue)
715 $statuses is an arrayref of statuses and is mandatory.
720 my ( $subscription, $statuses ) = @_;
722 return unless ($subscription and @$statuses);
724 my $dbh = C4::Context->dbh;
726 SELECT serialid,serialseq, status, planneddate, publisheddate,
727 publisheddatetext, notes, routingnotes
729 WHERE subscriptionid=?
731 . q| AND status IN (| . join( ",", ('?') x @$statuses ) . q|)|
733 ORDER BY publisheddate,serialid DESC
735 $debug and warn "GetSerials2 query: $query";
736 my $sth = $dbh->prepare($query);
737 $sth->execute( $subscription, @$statuses );
740 while ( my $line = $sth->fetchrow_hashref ) {
741 $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
742 # Format dates for display
743 for my $datefield ( qw( planneddate publisheddate ) ) {
744 if (!defined($line->{$datefield}) || $line->{$datefield} =~m/^00/) {
745 $line->{$datefield} = q{};
748 $line->{$datefield} = output_pref( { dt => dt_from_string( $line->{$datefield} ), dateonly => 1 } );
751 push @serials, $line;
756 =head2 GetLatestSerials
758 \@serials = GetLatestSerials($subscriptionid,$limit)
759 get the $limit's latest serials arrived or missing for a given subscription
761 a ref to an array which contains all of the latest serials stored into a hash.
765 sub GetLatestSerials {
766 my ( $subscriptionid, $limit ) = @_;
768 return unless ($subscriptionid and $limit);
770 my $dbh = C4::Context->dbh;
772 my $statuses = join( ',', ( ARRIVED, MISSING_STATUSES ) );
773 my $strsth = "SELECT serialid,serialseq, status, planneddate, publisheddate, notes
775 WHERE subscriptionid = ?
776 AND status IN ($statuses)
777 ORDER BY publisheddate DESC LIMIT 0,$limit
779 my $sth = $dbh->prepare($strsth);
780 $sth->execute($subscriptionid);
782 while ( my $line = $sth->fetchrow_hashref ) {
783 $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
784 push @serials, $line;
790 =head2 GetPreviousSerialid
792 $serialid = GetPreviousSerialid($subscriptionid, $nth)
793 get the $nth's previous serial for the given subscriptionid
799 sub GetPreviousSerialid {
800 my ( $subscriptionid, $nth ) = @_;
802 my $dbh = C4::Context->dbh;
806 my $strsth = "SELECT serialid
808 WHERE subscriptionid = ?
810 ORDER BY serialid DESC LIMIT $nth,1
812 my $sth = $dbh->prepare($strsth);
813 $sth->execute($subscriptionid);
815 my $line = $sth->fetchrow_hashref;
816 $return = $line->{'serialid'} if ($line);
824 $nextseq, $newlastvalue1, $newlastvalue2, $newlastvalue3,
825 $newinnerloop1, $newinnerloop2, $newinnerloop3
826 ) = GetNextSeq( $subscription, $pattern, $frequency, $planneddate );
828 $subscription is a hashref containing all the attributes of the table
830 $pattern is a hashref containing all the attributes of the table
831 'subscription_numberpatterns'.
832 $frequency is a hashref containing all the attributes of the table 'subscription_frequencies'
833 $planneddate is a date string in iso format.
834 This function get the next issue for the subscription given on input arg
839 my ($subscription, $pattern, $frequency, $planneddate) = @_;
841 return unless ($subscription and $pattern);
843 my ( $newlastvalue1, $newlastvalue2, $newlastvalue3,
844 $newinnerloop1, $newinnerloop2, $newinnerloop3 );
847 if ($subscription->{'skip_serialseq'}) {
848 my @irreg = split /;/, $subscription->{'irregularity'};
850 my $irregularities = {};
851 $irregularities->{$_} = 1 foreach(@irreg);
852 my $issueno = GetFictiveIssueNumber($subscription, $planneddate, $frequency) + 1;
853 while($irregularities->{$issueno}) {
860 my $numberingmethod = $pattern->{numberingmethod};
862 if ($numberingmethod) {
863 $calculated = $numberingmethod;
864 my $locale = $subscription->{locale};
865 $newlastvalue1 = $subscription->{lastvalue1} || 0;
866 $newlastvalue2 = $subscription->{lastvalue2} || 0;
867 $newlastvalue3 = $subscription->{lastvalue3} || 0;
868 $newinnerloop1 = $subscription->{innerloop1} || 0;
869 $newinnerloop2 = $subscription->{innerloop2} || 0;
870 $newinnerloop3 = $subscription->{innerloop3} || 0;
873 $calc{$_} = 1 if ($numberingmethod =~ /\{$_\}/);
876 for(my $i = 0; $i < $count; $i++) {
878 # check if we have to increase the new value.
880 if ($newinnerloop1 >= $pattern->{every1}) {
882 $newlastvalue1 += $pattern->{add1};
884 # reset counter if needed.
885 $newlastvalue1 = $pattern->{setto1} if ($newlastvalue1 > $pattern->{whenmorethan1});
888 # check if we have to increase the new value.
890 if ($newinnerloop2 >= $pattern->{every2}) {
892 $newlastvalue2 += $pattern->{add2};
894 # reset counter if needed.
895 $newlastvalue2 = $pattern->{setto2} if ($newlastvalue2 > $pattern->{whenmorethan2});
898 # check if we have to increase the new value.
900 if ($newinnerloop3 >= $pattern->{every3}) {
902 $newlastvalue3 += $pattern->{add3};
904 # reset counter if needed.
905 $newlastvalue3 = $pattern->{setto3} if ($newlastvalue3 > $pattern->{whenmorethan3});
909 my $newlastvalue1string = _numeration( $newlastvalue1, $pattern->{numbering1}, $locale );
910 $calculated =~ s/\{X\}/$newlastvalue1string/g;
913 my $newlastvalue2string = _numeration( $newlastvalue2, $pattern->{numbering2}, $locale );
914 $calculated =~ s/\{Y\}/$newlastvalue2string/g;
917 my $newlastvalue3string = _numeration( $newlastvalue3, $pattern->{numbering3}, $locale );
918 $calculated =~ s/\{Z\}/$newlastvalue3string/g;
923 $newlastvalue1, $newlastvalue2, $newlastvalue3,
924 $newinnerloop1, $newinnerloop2, $newinnerloop3);
929 $calculated = GetSeq($subscription, $pattern)
930 $subscription is a hashref containing all the attributes of the table 'subscription'
931 $pattern is a hashref containing all the attributes of the table 'subscription_numberpatterns'
932 this function transforms {X},{Y},{Z} to 150,0,0 for example.
934 the sequence in string format
939 my ($subscription, $pattern) = @_;
941 return unless ($subscription and $pattern);
943 my $locale = $subscription->{locale};
945 my $calculated = $pattern->{numberingmethod};
947 my $newlastvalue1 = $subscription->{'lastvalue1'} || 0;
948 $newlastvalue1 = _numeration($newlastvalue1, $pattern->{numbering1}, $locale) if ($pattern->{numbering1}); # reset counter if needed.
949 $calculated =~ s/\{X\}/$newlastvalue1/g;
951 my $newlastvalue2 = $subscription->{'lastvalue2'} || 0;
952 $newlastvalue2 = _numeration($newlastvalue2, $pattern->{numbering2}, $locale) if ($pattern->{numbering2}); # reset counter if needed.
953 $calculated =~ s/\{Y\}/$newlastvalue2/g;
955 my $newlastvalue3 = $subscription->{'lastvalue3'} || 0;
956 $newlastvalue3 = _numeration($newlastvalue3, $pattern->{numbering3}, $locale) if ($pattern->{numbering3}); # reset counter if needed.
957 $calculated =~ s/\{Z\}/$newlastvalue3/g;
961 =head2 GetExpirationDate
963 $enddate = GetExpirationDate($subscriptionid, [$startdate])
965 this function return the next expiration date for a subscription given on input args.
972 sub GetExpirationDate {
973 my ( $subscriptionid, $startdate ) = @_;
975 return unless ($subscriptionid);
977 my $dbh = C4::Context->dbh;
978 my $subscription = GetSubscription($subscriptionid);
981 # we don't do the same test if the subscription is based on X numbers or on X weeks/months
982 $enddate = $startdate || $subscription->{startdate};
983 my @date = split( /-/, $enddate );
985 return if ( scalar(@date) != 3 || not check_date(@date) );
987 my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
988 if ( $frequency and $frequency->{unit} ) {
991 if ( my $length = $subscription->{numberlength} ) {
993 #calculate the date of the last issue.
994 for ( my $i = 1 ; $i <= $length ; $i++ ) {
995 $enddate = GetNextDate( $subscription, $enddate, $frequency );
997 } elsif ( $subscription->{monthlength} ) {
998 if ( $$subscription{startdate} ) {
999 my @enddate = Add_Delta_YM( $date[0], $date[1], $date[2], 0, $subscription->{monthlength} );
1000 $enddate = sprintf( "%04d-%02d-%02d", $enddate[0], $enddate[1], $enddate[2] );
1002 } elsif ( $subscription->{weeklength} ) {
1003 if ( $$subscription{startdate} ) {
1004 my @date = split( /-/, $subscription->{startdate} );
1005 my @enddate = Add_Delta_Days( $date[0], $date[1], $date[2], $subscription->{weeklength} * 7 );
1006 $enddate = sprintf( "%04d-%02d-%02d", $enddate[0], $enddate[1], $enddate[2] );
1009 $enddate = $subscription->{enddate};
1013 return $subscription->{enddate};
1017 =head2 CountSubscriptionFromBiblionumber
1019 $subscriptionsnumber = CountSubscriptionFromBiblionumber($biblionumber)
1020 this returns a count of the subscriptions for a given biblionumber
1022 the number of subscriptions
1026 sub CountSubscriptionFromBiblionumber {
1027 my ($biblionumber) = @_;
1029 return unless ($biblionumber);
1031 my $dbh = C4::Context->dbh;
1032 my $query = "SELECT count(*) FROM subscription WHERE biblionumber=?";
1033 my $sth = $dbh->prepare($query);
1034 $sth->execute($biblionumber);
1035 my $subscriptionsnumber = $sth->fetchrow;
1036 return $subscriptionsnumber;
1039 =head2 ModSubscriptionHistory
1041 ModSubscriptionHistory($subscriptionid,$histstartdate,$enddate,$recievedlist,$missinglist,$opacnote,$librariannote);
1043 this function modifies the history of a subscription. Put your new values on input arg.
1044 returns the number of rows affected
1048 sub ModSubscriptionHistory {
1049 my ( $subscriptionid, $histstartdate, $enddate, $receivedlist, $missinglist, $opacnote, $librariannote ) = @_;
1051 return unless ($subscriptionid);
1053 my $dbh = C4::Context->dbh;
1054 my $query = "UPDATE subscriptionhistory
1055 SET histstartdate=?,histenddate=?,recievedlist=?,missinglist=?,opacnote=?,librariannote=?
1056 WHERE subscriptionid=?
1058 my $sth = $dbh->prepare($query);
1059 $receivedlist =~ s/^; // if $receivedlist;
1060 $missinglist =~ s/^; // if $missinglist;
1061 $opacnote =~ s/^; // if $opacnote;
1062 $sth->execute( $histstartdate, $enddate, $receivedlist, $missinglist, $opacnote, $librariannote, $subscriptionid );
1066 =head2 ModSerialStatus
1068 ModSerialStatus($serialid, $serialseq, $planneddate, $publisheddate,
1069 $publisheddatetext, $status, $notes);
1071 This function modify the serial status. Serial status is a number.(eg 2 is "arrived")
1072 Note : if we change from "waited" to something else,then we will have to create a new "waited" entry
1076 sub ModSerialStatus {
1077 my ($serialid, $serialseq, $planneddate, $publisheddate, $publisheddatetext,
1078 $status, $notes) = @_;
1080 return unless ($serialid);
1082 #It is a usual serial
1083 # 1st, get previous status :
1084 my $dbh = C4::Context->dbh;
1085 my $query = "SELECT serial.subscriptionid,serial.status,subscription.periodicity,serial.routingnotes
1086 FROM serial, subscription
1087 WHERE serial.subscriptionid=subscription.subscriptionid
1089 my $sth = $dbh->prepare($query);
1090 $sth->execute($serialid);
1091 my ( $subscriptionid, $oldstatus, $periodicity, $routingnotes ) = $sth->fetchrow;
1092 my $frequency = GetSubscriptionFrequency($periodicity);
1094 # change status & update subscriptionhistory
1096 if ( $status == DELETED ) {
1097 DelIssue( { 'serialid' => $serialid, 'subscriptionid' => $subscriptionid, 'serialseq' => $serialseq } );
1101 SET serialseq = ?, publisheddate = ?, publisheddatetext = ?,
1102 planneddate = ?, status = ?, notes = ?, routingnotes = ?
1105 $sth = $dbh->prepare($query);
1106 $sth->execute( $serialseq, $publisheddate, $publisheddatetext,
1107 $planneddate, $status, $notes, $routingnotes, $serialid );
1108 $query = "SELECT * FROM subscription WHERE subscriptionid = ?";
1109 $sth = $dbh->prepare($query);
1110 $sth->execute($subscriptionid);
1111 my $val = $sth->fetchrow_hashref;
1112 unless ( $val->{manualhistory} ) {
1113 $query = "SELECT missinglist,recievedlist FROM subscriptionhistory WHERE subscriptionid=?";
1114 $sth = $dbh->prepare($query);
1115 $sth->execute($subscriptionid);
1116 my ( $missinglist, $recievedlist ) = $sth->fetchrow;
1118 if ( $status == ARRIVED || ($oldstatus == ARRIVED && $status != ARRIVED) ) {
1119 $recievedlist = _handle_seqno($serialseq, $recievedlist);
1122 # in case serial has been previously marked as missing
1123 if (grep /$status/, (EXPECTED, ARRIVED, LATE, CLAIMED)) {
1124 $missinglist = _handle_seqno($serialseq, $missinglist, 'REMOVE');
1127 $missinglist = _handle_seqno($serialseq, $missinglist) if grep { $_ == $status } MISSING_STATUSES;
1128 $missinglist .= "; not issued $serialseq" if $status == NOT_ISSUED and not _handle_seqno($serialseq, $missinglist, 'CHECK');
1130 $query = "UPDATE subscriptionhistory SET recievedlist=?, missinglist=? WHERE subscriptionid=?";
1131 $sth = $dbh->prepare($query);
1132 $recievedlist =~ s/^; //;
1133 $missinglist =~ s/^; //;
1134 $sth->execute( $recievedlist, $missinglist, $subscriptionid );
1138 # create new expected entry if needed (ie : was "expected" and has changed)
1139 my $otherIssueExpected = scalar findSerialsByStatus(EXPECTED, $subscriptionid);
1140 if ( !$otherIssueExpected && $oldstatus == EXPECTED && $status != EXPECTED ) {
1141 my $subscription = GetSubscription($subscriptionid);
1142 my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
1143 my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
1147 $newserialseq, $newlastvalue1, $newlastvalue2, $newlastvalue3,
1148 $newinnerloop1, $newinnerloop2, $newinnerloop3
1150 = GetNextSeq( $subscription, $pattern, $frequency, $publisheddate );
1152 # next date (calculated from actual date & frequency parameters)
1153 my $nextpublisheddate = GetNextDate($subscription, $publisheddate, $frequency, 1);
1154 my $nextpubdate = $nextpublisheddate;
1155 $query = "UPDATE subscription SET lastvalue1=?, lastvalue2=?, lastvalue3=?, innerloop1=?, innerloop2=?, innerloop3=?
1156 WHERE subscriptionid = ?";
1157 $sth = $dbh->prepare($query);
1158 $sth->execute( $newlastvalue1, $newlastvalue2, $newlastvalue3, $newinnerloop1, $newinnerloop2, $newinnerloop3, $subscriptionid );
1159 my $newnote = C4::Context->preference('PreserveSerialNotes') ? $notes : "";
1160 NewIssue( $newserialseq, $subscriptionid, $subscription->{'biblionumber'}, 1, $nextpubdate, $nextpubdate, undef, $newnote, $routingnotes );
1161 # check if an alert must be sent... (= a letter is defined & status became "arrived"
1162 if ( $subscription->{letter} && $status == ARRIVED && $oldstatus != ARRIVED ) {
1163 require C4::Letters;
1164 C4::Letters::SendAlerts( 'issue', $serialid, $subscription->{letter} );
1172 # Adds or removes seqno from list when needed; returns list
1173 # Or checks and returns true when present
1175 my ( $seq, $list, $op ) = @_; # op = ADD | REMOVE | CHECK (default: ADD)
1177 $seq_r =~ s/([()])/\\$1/g; # Adjust disturbing parentheses for regex, maybe extend in future
1179 if( !$op or $op eq 'ADD' ) {
1180 $list .= "; $seq" if $list !~ /(^|;)\s*$seq_r(?=;|$)/;
1181 } elsif( $op eq 'REMOVE' ) {
1182 $list=~ s/(^|;)\s*(not issued )?$seq_r(?=;|$)//g;
1184 return $list =~ /(^|;)\s*$seq_r(?=;|$)/ ? 1 : q{};
1189 =head2 GetNextExpected
1191 $nextexpected = GetNextExpected($subscriptionid)
1193 Get the planneddate for the current expected issue of the subscription.
1199 planneddate => ISO date
1204 sub GetNextExpected {
1205 my ($subscriptionid) = @_;
1207 my $dbh = C4::Context->dbh;
1211 WHERE subscriptionid = ?
1215 my $sth = $dbh->prepare($query);
1217 # Each subscription has only one 'expected' issue.
1218 $sth->execute( $subscriptionid, EXPECTED );
1219 my $nextissue = $sth->fetchrow_hashref;
1220 if ( !$nextissue ) {
1224 WHERE subscriptionid = ?
1225 ORDER BY publisheddate DESC
1228 $sth = $dbh->prepare($query);
1229 $sth->execute($subscriptionid);
1230 $nextissue = $sth->fetchrow_hashref;
1232 foreach(qw/planneddate publisheddate/) {
1233 # or should this default to 1st Jan ???
1234 $nextissue->{$_} //= strftime( '%Y-%m-%d', localtime );
1240 =head2 ModNextExpected
1242 ModNextExpected($subscriptionid,$date)
1244 Update the planneddate for the current expected issue of the subscription.
1245 This will modify all future prediction results.
1247 C<$date> is an ISO date.
1253 sub ModNextExpected {
1254 my ( $subscriptionid, $date ) = @_;
1255 my $dbh = C4::Context->dbh;
1257 #FIXME: Would expect to only set planneddate, but we set both on new issue creation, so updating it here
1258 my $sth = $dbh->prepare('UPDATE serial SET planneddate=?,publisheddate=? WHERE subscriptionid=? AND status=?');
1260 # Each subscription has only one 'expected' issue.
1261 $sth->execute( $date, $date, $subscriptionid, EXPECTED );
1266 =head2 GetSubscriptionIrregularities
1270 =item @irreg = &GetSubscriptionIrregularities($subscriptionid);
1271 get the list of irregularities for a subscription
1277 sub GetSubscriptionIrregularities {
1278 my $subscriptionid = shift;
1280 return unless $subscriptionid;
1282 my $dbh = C4::Context->dbh;
1286 WHERE subscriptionid = ?
1288 my $sth = $dbh->prepare($query);
1289 $sth->execute($subscriptionid);
1291 my ($result) = $sth->fetchrow_array;
1292 my @irreg = split /;/, $result;
1297 =head2 ModSubscription
1299 this function modifies a subscription. Put all new values on input args.
1300 returns the number of rows affected
1304 sub ModSubscription {
1306 $auser, $branchcode, $aqbooksellerid, $cost, $aqbudgetid, $startdate,
1307 $periodicity, $firstacquidate, $irregularity, $numberpattern, $locale,
1308 $numberlength, $weeklength, $monthlength, $lastvalue1, $innerloop1,
1309 $lastvalue2, $innerloop2, $lastvalue3, $innerloop3, $status,
1310 $biblionumber, $callnumber, $notes, $letter, $manualhistory,
1311 $internalnotes, $serialsadditems, $staffdisplaycount, $opacdisplaycount,
1312 $graceperiod, $location, $enddate, $subscriptionid, $skip_serialseq,
1313 $itemtype, $previousitemtype, $mana_id
1316 my $subscription = Koha::Subscriptions->find($subscriptionid);
1319 librarian => $auser,
1320 branchcode => $branchcode,
1321 aqbooksellerid => $aqbooksellerid,
1323 aqbudgetid => $aqbudgetid,
1324 biblionumber => $biblionumber,
1325 startdate => $startdate,
1326 periodicity => $periodicity,
1327 numberlength => $numberlength,
1328 weeklength => $weeklength,
1329 monthlength => $monthlength,
1330 lastvalue1 => $lastvalue1,
1331 innerloop1 => $innerloop1,
1332 lastvalue2 => $lastvalue2,
1333 innerloop2 => $innerloop2,
1334 lastvalue3 => $lastvalue3,
1335 innerloop3 => $innerloop3,
1339 firstacquidate => $firstacquidate,
1340 irregularity => $irregularity,
1341 numberpattern => $numberpattern,
1343 callnumber => $callnumber,
1344 manualhistory => $manualhistory,
1345 internalnotes => $internalnotes,
1346 serialsadditems => $serialsadditems,
1347 staffdisplaycount => $staffdisplaycount,
1348 opacdisplaycount => $opacdisplaycount,
1349 graceperiod => $graceperiod,
1350 location => $location,
1351 enddate => $enddate,
1352 skip_serialseq => $skip_serialseq,
1353 itemtype => $itemtype,
1354 previousitemtype => $previousitemtype,
1355 mana_id => $mana_id,
1358 # FIXME Must be $subscription->serials
1359 # FIXME We shouldn't need serial.subscription (instead use serial->subscription->biblionumber)
1360 Koha::Serials->search({ subscriptionid => $subscriptionid })->update({ biblionumber => $biblionumber });
1362 logaction( "SERIAL", "MODIFY", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1364 $subscription->discard_changes;
1365 return $subscription;
1368 =head2 NewSubscription
1370 $subscriptionid = &NewSubscription($auser,branchcode,$aqbooksellerid,$cost,$aqbudgetid,$biblionumber,
1371 $startdate,$periodicity,$numberlength,$weeklength,$monthlength,
1372 $lastvalue1,$innerloop1,$lastvalue2,$innerloop2,$lastvalue3,$innerloop3,
1373 $status, $notes, $letter, $firstacquidate, $irregularity, $numberpattern,
1374 $locale, $callnumber, $manualhistory, $internalnotes, $serialsadditems,
1375 $staffdisplaycount, $opacdisplaycount, $graceperiod, $location, $enddate,
1376 $skip_serialseq, $itemtype, $previousitemtype);
1378 Create a new subscription with value given on input args.
1381 the id of this new subscription
1385 sub NewSubscription {
1387 $auser, $branchcode, $aqbooksellerid, $cost, $aqbudgetid, $biblionumber,
1388 $startdate, $periodicity, $numberlength, $weeklength, $monthlength,
1389 $lastvalue1, $innerloop1, $lastvalue2, $innerloop2, $lastvalue3,
1390 $innerloop3, $status, $notes, $letter, $firstacquidate, $irregularity,
1391 $numberpattern, $locale, $callnumber, $manualhistory, $internalnotes,
1392 $serialsadditems, $staffdisplaycount, $opacdisplaycount, $graceperiod,
1393 $location, $enddate, $skip_serialseq, $itemtype, $previousitemtype, $mana_id
1395 my $dbh = C4::Context->dbh;
1397 my $subscription = Koha::Subscription->new(
1399 librarian => $auser,
1400 branchcode => $branchcode,
1401 aqbooksellerid => $aqbooksellerid,
1403 aqbudgetid => $aqbudgetid,
1404 biblionumber => $biblionumber,
1405 startdate => $startdate,
1406 periodicity => $periodicity,
1407 numberlength => $numberlength,
1408 weeklength => $weeklength,
1409 monthlength => $monthlength,
1410 lastvalue1 => $lastvalue1,
1411 innerloop1 => $innerloop1,
1412 lastvalue2 => $lastvalue2,
1413 innerloop2 => $innerloop2,
1414 lastvalue3 => $lastvalue3,
1415 innerloop3 => $innerloop3,
1419 firstacquidate => $firstacquidate,
1420 irregularity => $irregularity,
1421 numberpattern => $numberpattern,
1423 callnumber => $callnumber,
1424 manualhistory => $manualhistory,
1425 internalnotes => $internalnotes,
1426 serialsadditems => $serialsadditems,
1427 staffdisplaycount => $staffdisplaycount,
1428 opacdisplaycount => $opacdisplaycount,
1429 graceperiod => $graceperiod,
1430 location => $location,
1431 enddate => $enddate,
1432 skip_serialseq => $skip_serialseq,
1433 itemtype => $itemtype,
1434 previousitemtype => $previousitemtype,
1435 mana_id => $mana_id,
1438 $subscription->discard_changes;
1439 my $subscriptionid = $subscription->subscriptionid;
1440 my ( $query, $sth );
1442 $enddate = GetExpirationDate( $subscriptionid, $startdate );
1446 WHERE subscriptionid=?
1448 $sth = $dbh->prepare($query);
1449 $sth->execute( $enddate, $subscriptionid );
1452 # then create the 1st expected number
1454 INSERT INTO subscriptionhistory
1455 (biblionumber, subscriptionid, histstartdate, missinglist, recievedlist)
1456 VALUES (?,?,?, '', '')
1458 $sth = $dbh->prepare($query);
1459 $sth->execute( $biblionumber, $subscriptionid, $startdate);
1461 # reread subscription to get a hash (for calculation of the 1st issue number)
1462 $subscription = GetSubscription($subscriptionid); # We should not do that
1463 my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
1465 # calculate issue number
1466 my $serialseq = GetSeq($subscription, $pattern) || q{};
1470 serialseq => $serialseq,
1471 serialseq_x => $subscription->{'lastvalue1'},
1472 serialseq_y => $subscription->{'lastvalue2'},
1473 serialseq_z => $subscription->{'lastvalue3'},
1474 subscriptionid => $subscriptionid,
1475 biblionumber => $biblionumber,
1477 planneddate => $firstacquidate,
1478 publisheddate => $firstacquidate,
1482 logaction( "SERIAL", "ADD", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1484 #set serial flag on biblio if not already set.
1485 my $biblio = Koha::Biblios->find( $biblionumber );
1486 if ( $biblio and !$biblio->serial ) {
1487 my $record = GetMarcBiblio({ biblionumber => $biblionumber });
1488 my ( $tag, $subf ) = GetMarcFromKohaField( 'biblio.serial' );
1490 eval { $record->field($tag)->update( $subf => 1 ); };
1492 ModBiblio( $record, $biblionumber, $biblio->frameworkcode );
1494 return $subscriptionid;
1497 =head2 GetSubscriptionLength
1499 my ($numberlength, $weeklength, $monthlength) = GetSubscriptionLength( $subtype, $sublength );
1501 This function calculates the subscription length.
1505 sub GetSubscriptionLength {
1506 my ($subtype, $length) = @_;
1508 return unless looks_like_number($length);
1512 $subtype eq 'issues' ? $length : 0,
1513 $subtype eq 'weeks' ? $length : 0,
1514 $subtype eq 'months' ? $length : 0,
1519 =head2 ReNewSubscription
1521 ReNewSubscription($params);
1523 $params is a hashref with the following keys: subscriptionid, user, startdate, numberlength, weeklength, monthlength, note, branchcode
1525 this function renew a subscription with values given on input args.
1529 sub ReNewSubscription {
1530 my ( $params ) = @_;
1531 my $subscriptionid = $params->{subscriptionid};
1532 my $user = $params->{user};
1533 my $startdate = $params->{startdate};
1534 my $numberlength = $params->{numberlength};
1535 my $weeklength = $params->{weeklength};
1536 my $monthlength = $params->{monthlength};
1537 my $note = $params->{note};
1538 my $branchcode = $params->{branchcode};
1540 my $dbh = C4::Context->dbh;
1541 my $subscription = GetSubscription($subscriptionid);
1545 LEFT JOIN biblioitems ON biblio.biblionumber=biblioitems.biblionumber
1546 WHERE biblio.biblionumber=?
1548 my $sth = $dbh->prepare($query);
1549 $sth->execute( $subscription->{biblionumber} );
1550 my $biblio = $sth->fetchrow_hashref;
1552 if ( C4::Context->preference("RenewSerialAddsSuggestion") ) {
1553 require C4::Suggestions;
1554 C4::Suggestions::NewSuggestion(
1555 { 'suggestedby' => $user,
1556 'title' => $subscription->{bibliotitle},
1557 'author' => $biblio->{author},
1558 'publishercode' => $biblio->{publishercode},
1560 'biblionumber' => $subscription->{biblionumber},
1561 'branchcode' => $branchcode,
1566 $numberlength ||= 0; # Should not we raise an exception instead?
1569 # renew subscription
1572 SET startdate=?,numberlength=?,weeklength=?,monthlength=?,reneweddate=NOW()
1573 WHERE subscriptionid=?
1575 $sth = $dbh->prepare($query);
1576 $sth->execute( $startdate, $numberlength, $weeklength, $monthlength, $subscriptionid );
1577 my $enddate = GetExpirationDate($subscriptionid);
1578 $debug && warn "enddate :$enddate";
1582 WHERE subscriptionid=?
1584 $sth = $dbh->prepare($query);
1585 $sth->execute( $enddate, $subscriptionid );
1587 logaction( "SERIAL", "RENEW", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1593 NewIssue($serialseq,$subscriptionid,$biblionumber,$status, $planneddate, $publisheddate, $notes, $routingnotes)
1595 Create a new issue stored on the database.
1596 Note : we have to update the recievedlist and missinglist on subscriptionhistory for this subscription.
1597 returns the serial id
1602 my ( $serialseq, $subscriptionid, $biblionumber, $status, $planneddate,
1603 $publisheddate, $publisheddatetext, $notes, $routingnotes ) = @_;
1604 ### FIXME biblionumber CAN be provided by subscriptionid. So Do we STILL NEED IT ?
1606 return unless ($subscriptionid);
1608 my $schema = Koha::Database->new()->schema();
1610 my $subscription = Koha::Subscriptions->find( $subscriptionid );
1612 my $serial = Koha::Serial->new(
1614 serialseq => $serialseq,
1615 serialseq_x => $subscription->lastvalue1(),
1616 serialseq_y => $subscription->lastvalue2(),
1617 serialseq_z => $subscription->lastvalue3(),
1618 subscriptionid => $subscriptionid,
1619 biblionumber => $biblionumber,
1621 planneddate => $planneddate,
1622 publisheddate => $publisheddate,
1623 publisheddatetext => $publisheddatetext,
1625 routingnotes => $routingnotes
1629 my $serialid = $serial->id();
1631 my $subscription_history = Koha::Subscription::Histories->find($subscriptionid);
1632 my $missinglist = $subscription_history->missinglist();
1633 my $recievedlist = $subscription_history->recievedlist();
1635 if ( $status == ARRIVED ) {
1636 ### TODO Add a feature that improves recognition and description.
1637 ### As such count (serialseq) i.e. : N18,2(N19),N20
1638 ### Would use substr and index But be careful to previous presence of ()
1639 $recievedlist .= "; $serialseq" unless ( index( $recievedlist, $serialseq ) > 0 );
1641 if ( grep { $_ eq $status } (MISSING_STATUSES) ) {
1642 $missinglist .= "; $serialseq" unless ( index( $missinglist, $serialseq ) > 0 );
1645 $recievedlist =~ s/^; //;
1646 $missinglist =~ s/^; //;
1648 $subscription_history->recievedlist($recievedlist);
1649 $subscription_history->missinglist($missinglist);
1650 $subscription_history->store();
1655 =head2 HasSubscriptionStrictlyExpired
1657 1 or 0 = HasSubscriptionStrictlyExpired($subscriptionid)
1659 the subscription has stricly expired when today > the end subscription date
1662 1 if true, 0 if false, -1 if the expiration date is not set.
1666 sub HasSubscriptionStrictlyExpired {
1668 # Getting end of subscription date
1669 my ($subscriptionid) = @_;
1671 return unless ($subscriptionid);
1673 my $dbh = C4::Context->dbh;
1674 my $subscription = GetSubscription($subscriptionid);
1675 my $expirationdate = $subscription->{enddate} || GetExpirationDate($subscriptionid);
1677 # If the expiration date is set
1678 if ( $expirationdate != 0 ) {
1679 my ( $endyear, $endmonth, $endday ) = split( '-', $expirationdate );
1681 # Getting today's date
1682 my ( $nowyear, $nowmonth, $nowday ) = Today();
1684 # if today's date > expiration date, then the subscription has stricly expired
1685 if ( Delta_Days( $nowyear, $nowmonth, $nowday, $endyear, $endmonth, $endday ) < 0 ) {
1692 # There are some cases where the expiration date is not set
1693 # As we can't determine if the subscription has expired on a date-basis,
1699 =head2 HasSubscriptionExpired
1701 $has_expired = HasSubscriptionExpired($subscriptionid)
1703 the subscription has expired when the next issue to arrive is out of subscription limit.
1706 0 if the subscription has not expired
1707 1 if the subscription has expired
1708 2 if has subscription does not have a valid expiration date set
1712 sub HasSubscriptionExpired {
1713 my ($subscriptionid) = @_;
1715 return unless ($subscriptionid);
1717 my $dbh = C4::Context->dbh;
1718 my $subscription = GetSubscription($subscriptionid);
1719 my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
1720 if ( $frequency and $frequency->{unit} ) {
1721 my $expirationdate = $subscription->{enddate} || GetExpirationDate($subscriptionid);
1722 if (!defined $expirationdate) {
1723 $expirationdate = q{};
1726 SELECT max(planneddate)
1728 WHERE subscriptionid=?
1730 my $sth = $dbh->prepare($query);
1731 $sth->execute($subscriptionid);
1732 my ($res) = $sth->fetchrow;
1733 if (!$res || $res=~m/^0000/) {
1736 my @res = split( /-/, $res );
1737 my @endofsubscriptiondate = split( /-/, $expirationdate );
1738 return 2 if ( scalar(@res) != 3 || scalar(@endofsubscriptiondate) != 3 || not check_date(@res) || not check_date(@endofsubscriptiondate) );
1740 if ( ( @endofsubscriptiondate && Delta_Days( $res[0], $res[1], $res[2], $endofsubscriptiondate[0], $endofsubscriptiondate[1], $endofsubscriptiondate[2] ) <= 0 )
1745 if ( $subscription->{'numberlength'} ) {
1746 my $countreceived = countissuesfrom( $subscriptionid, $subscription->{'startdate'} );
1747 return 1 if ( $countreceived > $subscription->{'numberlength'} );
1753 return 0; # Notice that you'll never get here.
1756 =head2 DelSubscription
1758 DelSubscription($subscriptionid)
1759 this function deletes subscription which has $subscriptionid as id.
1763 sub DelSubscription {
1764 my ($subscriptionid) = @_;
1765 my $dbh = C4::Context->dbh;
1766 $dbh->do("DELETE FROM subscription WHERE subscriptionid=?", undef, $subscriptionid);
1768 Koha::AdditionalFieldValues->search({
1769 'field.tablename' => 'subscription',
1770 'me.record_id' => $subscriptionid,
1771 }, { join => 'field' })->delete;
1773 logaction( "SERIAL", "DELETE", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1778 DelIssue($serialseq,$subscriptionid)
1779 this function deletes an issue which has $serialseq and $subscriptionid given on input arg.
1781 returns the number of rows affected
1786 my ($dataissue) = @_;
1787 my $dbh = C4::Context->dbh;
1788 ### TODO Add itemdeletion. Would need to get itemnumbers. Should be in a pref ?
1793 AND subscriptionid= ?
1795 my $mainsth = $dbh->prepare($query);
1796 $mainsth->execute( $dataissue->{'serialid'}, $dataissue->{'subscriptionid'} );
1798 #Delete element from subscription history
1799 $query = "SELECT * FROM subscription WHERE subscriptionid = ?";
1800 my $sth = $dbh->prepare($query);
1801 $sth->execute( $dataissue->{'subscriptionid'} );
1802 my $val = $sth->fetchrow_hashref;
1803 unless ( $val->{manualhistory} ) {
1805 SELECT * FROM subscriptionhistory
1806 WHERE subscriptionid= ?
1808 my $sth = $dbh->prepare($query);
1809 $sth->execute( $dataissue->{'subscriptionid'} );
1810 my $data = $sth->fetchrow_hashref;
1811 my $serialseq = $dataissue->{'serialseq'};
1812 $data->{'missinglist'} =~ s/\b$serialseq\b//;
1813 $data->{'recievedlist'} =~ s/\b$serialseq\b//;
1814 my $strsth = "UPDATE subscriptionhistory SET " . join( ",", map { join( "=", $_, $dbh->quote( $data->{$_} ) ) } keys %$data ) . " WHERE subscriptionid=?";
1815 $sth = $dbh->prepare($strsth);
1816 $sth->execute( $dataissue->{'subscriptionid'} );
1819 return $mainsth->rows;
1822 =head2 GetLateOrMissingIssues
1824 @issuelist = GetLateMissingIssues($supplierid,$serialid)
1826 this function selects missing issues on database - where serial.status = MISSING* or serial.status = LATE or planneddate<now
1829 the issuelist as an array of hash refs. Each element of this array contains
1830 name,title,planneddate,serialseq,serial.subscriptionid from tables : subscription, serial & biblio
1834 sub GetLateOrMissingIssues {
1835 my ( $supplierid, $serialid, $order ) = @_;
1837 return unless ( $supplierid or $serialid );
1839 my $dbh = C4::Context->dbh;
1844 $byserial = "and serialid = " . $serialid;
1847 $order .= ", title";
1851 my $missing_statuses_string = join ',', (MISSING_STATUSES);
1853 $sth = $dbh->prepare(
1855 serialid, aqbooksellerid, name,
1856 biblio.title, biblioitems.issn, planneddate, serialseq,
1857 serial.status, serial.subscriptionid, claimdate, claims_count,
1858 subscription.branchcode
1860 LEFT JOIN subscription ON serial.subscriptionid=subscription.subscriptionid
1861 LEFT JOIN biblio ON subscription.biblionumber=biblio.biblionumber
1862 LEFT JOIN biblioitems ON subscription.biblionumber=biblioitems.biblionumber
1863 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
1864 WHERE subscription.subscriptionid = serial.subscriptionid
1865 AND (serial.STATUS IN ($missing_statuses_string) OR ((planneddate < now() AND serial.STATUS = ?) OR serial.STATUS = ? OR serial.STATUS = ?))
1866 AND subscription.aqbooksellerid=$supplierid
1871 $sth = $dbh->prepare(
1873 serialid, aqbooksellerid, name,
1874 biblio.title, planneddate, serialseq,
1875 serial.status, serial.subscriptionid, claimdate, claims_count,
1876 subscription.branchcode
1878 LEFT JOIN subscription ON serial.subscriptionid=subscription.subscriptionid
1879 LEFT JOIN biblio ON subscription.biblionumber=biblio.biblionumber
1880 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
1881 WHERE subscription.subscriptionid = serial.subscriptionid
1882 AND (serial.STATUS IN ($missing_statuses_string) OR ((planneddate < now() AND serial.STATUS = ?) OR serial.STATUS = ? OR serial.STATUS = ?))
1887 $sth->execute( EXPECTED, LATE, CLAIMED );
1889 while ( my $line = $sth->fetchrow_hashref ) {
1891 if ($line->{planneddate} && $line->{planneddate} !~/^0+\-/) {
1892 $line->{planneddateISO} = $line->{planneddate};
1893 $line->{planneddate} = output_pref( { dt => dt_from_string( $line->{"planneddate"} ), dateonly => 1 } );
1895 if ($line->{claimdate} && $line->{claimdate} !~/^0+\-/) {
1896 $line->{claimdateISO} = $line->{claimdate};
1897 $line->{claimdate} = output_pref( { dt => dt_from_string( $line->{"claimdate"} ), dateonly => 1 } );
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 ];
1926 my $dbh = C4::Context->dbh;
1929 SET claimdate = NOW(),
1930 claims_count = claims_count + 1,
1932 WHERE serialid in (| . join( q|,|, (q|?|) x @$serialids ) . q|)|,
1933 {}, CLAIMED, @$serialids );
1936 =head2 check_routing
1938 $result = &check_routing($subscriptionid)
1940 this function checks to see if a serial has a routing list and returns the count of routingid
1941 used to show either an 'add' or 'edit' link
1946 my ($subscriptionid) = @_;
1948 return unless ($subscriptionid);
1950 my $dbh = C4::Context->dbh;
1951 my $sth = $dbh->prepare(
1952 "SELECT count(routingid) routingids FROM subscription LEFT JOIN subscriptionroutinglist
1953 ON subscription.subscriptionid = subscriptionroutinglist.subscriptionid
1954 WHERE subscription.subscriptionid = ? GROUP BY routingid ORDER BY ranking ASC
1957 $sth->execute($subscriptionid);
1958 my $line = $sth->fetchrow_hashref;
1959 my $result = $line->{'routingids'};
1963 =head2 addroutingmember
1965 addroutingmember($borrowernumber,$subscriptionid)
1967 this function takes a borrowernumber and subscriptionid and adds the member to the
1968 routing list for that serial subscription and gives them a rank on the list
1969 of either 1 or highest current rank + 1
1973 sub addroutingmember {
1974 my ( $borrowernumber, $subscriptionid ) = @_;
1976 return unless ($borrowernumber and $subscriptionid);
1979 my $dbh = C4::Context->dbh;
1980 my $sth = $dbh->prepare( "SELECT max(ranking) rank FROM subscriptionroutinglist WHERE subscriptionid = ?" );
1981 $sth->execute($subscriptionid);
1982 while ( my $line = $sth->fetchrow_hashref ) {
1983 if ( $line->{'rank'} > 0 ) {
1984 $rank = $line->{'rank'} + 1;
1989 $sth = $dbh->prepare( "INSERT INTO subscriptionroutinglist (subscriptionid,borrowernumber,ranking) VALUES (?,?,?)" );
1990 $sth->execute( $subscriptionid, $borrowernumber, $rank );
1993 =head2 reorder_members
1995 reorder_members($subscriptionid,$routingid,$rank)
1997 this function is used to reorder the routing list
1999 it takes the routingid of the member one wants to re-rank and the rank it is to move to
2000 - it gets all members on list puts their routingid's into an array
2001 - removes the one in the array that is $routingid
2002 - then reinjects $routingid at point indicated by $rank
2003 - then update the database with the routingids in the new order
2007 sub reorder_members {
2008 my ( $subscriptionid, $routingid, $rank ) = @_;
2009 my $dbh = C4::Context->dbh;
2010 my $sth = $dbh->prepare( "SELECT * FROM subscriptionroutinglist WHERE subscriptionid = ? ORDER BY ranking ASC" );
2011 $sth->execute($subscriptionid);
2013 while ( my $line = $sth->fetchrow_hashref ) {
2014 push( @result, $line->{'routingid'} );
2017 # To find the matching index
2019 my $key = -1; # to allow for 0 being a valid response
2020 for ( $i = 0 ; $i < @result ; $i++ ) {
2021 if ( $routingid == $result[$i] ) {
2022 $key = $i; # save the index
2027 # if index exists in array then move it to new position
2028 if ( $key > -1 && $rank > 0 ) {
2029 my $new_rank = $rank - 1; # $new_rank is what you want the new index to be in the array
2030 my $moving_item = splice( @result, $key, 1 );
2031 splice( @result, $new_rank, 0, $moving_item );
2033 for ( my $j = 0 ; $j < @result ; $j++ ) {
2034 my $sth = $dbh->prepare( "UPDATE subscriptionroutinglist SET ranking = '" . ( $j + 1 ) . "' WHERE routingid = '" . $result[$j] . "'" );
2040 =head2 delroutingmember
2042 delroutingmember($routingid,$subscriptionid)
2044 this function either deletes one member from routing list if $routingid exists otherwise
2045 deletes all members from the routing list
2049 sub delroutingmember {
2051 # if $routingid exists then deletes that row otherwise deletes all with $subscriptionid
2052 my ( $routingid, $subscriptionid ) = @_;
2053 my $dbh = C4::Context->dbh;
2055 my $sth = $dbh->prepare("DELETE FROM subscriptionroutinglist WHERE routingid = ?");
2056 $sth->execute($routingid);
2057 reorder_members( $subscriptionid, $routingid );
2059 my $sth = $dbh->prepare("DELETE FROM subscriptionroutinglist WHERE subscriptionid = ?");
2060 $sth->execute($subscriptionid);
2065 =head2 getroutinglist
2067 @routinglist = getroutinglist($subscriptionid)
2069 this gets the info from the subscriptionroutinglist for $subscriptionid
2072 the routinglist as an array. Each element of the array contains a hash_ref containing
2073 routingid - a unique id, borrowernumber, ranking, and biblionumber of subscription
2077 sub getroutinglist {
2078 my ($subscriptionid) = @_;
2079 my $dbh = C4::Context->dbh;
2080 my $sth = $dbh->prepare(
2081 'SELECT routingid, borrowernumber, ranking, biblionumber
2083 JOIN subscriptionroutinglist ON subscription.subscriptionid = subscriptionroutinglist.subscriptionid
2084 WHERE subscription.subscriptionid = ? ORDER BY ranking ASC'
2086 $sth->execute($subscriptionid);
2087 my $routinglist = $sth->fetchall_arrayref({});
2088 return @{$routinglist};
2091 =head2 countissuesfrom
2093 $result = countissuesfrom($subscriptionid,$startdate)
2095 Returns a count of serial rows matching the given subsctiptionid
2096 with published date greater than startdate
2100 sub countissuesfrom {
2101 my ( $subscriptionid, $startdate ) = @_;
2102 my $dbh = C4::Context->dbh;
2106 WHERE subscriptionid=?
2107 AND serial.publisheddate>?
2109 my $sth = $dbh->prepare($query);
2110 $sth->execute( $subscriptionid, $startdate );
2111 my ($countreceived) = $sth->fetchrow;
2112 return $countreceived;
2117 $result = CountIssues($subscriptionid)
2119 Returns a count of serial rows matching the given subsctiptionid
2124 my ($subscriptionid) = @_;
2125 my $dbh = C4::Context->dbh;
2129 WHERE subscriptionid=?
2131 my $sth = $dbh->prepare($query);
2132 $sth->execute($subscriptionid);
2133 my ($countreceived) = $sth->fetchrow;
2134 return $countreceived;
2139 $result = HasItems($subscriptionid)
2141 returns a count of items from serial matching the subscriptionid
2146 my ($subscriptionid) = @_;
2147 my $dbh = C4::Context->dbh;
2149 SELECT COUNT(serialitems.itemnumber)
2151 LEFT JOIN serialitems USING(serialid)
2152 WHERE subscriptionid=? AND serialitems.serialid IS NOT NULL
2154 my $sth=$dbh->prepare($query);
2155 $sth->execute($subscriptionid);
2156 my ($countitems)=$sth->fetchrow_array();
2160 =head2 abouttoexpire
2162 $result = abouttoexpire($subscriptionid)
2164 this function alerts you to the penultimate issue for a serial subscription
2166 returns 1 - if this is the penultimate issue
2172 my ($subscriptionid) = @_;
2173 my $dbh = C4::Context->dbh;
2174 my $subscription = GetSubscription($subscriptionid);
2175 my $per = $subscription->{'periodicity'};
2176 my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($per);
2177 if ($frequency and $frequency->{unit}){
2179 my $expirationdate = GetExpirationDate($subscriptionid);
2181 my ($res) = $dbh->selectrow_array('select max(planneddate) from serial where subscriptionid = ?', undef, $subscriptionid);
2182 my $nextdate = GetNextDate($subscription, $res, $frequency);
2184 # only compare dates if both dates exist.
2185 if ($nextdate and $expirationdate) {
2186 if(Date::Calc::Delta_Days(
2187 split( /-/, $nextdate ),
2188 split( /-/, $expirationdate )
2194 } elsif ( $subscription->{numberlength} && $subscription->{numberlength}>0) {
2195 return (countissuesfrom($subscriptionid,$subscription->{'startdate'}) >=$subscription->{numberlength}-1);
2201 =head2 GetFictiveIssueNumber
2203 $issueno = GetFictiveIssueNumber($subscription, $publishedate, $frequency);
2205 Get the position of the issue published at $publisheddate, considering the
2206 first issue (at firstacquidate) is at position 1, the next is at position 2, etc...
2207 This issuenumber doesn't take into account irregularities, so, for instance, if the 3rd
2208 issue is declared as 'irregular' (will be skipped at receipt), the next issue number
2209 will be 4, not 3. It's why it is called 'fictive'. It is NOT a serial seq, and is not
2210 depending on how many rows are in serial table.
2211 The issue number calculation is based on subscription frequency, first acquisition
2212 date, and $publisheddate.
2214 Returns undef when called for irregular frequencies.
2216 The routine is used to skip irregularities when calculating the next issue
2217 date (in GetNextDate) or the next issue number (in GetNextSeq).
2221 sub GetFictiveIssueNumber {
2222 my ($subscription, $publisheddate, $frequency) = @_;
2224 my $unit = $frequency->{unit} ? lc $frequency->{'unit'} : undef;
2228 my ( $year, $month, $day ) = split /-/, $publisheddate;
2229 my ( $fa_year, $fa_month, $fa_day ) = split /-/, $subscription->{'firstacquidate'};
2230 my $delta = _delta_units( [$fa_year, $fa_month, $fa_day], [$year, $month, $day], $unit );
2232 if( $frequency->{'unitsperissue'} == 1 ) {
2233 $issueno = $delta * $frequency->{'issuesperunit'} + $subscription->{'countissuesperunit'};
2234 } else { # issuesperunit == 1
2235 $issueno = 1 + int( $delta / $frequency->{'unitsperissue'} );
2241 my ( $date1, $date2, $unit ) = @_;
2242 # date1 and date2 are array refs in the form [ yy, mm, dd ]
2244 if( $unit eq 'day' ) {
2245 return Delta_Days( @$date1, @$date2 );
2246 } elsif( $unit eq 'week' ) {
2247 return int( Delta_Days( @$date1, @$date2 ) / 7 );
2250 # In case of months or years, this is a wrapper around N_Delta_YMD.
2251 # Note that N_Delta_YMD returns 29 days between e.g. 22-2-72 and 22-3-72
2252 # while we expect 1 month.
2253 my @delta = N_Delta_YMD( @$date1, @$date2 );
2254 if( $delta[2] > 27 ) {
2255 # Check if we could add a month
2256 my @jump = Add_Delta_YM( @$date1, $delta[0], 1 + $delta[1] );
2257 if( Delta_Days( @jump, @$date2 ) >= 0 ) {
2261 if( $delta[1] >= 12 ) {
2265 # if unit is year, we only return full years
2266 return $unit eq 'month' ? $delta[0] * 12 + $delta[1] : $delta[0];
2269 sub _get_next_date_day {
2270 my ($subscription, $freqdata, $year, $month, $day) = @_;
2272 my @newissue; # ( yy, mm, dd )
2273 # We do not need $delta_days here, since it would be zero where used
2275 if( $freqdata->{issuesperunit} == 1 ) {
2277 @newissue = Add_Delta_Days(
2278 $year, $month, $day, $freqdata->{"unitsperissue"} );
2279 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2281 @newissue = ( $year, $month, $day );
2282 $subscription->{countissuesperunit}++;
2284 # We finished a cycle of issues within a unit.
2285 # No subtraction of zero needed, just add one day
2286 @newissue = Add_Delta_Days( $year, $month, $day, 1 );
2287 $subscription->{countissuesperunit} = 1;
2292 sub _get_next_date_week {
2293 my ($subscription, $freqdata, $year, $month, $day) = @_;
2295 my @newissue; # ( yy, mm, dd )
2296 my $delta_days = int( 7 / $freqdata->{issuesperunit} );
2298 if( $freqdata->{issuesperunit} == 1 ) {
2299 # Add full weeks (of 7 days)
2300 @newissue = Add_Delta_Days(
2301 $year, $month, $day, 7 * $freqdata->{"unitsperissue"} );
2302 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2303 # Add rounded number of days based on frequency.
2304 @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
2305 $subscription->{countissuesperunit}++;
2307 # We finished a cycle of issues within a unit.
2308 # Subtract delta * (issues - 1), add 1 week
2309 @newissue = Add_Delta_Days( $year, $month, $day,
2310 -$delta_days * ($freqdata->{issuesperunit} - 1) );
2311 @newissue = Add_Delta_Days( @newissue, 7 );
2312 $subscription->{countissuesperunit} = 1;
2317 sub _get_next_date_month {
2318 my ($subscription, $freqdata, $year, $month, $day) = @_;
2320 my @newissue; # ( yy, mm, dd )
2321 my $delta_days = int( 30 / $freqdata->{issuesperunit} );
2323 if( $freqdata->{issuesperunit} == 1 ) {
2325 @newissue = Add_Delta_YM(
2326 $year, $month, $day, 0, $freqdata->{"unitsperissue"} );
2327 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2328 # Add rounded number of days based on frequency.
2329 @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
2330 $subscription->{countissuesperunit}++;
2332 # We finished a cycle of issues within a unit.
2333 # Subtract delta * (issues - 1), add 1 month
2334 @newissue = Add_Delta_Days( $year, $month, $day,
2335 -$delta_days * ($freqdata->{issuesperunit} - 1) );
2336 @newissue = Add_Delta_YM( @newissue, 0, 1 );
2337 $subscription->{countissuesperunit} = 1;
2342 sub _get_next_date_year {
2343 my ($subscription, $freqdata, $year, $month, $day) = @_;
2345 my @newissue; # ( yy, mm, dd )
2346 my $delta_days = int( 365 / $freqdata->{issuesperunit} );
2348 if( $freqdata->{issuesperunit} == 1 ) {
2350 @newissue = Add_Delta_YM( $year, $month, $day, $freqdata->{"unitsperissue"}, 0 );
2351 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2352 # Add rounded number of days based on frequency.
2353 @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
2354 $subscription->{countissuesperunit}++;
2356 # We finished a cycle of issues within a unit.
2357 # Subtract delta * (issues - 1), add 1 year
2358 @newissue = Add_Delta_Days( $year, $month, $day, -$delta_days * ($freqdata->{issuesperunit} - 1) );
2359 @newissue = Add_Delta_YM( @newissue, 1, 0 );
2360 $subscription->{countissuesperunit} = 1;
2367 $resultdate = GetNextDate($publisheddate,$subscription,$freqdata,$updatecount)
2369 this function it takes the publisheddate and will return the next issue's date
2370 and will skip dates if there exists an irregularity.
2371 $publisheddate has to be an ISO date
2372 $subscription is a hashref containing at least 'firstacquidate', 'irregularity', and 'countissuesperunit'
2373 $frequency is a hashref containing frequency informations
2374 $updatecount is a boolean value which, when set to true, update the 'countissuesperunit' in database
2375 - eg if periodicity is monthly and $publisheddate is 2007-02-10 but if March and April is to be
2376 skipped then the returned date will be 2007-05-10
2379 $resultdate - then next date in the sequence (ISO date)
2381 Return undef if subscription is irregular
2386 my ( $subscription, $publisheddate, $freqdata, $updatecount ) = @_;
2388 return unless $subscription and $publisheddate;
2391 if ($freqdata->{'unit'}) {
2392 my ( $year, $month, $day ) = split /-/, $publisheddate;
2394 # Process an irregularity Hash
2395 # Suppose that irregularities are stored in a string with this structure
2396 # irreg1;irreg2;irreg3
2397 # where irregX is the number of issue which will not be received
2398 # (the first issue takes the number 1, the 2nd the number 2 and so on)
2400 if ( $subscription->{irregularity} ) {
2401 my @irreg = split /;/, $subscription->{'irregularity'} ;
2402 foreach my $irregularity (@irreg) {
2403 $irregularities{$irregularity} = 1;
2407 # Get the 'fictive' next issue number
2408 # It is used to check if next issue is an irregular issue.
2409 my $issueno = GetFictiveIssueNumber($subscription, $publisheddate, $freqdata) + 1;
2411 # Then get the next date
2412 my $unit = lc $freqdata->{'unit'};
2413 if ($unit eq 'day') {
2414 while ($irregularities{$issueno}) {
2415 ($year, $month, $day) = _get_next_date_day($subscription,
2416 $freqdata, $year, $month, $day);
2419 ($year, $month, $day) = _get_next_date_day($subscription, $freqdata,
2420 $year, $month, $day);
2422 elsif ($unit eq 'week') {
2423 while ($irregularities{$issueno}) {
2424 ($year, $month, $day) = _get_next_date_week($subscription,
2425 $freqdata, $year, $month, $day);
2428 ($year, $month, $day) = _get_next_date_week($subscription,
2429 $freqdata, $year, $month, $day);
2431 elsif ($unit eq 'month') {
2432 while ($irregularities{$issueno}) {
2433 ($year, $month, $day) = _get_next_date_month($subscription,
2434 $freqdata, $year, $month, $day);
2437 ($year, $month, $day) = _get_next_date_month($subscription,
2438 $freqdata, $year, $month, $day);
2440 elsif ($unit eq 'year') {
2441 while ($irregularities{$issueno}) {
2442 ($year, $month, $day) = _get_next_date_year($subscription,
2443 $freqdata, $year, $month, $day);
2446 ($year, $month, $day) = _get_next_date_year($subscription,
2447 $freqdata, $year, $month, $day);
2451 my $dbh = C4::Context->dbh;
2454 SET countissuesperunit = ?
2455 WHERE subscriptionid = ?
2457 my $sth = $dbh->prepare($query);
2458 $sth->execute($subscription->{'countissuesperunit'}, $subscription->{'subscriptionid'});
2461 return sprintf("%04d-%02d-%02d", $year, $month, $day);
2467 $string = &_numeration($value,$num_type,$locale);
2469 _numeration returns the string corresponding to $value in the num_type
2481 my ($value, $num_type, $locale) = @_;
2486 if ( $num_type =~ /^dayname$/ or $num_type =~ /^dayabrv$/ ) {
2487 # 1970-11-01 was a Sunday
2488 $value = $value % 7;
2489 my $dt = DateTime->new(
2495 $string = $num_type =~ /^dayname$/
2496 ? $dt->strftime("%A")
2497 : $dt->strftime("%a");
2498 } elsif ( $num_type =~ /^monthname$/ or $num_type =~ /^monthabrv$/ ) {
2499 $value = $value % 12;
2500 my $dt = DateTime->new(
2502 month => $value + 1,
2505 $string = $num_type =~ /^monthname$/
2506 ? $dt->strftime("%B")
2507 : $dt->strftime("%b");
2508 } elsif ( $num_type =~ /^season$/ ) {
2509 my @seasons= qw( Spring Summer Fall Winter );
2510 $value = $value % 4;
2511 $string = $seasons[$value];
2512 } elsif ( $num_type =~ /^seasonabrv$/ ) {
2513 my @seasonsabrv= qw( Spr Sum Fal Win );
2514 $value = $value % 4;
2515 $string = $seasonsabrv[$value];
2523 =head2 CloseSubscription
2525 Close a subscription given a subscriptionid
2529 sub CloseSubscription {
2530 my ( $subscriptionid ) = @_;
2531 return unless $subscriptionid;
2532 my $dbh = C4::Context->dbh;
2533 my $sth = $dbh->prepare( q{
2536 WHERE subscriptionid = ?
2538 $sth->execute( $subscriptionid );
2540 # Set status = missing when status = stopped
2541 $sth = $dbh->prepare( q{
2544 WHERE subscriptionid = ?
2547 $sth->execute( STOPPED, $subscriptionid, EXPECTED );
2550 =head2 ReopenSubscription
2552 Reopen a subscription given a subscriptionid
2556 sub ReopenSubscription {
2557 my ( $subscriptionid ) = @_;
2558 return unless $subscriptionid;
2559 my $dbh = C4::Context->dbh;
2560 my $sth = $dbh->prepare( q{
2563 WHERE subscriptionid = ?
2565 $sth->execute( $subscriptionid );
2567 # Set status = expected when status = stopped
2568 $sth = $dbh->prepare( q{
2571 WHERE subscriptionid = ?
2574 $sth->execute( EXPECTED, $subscriptionid, STOPPED );
2577 =head2 subscriptionCurrentlyOnOrder
2579 $bool = subscriptionCurrentlyOnOrder( $subscriptionid );
2581 Return 1 if subscription is currently on order else 0.
2585 sub subscriptionCurrentlyOnOrder {
2586 my ( $subscriptionid ) = @_;
2587 my $dbh = C4::Context->dbh;
2589 SELECT COUNT(*) FROM aqorders
2590 WHERE subscriptionid = ?
2591 AND datereceived IS NULL
2592 AND datecancellationprinted IS NULL
2594 my $sth = $dbh->prepare( $query );
2595 $sth->execute($subscriptionid);
2596 return $sth->fetchrow_array;
2599 =head2 can_claim_subscription
2601 $can = can_claim_subscription( $subscriptionid[, $userid] );
2603 Return 1 if the subscription can be claimed by the current logged user (or a given $userid), else 0.
2607 sub can_claim_subscription {
2608 my ( $subscription, $userid ) = @_;
2609 return _can_do_on_subscription( $subscription, $userid, 'claim_serials' );
2612 =head2 can_edit_subscription
2614 $can = can_edit_subscription( $subscriptionid[, $userid] );
2616 Return 1 if the subscription can be edited by the current logged user (or a given $userid), else 0.
2620 sub can_edit_subscription {
2621 my ( $subscription, $userid ) = @_;
2622 return _can_do_on_subscription( $subscription, $userid, 'edit_subscription' );
2625 =head2 can_show_subscription
2627 $can = can_show_subscription( $subscriptionid[, $userid] );
2629 Return 1 if the subscription can be shown by the current logged user (or a given $userid), else 0.
2633 sub can_show_subscription {
2634 my ( $subscription, $userid ) = @_;
2635 return _can_do_on_subscription( $subscription, $userid, '*' );
2638 sub _can_do_on_subscription {
2639 my ( $subscription, $userid, $permission ) = @_;
2640 return 0 unless C4::Context->userenv;
2641 my $flags = C4::Context->userenv->{flags};
2642 $userid ||= C4::Context->userenv->{'id'};
2644 if ( C4::Context->preference('IndependentBranches') ) {
2646 if C4::Context->IsSuperLibrarian()
2648 C4::Auth::haspermission( $userid, { serials => 'superserials' } )
2650 C4::Auth::haspermission( $userid,
2651 { serials => $permission } )
2652 and ( not defined $subscription->{branchcode}
2653 or $subscription->{branchcode} eq ''
2654 or $subscription->{branchcode} eq
2655 C4::Context->userenv->{'branch'} )
2660 if C4::Context->IsSuperLibrarian()
2662 C4::Auth::haspermission( $userid, { serials => 'superserials' } )
2663 or C4::Auth::haspermission(
2664 $userid, { serials => $permission }
2671 =head2 findSerialsByStatus
2673 @serials = findSerialsByStatus($status, $subscriptionid);
2675 Returns an array of serials matching a given status and subscription id.
2679 sub findSerialsByStatus {
2680 my ( $status, $subscriptionid ) = @_;
2681 my $dbh = C4::Context->dbh;
2682 my $query = q| SELECT * from serial
2684 AND subscriptionid = ?
2686 my $serials = $dbh->selectall_arrayref( $query, { Slice => {} }, $status, $subscriptionid );
2695 Koha Development Team <http://koha-community.org/>