Translation updates for Koha 18.11.09
[koha.git] / C4 / Serials.pm
blobc7cbd70a914c75fb64043dab3e39dd78a5068e32
1 package C4::Serials;
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>.
21 use Modern::Perl;
23 use C4::Auth qw(haspermission);
24 use C4::Context;
25 use DateTime;
26 use Date::Calc qw(:all);
27 use POSIX qw(strftime);
28 use C4::Biblio;
29 use C4::Log; # logaction
30 use C4::Debug;
31 use C4::Serials::Frequency;
32 use C4::Serials::Numberpattern;
33 use Koha::AdditionalField;
34 use Koha::DateUtils;
35 use Koha::Serial;
36 use Koha::Subscriptions;
37 use Koha::Subscription::Histories;
39 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
41 # Define statuses
42 use constant {
43 EXPECTED => 1,
44 ARRIVED => 2,
45 LATE => 3,
46 MISSING => 4,
47 MISSING_NEVER_RECIEVED => 41,
48 MISSING_SOLD_OUT => 42,
49 MISSING_DAMAGED => 43,
50 MISSING_LOST => 44,
51 NOT_ISSUED => 5,
52 DELETED => 6,
53 CLAIMED => 7,
54 STOPPED => 8,
57 use constant MISSING_STATUSES => (
58 MISSING, MISSING_NEVER_RECIEVED,
59 MISSING_SOLD_OUT, MISSING_DAMAGED,
60 MISSING_LOST
63 BEGIN {
64 require Exporter;
65 @ISA = qw(Exporter);
66 @EXPORT = qw(
67 &NewSubscription &ModSubscription &DelSubscription
68 &GetSubscription &CountSubscriptionFromBiblionumber &GetSubscriptionsFromBiblionumber
69 &SearchSubscriptions
70 &GetFullSubscriptionsFromBiblionumber &GetFullSubscription &ModSubscriptionHistory
71 &HasSubscriptionStrictlyExpired &HasSubscriptionExpired &GetExpirationDate &abouttoexpire
72 &GetSubscriptionHistoryFromSubscriptionId
74 &GetNextSeq &GetSeq &NewIssue &GetSerials
75 &GetLatestSerials &ModSerialStatus &GetNextDate &GetSerials2
76 &ReNewSubscription &GetLateOrMissingIssues
77 &GetSerialInformation &AddItem2Serial
78 &PrepareSerialsData &GetNextExpected &ModNextExpected
79 &GetPreviousSerialid
81 &GetSuppliersWithLateIssues
82 &getroutinglist &delroutingmember &addroutingmember
83 &reorder_members
84 &check_routing &updateClaim
85 &CountIssues
86 HasItems
87 &subscriptionCurrentlyOnOrder
92 =head1 NAME
94 C4::Serials - Serials Module Functions
96 =head1 SYNOPSIS
98 use C4::Serials;
100 =head1 DESCRIPTION
102 Functions for handling subscriptions, claims routing etc.
105 =head1 SUBROUTINES
107 =head2 GetSuppliersWithLateIssues
109 $supplierlist = GetSuppliersWithLateIssues()
111 this function get all suppliers with late issues.
113 return :
114 an array_ref of suppliers each entry is a hash_ref containing id and name
115 the array is in name order
117 =cut
119 sub GetSuppliersWithLateIssues {
120 my $dbh = C4::Context->dbh;
121 my $statuses = join(',', ( LATE, MISSING_STATUSES, CLAIMED ) );
122 my $query = qq|
123 SELECT DISTINCT id, name
124 FROM subscription
125 LEFT JOIN serial ON serial.subscriptionid=subscription.subscriptionid
126 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
127 WHERE id > 0
128 AND (
129 (planneddate < now() AND serial.status=1)
130 OR serial.STATUS IN ( $statuses )
132 AND subscription.closed = 0
133 ORDER BY name|;
134 return $dbh->selectall_arrayref($query, { Slice => {} });
137 =head2 GetSubscriptionHistoryFromSubscriptionId
139 $history = GetSubscriptionHistoryFromSubscriptionId($subscriptionid);
141 This function returns the subscription history as a hashref
143 =cut
145 sub GetSubscriptionHistoryFromSubscriptionId {
146 my ($subscriptionid) = @_;
148 return unless $subscriptionid;
150 my $dbh = C4::Context->dbh;
151 my $query = qq|
152 SELECT *
153 FROM subscriptionhistory
154 WHERE subscriptionid = ?
156 my $sth = $dbh->prepare($query);
157 $sth->execute($subscriptionid);
158 my $results = $sth->fetchrow_hashref;
159 $sth->finish;
161 return $results;
164 =head2 GetSerialInformation
166 $data = GetSerialInformation($serialid);
167 returns a hash_ref containing :
168 items : items marcrecord (can be an array)
169 serial table field
170 subscription table field
171 + information about subscription expiration
173 =cut
175 sub GetSerialInformation {
176 my ($serialid) = @_;
177 my $dbh = C4::Context->dbh;
178 my $query = qq|
179 SELECT serial.*, serial.notes as sernotes, serial.status as serstatus,subscription.*,subscription.subscriptionid as subsid
180 FROM serial LEFT JOIN subscription ON subscription.subscriptionid=serial.subscriptionid
181 WHERE serialid = ?
183 my $rq = $dbh->prepare($query);
184 $rq->execute($serialid);
185 my $data = $rq->fetchrow_hashref;
187 # create item information if we have serialsadditems for this subscription
188 if ( $data->{'serialsadditems'} ) {
189 my $queryitem = $dbh->prepare("SELECT itemnumber from serialitems where serialid=?");
190 $queryitem->execute($serialid);
191 my $itemnumbers = $queryitem->fetchall_arrayref( [0] );
192 require C4::Items;
193 if ( scalar(@$itemnumbers) > 0 ) {
194 foreach my $itemnum (@$itemnumbers) {
196 #It is ASSUMED that GetMarcItem ALWAYS WORK...
197 #Maybe GetMarcItem should return values on failure
198 $debug and warn "itemnumber :$itemnum->[0], bibnum :" . $data->{'biblionumber'};
199 my $itemprocessed = C4::Items::PrepareItemrecordDisplay( $data->{'biblionumber'}, $itemnum->[0], $data );
200 $itemprocessed->{'itemnumber'} = $itemnum->[0];
201 $itemprocessed->{'itemid'} = $itemnum->[0];
202 $itemprocessed->{'serialid'} = $serialid;
203 $itemprocessed->{'biblionumber'} = $data->{'biblionumber'};
204 push @{ $data->{'items'} }, $itemprocessed;
206 } else {
207 my $itemprocessed = C4::Items::PrepareItemrecordDisplay( $data->{'biblionumber'}, '', $data );
208 $itemprocessed->{'itemid'} = "N$serialid";
209 $itemprocessed->{'serialid'} = $serialid;
210 $itemprocessed->{'biblionumber'} = $data->{'biblionumber'};
211 $itemprocessed->{'countitems'} = 0;
212 push @{ $data->{'items'} }, $itemprocessed;
215 $data->{ "status" . $data->{'serstatus'} } = 1;
216 $data->{'subscriptionexpired'} = HasSubscriptionExpired( $data->{'subscriptionid'} ) && $data->{'status'} == 1;
217 $data->{'abouttoexpire'} = abouttoexpire( $data->{'subscriptionid'} );
218 $data->{cannotedit} = not can_edit_subscription( $data );
219 return $data;
222 =head2 AddItem2Serial
224 $rows = AddItem2Serial($serialid,$itemnumber);
225 Adds an itemnumber to Serial record
226 returns the number of rows affected
228 =cut
230 sub AddItem2Serial {
231 my ( $serialid, $itemnumber ) = @_;
233 return unless ($serialid and $itemnumber);
235 my $dbh = C4::Context->dbh;
236 my $rq = $dbh->prepare("INSERT INTO `serialitems` SET serialid=? , itemnumber=?");
237 $rq->execute( $serialid, $itemnumber );
238 return $rq->rows;
241 =head2 GetSubscription
243 $subs = GetSubscription($subscriptionid)
244 this function returns the subscription which has $subscriptionid as id.
245 return :
246 a hashref. This hash contains
247 subscription, subscriptionhistory, aqbooksellers.name, biblio.title
249 =cut
251 sub GetSubscription {
252 my ($subscriptionid) = @_;
253 my $dbh = C4::Context->dbh;
254 my $query = qq(
255 SELECT subscription.*,
256 subscriptionhistory.*,
257 aqbooksellers.name AS aqbooksellername,
258 biblio.title AS bibliotitle,
259 subscription.biblionumber as bibnum
260 FROM subscription
261 LEFT JOIN subscriptionhistory ON subscription.subscriptionid=subscriptionhistory.subscriptionid
262 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid=aqbooksellers.id
263 LEFT JOIN biblio ON biblio.biblionumber=subscription.biblionumber
264 WHERE subscription.subscriptionid = ?
267 $debug and warn "query : $query\nsubsid :$subscriptionid";
268 my $sth = $dbh->prepare($query);
269 $sth->execute($subscriptionid);
270 my $subscription = $sth->fetchrow_hashref;
272 return unless $subscription;
274 $subscription->{cannotedit} = not can_edit_subscription( $subscription );
276 # Add additional fields to the subscription into a new key "additional_fields"
277 my $additional_field_values = Koha::AdditionalField->fetch_all_values({
278 tablename => 'subscription',
279 record_id => $subscriptionid,
281 $subscription->{additional_fields} = $additional_field_values->{$subscriptionid};
283 return $subscription;
286 =head2 GetFullSubscription
288 $array_ref = GetFullSubscription($subscriptionid)
289 this function reads the serial table.
291 =cut
293 sub GetFullSubscription {
294 my ($subscriptionid) = @_;
296 return unless ($subscriptionid);
298 my $dbh = C4::Context->dbh;
299 my $query = qq|
300 SELECT serial.serialid,
301 serial.serialseq,
302 serial.planneddate,
303 serial.publisheddate,
304 serial.publisheddatetext,
305 serial.status,
306 serial.notes as notes,
307 year(IF(serial.publisheddate="00-00-0000",serial.planneddate,serial.publisheddate)) as year,
308 aqbooksellers.name as aqbooksellername,
309 biblio.title as bibliotitle,
310 subscription.branchcode AS branchcode,
311 subscription.subscriptionid AS subscriptionid
312 FROM serial
313 LEFT JOIN subscription ON
314 (serial.subscriptionid=subscription.subscriptionid )
315 LEFT JOIN aqbooksellers on subscription.aqbooksellerid=aqbooksellers.id
316 LEFT JOIN biblio on biblio.biblionumber=subscription.biblionumber
317 WHERE serial.subscriptionid = ?
318 ORDER BY year DESC,
319 IF(serial.publisheddate="00-00-0000",serial.planneddate,serial.publisheddate) DESC,
320 serial.subscriptionid
322 $debug and warn "GetFullSubscription query: $query";
323 my $sth = $dbh->prepare($query);
324 $sth->execute($subscriptionid);
325 my $subscriptions = $sth->fetchall_arrayref( {} );
326 my $cannotedit = not can_edit_subscription( $subscriptions->[0] ) if scalar @$subscriptions;
327 for my $subscription ( @$subscriptions ) {
328 $subscription->{cannotedit} = $cannotedit;
330 return $subscriptions;
333 =head2 PrepareSerialsData
335 $array_ref = PrepareSerialsData($serialinfomation)
336 where serialinformation is a hashref array
338 =cut
340 sub PrepareSerialsData {
341 my ($lines) = @_;
343 return unless ($lines);
345 my %tmpresults;
346 my $year;
347 my @res;
348 my $startdate;
349 my $aqbooksellername;
350 my $bibliotitle;
351 my @loopissues;
352 my $first;
353 my $previousnote = "";
355 foreach my $subs (@{$lines}) {
356 for my $datefield ( qw(publisheddate planneddate) ) {
357 # handle 0000-00-00 dates
358 if (defined $subs->{$datefield} and $subs->{$datefield} =~ m/^00/) {
359 $subs->{$datefield} = undef;
362 $subs->{ "status" . $subs->{'status'} } = 1;
363 if ( grep { $_ == $subs->{status} } ( EXPECTED, LATE, MISSING_STATUSES, CLAIMED ) ) {
364 $subs->{"checked"} = 1;
367 if ( $subs->{'year'} && $subs->{'year'} ne "" ) {
368 $year = $subs->{'year'};
369 } else {
370 $year = "manage";
372 if ( $tmpresults{$year} ) {
373 push @{ $tmpresults{$year}->{'serials'} }, $subs;
374 } else {
375 $tmpresults{$year} = {
376 'year' => $year,
377 'aqbooksellername' => $subs->{'aqbooksellername'},
378 'bibliotitle' => $subs->{'bibliotitle'},
379 'serials' => [$subs],
380 'first' => $first,
384 foreach my $key ( sort { $b cmp $a } keys %tmpresults ) {
385 push @res, $tmpresults{$key};
387 return \@res;
390 =head2 GetSubscriptionsFromBiblionumber
392 $array_ref = GetSubscriptionsFromBiblionumber($biblionumber)
393 this function get the subscription list. it reads the subscription table.
394 return :
395 reference to an array of subscriptions which have the biblionumber given on input arg.
396 each element of this array is a hashref containing
397 startdate, histstartdate,opacnote,missinglist,recievedlist,periodicity,status & enddate
399 =cut
401 sub GetSubscriptionsFromBiblionumber {
402 my ($biblionumber) = @_;
404 return unless ($biblionumber);
406 my $dbh = C4::Context->dbh;
407 my $query = qq(
408 SELECT subscription.*,
409 branches.branchname,
410 subscriptionhistory.*,
411 aqbooksellers.name AS aqbooksellername,
412 biblio.title AS bibliotitle
413 FROM subscription
414 LEFT JOIN subscriptionhistory ON subscription.subscriptionid=subscriptionhistory.subscriptionid
415 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid=aqbooksellers.id
416 LEFT JOIN biblio ON biblio.biblionumber=subscription.biblionumber
417 LEFT JOIN branches ON branches.branchcode=subscription.branchcode
418 WHERE subscription.biblionumber = ?
420 my $sth = $dbh->prepare($query);
421 $sth->execute($biblionumber);
422 my @res;
423 while ( my $subs = $sth->fetchrow_hashref ) {
424 $subs->{startdate} = output_pref( { dt => dt_from_string( $subs->{startdate} ), dateonly => 1 } );
425 $subs->{histstartdate} = output_pref( { dt => dt_from_string( $subs->{histstartdate} ), dateonly => 1 } );
426 if ( defined $subs->{histenddate} ) {
427 $subs->{histenddate} = output_pref( { dt => dt_from_string( $subs->{histenddate} ), dateonly => 1 } );
428 } else {
429 $subs->{histenddate} = "";
431 $subs->{opacnote} //= "";
432 $subs->{opacnote} =~ s/\n/\<br\/\>/g;
433 $subs->{missinglist} =~ s/\n/\<br\/\>/g;
434 $subs->{recievedlist} =~ s/\n/\<br\/\>/g;
435 $subs->{ "periodicity" . $subs->{periodicity} } = 1;
436 $subs->{ "numberpattern" . $subs->{numberpattern} } = 1;
437 $subs->{ "status" . $subs->{'status'} } = 1;
439 if (not defined $subs->{enddate} ) {
440 $subs->{enddate} = '';
441 } else {
442 $subs->{enddate} = output_pref( { dt => dt_from_string( $subs->{enddate}), dateonly => 1 } );
444 $subs->{'abouttoexpire'} = abouttoexpire( $subs->{'subscriptionid'} );
445 $subs->{'subscriptionexpired'} = HasSubscriptionExpired( $subs->{'subscriptionid'} );
446 $subs->{cannotedit} = not can_edit_subscription( $subs );
447 push @res, $subs;
449 return \@res;
452 =head2 GetFullSubscriptionsFromBiblionumber
454 $array_ref = GetFullSubscriptionsFromBiblionumber($biblionumber)
455 this function reads the serial table.
457 =cut
459 sub GetFullSubscriptionsFromBiblionumber {
460 my ($biblionumber) = @_;
461 my $dbh = C4::Context->dbh;
462 my $query = qq|
463 SELECT serial.serialid,
464 serial.serialseq,
465 serial.planneddate,
466 serial.publisheddate,
467 serial.publisheddatetext,
468 serial.status,
469 serial.notes as notes,
470 year(IF(serial.publisheddate="00-00-0000",serial.planneddate,serial.publisheddate)) as year,
471 biblio.title as bibliotitle,
472 subscription.branchcode AS branchcode,
473 subscription.subscriptionid AS subscriptionid
474 FROM serial
475 LEFT JOIN subscription ON
476 (serial.subscriptionid=subscription.subscriptionid)
477 LEFT JOIN aqbooksellers on subscription.aqbooksellerid=aqbooksellers.id
478 LEFT JOIN biblio on biblio.biblionumber=subscription.biblionumber
479 WHERE subscription.biblionumber = ?
480 ORDER BY year DESC,
481 IF(serial.publisheddate="00-00-0000",serial.planneddate,serial.publisheddate) DESC,
482 serial.subscriptionid
484 my $sth = $dbh->prepare($query);
485 $sth->execute($biblionumber);
486 my $subscriptions = $sth->fetchall_arrayref( {} );
487 my $cannotedit = not can_edit_subscription( $subscriptions->[0] ) if scalar @$subscriptions;
488 for my $subscription ( @$subscriptions ) {
489 $subscription->{cannotedit} = $cannotedit;
491 return $subscriptions;
494 =head2 SearchSubscriptions
496 @results = SearchSubscriptions($args);
498 This function returns a list of hashrefs, one for each subscription
499 that meets the conditions specified by the $args hashref.
501 The valid search fields are:
503 biblionumber
504 title
505 issn
507 callnumber
508 location
509 publisher
510 bookseller
511 branch
512 expiration_date
513 closed
515 The expiration_date search field is special; it specifies the maximum
516 subscription expiration date.
518 =cut
520 sub SearchSubscriptions {
521 my ( $args ) = @_;
523 my $additional_fields = $args->{additional_fields} // [];
524 my $matching_record_ids_for_additional_fields = [];
525 if ( @$additional_fields ) {
526 $matching_record_ids_for_additional_fields = Koha::AdditionalField->get_matching_record_ids({
527 fields => $additional_fields,
528 tablename => 'subscription',
529 exact_match => 0,
531 return () unless @$matching_record_ids_for_additional_fields;
534 my $query = q|
535 SELECT
536 subscription.notes AS publicnotes,
537 subscriptionhistory.*,
538 subscription.*,
539 biblio.notes AS biblionotes,
540 biblio.title,
541 biblio.author,
542 biblio.biblionumber,
543 aqbooksellers.name AS vendorname,
544 biblioitems.issn
545 FROM subscription
546 LEFT JOIN subscriptionhistory USING(subscriptionid)
547 LEFT JOIN biblio ON biblio.biblionumber = subscription.biblionumber
548 LEFT JOIN biblioitems ON biblioitems.biblionumber = subscription.biblionumber
549 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
551 $query .= q| WHERE 1|;
552 my @where_strs;
553 my @where_args;
554 if( $args->{biblionumber} ) {
555 push @where_strs, "biblio.biblionumber = ?";
556 push @where_args, $args->{biblionumber};
559 if( $args->{title} ){
560 my @words = split / /, $args->{title};
561 my (@strs, @args);
562 foreach my $word (@words) {
563 push @strs, "biblio.title LIKE ?";
564 push @args, "%$word%";
566 if (@strs) {
567 push @where_strs, '(' . join (' AND ', @strs) . ')';
568 push @where_args, @args;
571 if( $args->{issn} ){
572 push @where_strs, "biblioitems.issn LIKE ?";
573 push @where_args, "%$args->{issn}%";
575 if( $args->{ean} ){
576 push @where_strs, "biblioitems.ean LIKE ?";
577 push @where_args, "%$args->{ean}%";
579 if ( $args->{callnumber} ) {
580 push @where_strs, "subscription.callnumber LIKE ?";
581 push @where_args, "%$args->{callnumber}%";
583 if( $args->{publisher} ){
584 push @where_strs, "biblioitems.publishercode LIKE ?";
585 push @where_args, "%$args->{publisher}%";
587 if( $args->{bookseller} ){
588 push @where_strs, "aqbooksellers.name LIKE ?";
589 push @where_args, "%$args->{bookseller}%";
591 if( $args->{branch} ){
592 push @where_strs, "subscription.branchcode = ?";
593 push @where_args, "$args->{branch}";
595 if ( $args->{location} ) {
596 push @where_strs, "subscription.location = ?";
597 push @where_args, "$args->{location}";
599 if ( $args->{expiration_date} ) {
600 push @where_strs, "subscription.enddate <= ?";
601 push @where_args, "$args->{expiration_date}";
603 if( defined $args->{closed} ){
604 push @where_strs, "subscription.closed = ?";
605 push @where_args, "$args->{closed}";
608 if(@where_strs){
609 $query .= ' AND ' . join(' AND ', @where_strs);
611 if ( @$additional_fields ) {
612 $query .= ' AND subscriptionid IN ('
613 . join( ', ', @$matching_record_ids_for_additional_fields )
614 . ')';
617 $query .= " ORDER BY " . $args->{orderby} if $args->{orderby};
619 my $dbh = C4::Context->dbh;
620 my $sth = $dbh->prepare($query);
621 $sth->execute(@where_args);
622 my $results = $sth->fetchall_arrayref( {} );
624 for my $subscription ( @$results ) {
625 $subscription->{cannotedit} = not can_edit_subscription( $subscription );
626 $subscription->{cannotdisplay} = not can_show_subscription( $subscription );
628 my $additional_field_values = Koha::AdditionalField->fetch_all_values({
629 record_id => $subscription->{subscriptionid},
630 tablename => 'subscription'
632 $subscription->{additional_fields} = $additional_field_values->{$subscription->{subscriptionid}};
635 return @$results;
639 =head2 GetSerials
641 ($totalissues,@serials) = GetSerials($subscriptionid);
642 this function gets every serial not arrived for a given subscription
643 as well as the number of issues registered in the database (all types)
644 this number is used to see if a subscription can be deleted (=it must have only 1 issue)
646 FIXME: We should return \@serials.
648 =cut
650 sub GetSerials {
651 my ( $subscriptionid, $count ) = @_;
653 return unless $subscriptionid;
655 my $dbh = C4::Context->dbh;
657 # status = 2 is "arrived"
658 my $counter = 0;
659 $count = 5 unless ($count);
660 my @serials;
661 my $statuses = join( ',', ( ARRIVED, MISSING_STATUSES, NOT_ISSUED ) );
662 my $query = "SELECT serialid,serialseq, status, publisheddate,
663 publisheddatetext, planneddate,notes, routingnotes
664 FROM serial
665 WHERE subscriptionid = ? AND status NOT IN ( $statuses )
666 ORDER BY IF(publisheddate<>'0000-00-00',publisheddate,planneddate) DESC";
667 my $sth = $dbh->prepare($query);
668 $sth->execute($subscriptionid);
670 while ( my $line = $sth->fetchrow_hashref ) {
671 $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
672 for my $datefield ( qw( planneddate publisheddate) ) {
673 if ($line->{$datefield} && $line->{$datefield}!~m/^00/) {
674 $line->{$datefield} = output_pref( { dt => dt_from_string( $line->{$datefield} ), dateonly => 1 } );
675 } else {
676 $line->{$datefield} = q{};
679 push @serials, $line;
682 # OK, now add the last 5 issues arrives/missing
683 $query = "SELECT serialid,serialseq, status, planneddate, publisheddate,
684 publisheddatetext, notes, routingnotes
685 FROM serial
686 WHERE subscriptionid = ?
687 AND status IN ( $statuses )
688 ORDER BY IF(publisheddate<>'0000-00-00',publisheddate,planneddate) DESC
690 $sth = $dbh->prepare($query);
691 $sth->execute($subscriptionid);
692 while ( ( my $line = $sth->fetchrow_hashref ) && $counter < $count ) {
693 $counter++;
694 $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
695 for my $datefield ( qw( planneddate publisheddate) ) {
696 if ($line->{$datefield} && $line->{$datefield}!~m/^00/) {
697 $line->{$datefield} = output_pref( { dt => dt_from_string( $line->{$datefield} ), dateonly => 1 } );
698 } else {
699 $line->{$datefield} = q{};
703 push @serials, $line;
706 $query = "SELECT count(*) FROM serial WHERE subscriptionid=?";
707 $sth = $dbh->prepare($query);
708 $sth->execute($subscriptionid);
709 my ($totalissues) = $sth->fetchrow;
710 return ( $totalissues, @serials );
713 =head2 GetSerials2
715 @serials = GetSerials2($subscriptionid,$statuses);
716 this function returns every serial waited for a given subscription
717 as well as the number of issues registered in the database (all types)
718 this number is used to see if a subscription can be deleted (=it must have only 1 issue)
720 $statuses is an arrayref of statuses and is mandatory.
722 =cut
724 sub GetSerials2 {
725 my ( $subscription, $statuses ) = @_;
727 return unless ($subscription and @$statuses);
729 my $dbh = C4::Context->dbh;
730 my $query = q|
731 SELECT serialid,serialseq, status, planneddate, publisheddate,
732 publisheddatetext, notes, routingnotes
733 FROM serial
734 WHERE subscriptionid=?
736 . q| AND status IN (| . join( ",", ('?') x @$statuses ) . q|)|
737 . q|
738 ORDER BY publisheddate,serialid DESC
740 $debug and warn "GetSerials2 query: $query";
741 my $sth = $dbh->prepare($query);
742 $sth->execute( $subscription, @$statuses );
743 my @serials;
745 while ( my $line = $sth->fetchrow_hashref ) {
746 $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
747 # Format dates for display
748 for my $datefield ( qw( planneddate publisheddate ) ) {
749 if (!defined($line->{$datefield}) || $line->{$datefield} =~m/^00/) {
750 $line->{$datefield} = q{};
752 else {
753 $line->{$datefield} = output_pref( { dt => dt_from_string( $line->{$datefield} ), dateonly => 1 } );
756 push @serials, $line;
758 return @serials;
761 =head2 GetLatestSerials
763 \@serials = GetLatestSerials($subscriptionid,$limit)
764 get the $limit's latest serials arrived or missing for a given subscription
765 return :
766 a ref to an array which contains all of the latest serials stored into a hash.
768 =cut
770 sub GetLatestSerials {
771 my ( $subscriptionid, $limit ) = @_;
773 return unless ($subscriptionid and $limit);
775 my $dbh = C4::Context->dbh;
777 my $statuses = join( ',', ( ARRIVED, MISSING_STATUSES ) );
778 my $strsth = "SELECT serialid,serialseq, status, planneddate, publisheddate, notes
779 FROM serial
780 WHERE subscriptionid = ?
781 AND status IN ($statuses)
782 ORDER BY publisheddate DESC LIMIT 0,$limit
784 my $sth = $dbh->prepare($strsth);
785 $sth->execute($subscriptionid);
786 my @serials;
787 while ( my $line = $sth->fetchrow_hashref ) {
788 $line->{ "status" . $line->{status} } = 1; # fills a "statusX" value, used for template status select list
789 push @serials, $line;
792 return \@serials;
795 =head2 GetPreviousSerialid
797 $serialid = GetPreviousSerialid($subscriptionid, $nth)
798 get the $nth's previous serial for the given subscriptionid
799 return :
800 the serialid
802 =cut
804 sub GetPreviousSerialid {
805 my ( $subscriptionid, $nth ) = @_;
806 $nth ||= 1;
807 my $dbh = C4::Context->dbh;
808 my $return = undef;
810 # Status 2: Arrived
811 my $strsth = "SELECT serialid
812 FROM serial
813 WHERE subscriptionid = ?
814 AND status = 2
815 ORDER BY serialid DESC LIMIT $nth,1
817 my $sth = $dbh->prepare($strsth);
818 $sth->execute($subscriptionid);
819 my @serials;
820 my $line = $sth->fetchrow_hashref;
821 $return = $line->{'serialid'} if ($line);
823 return $return;
826 =head2 GetNextSeq
828 my (
829 $nextseq, $newlastvalue1, $newlastvalue2, $newlastvalue3,
830 $newinnerloop1, $newinnerloop2, $newinnerloop3
831 ) = GetNextSeq( $subscription, $pattern, $planneddate );
833 $subscription is a hashref containing all the attributes of the table
834 'subscription'.
835 $pattern is a hashref containing all the attributes of the table
836 'subscription_numberpatterns'.
837 $planneddate is a date string in iso format.
838 This function get the next issue for the subscription given on input arg
840 =cut
842 sub GetNextSeq {
843 my ($subscription, $pattern, $planneddate) = @_;
845 return unless ($subscription and $pattern);
847 my ( $newlastvalue1, $newlastvalue2, $newlastvalue3,
848 $newinnerloop1, $newinnerloop2, $newinnerloop3 );
849 my $count = 1;
851 if ($subscription->{'skip_serialseq'}) {
852 my @irreg = split /;/, $subscription->{'irregularity'};
853 if(@irreg > 0) {
854 my $irregularities = {};
855 $irregularities->{$_} = 1 foreach(@irreg);
856 my $issueno = GetFictiveIssueNumber($subscription, $planneddate) + 1;
857 while($irregularities->{$issueno}) {
858 $count++;
859 $issueno++;
864 my $numberingmethod = $pattern->{numberingmethod};
865 my $calculated = "";
866 if ($numberingmethod) {
867 $calculated = $numberingmethod;
868 my $locale = $subscription->{locale};
869 $newlastvalue1 = $subscription->{lastvalue1} || 0;
870 $newlastvalue2 = $subscription->{lastvalue2} || 0;
871 $newlastvalue3 = $subscription->{lastvalue3} || 0;
872 $newinnerloop1 = $subscription->{innerloop1} || 0;
873 $newinnerloop2 = $subscription->{innerloop2} || 0;
874 $newinnerloop3 = $subscription->{innerloop3} || 0;
875 my %calc;
876 foreach(qw/X Y Z/) {
877 $calc{$_} = 1 if ($numberingmethod =~ /\{$_\}/);
880 for(my $i = 0; $i < $count; $i++) {
881 if($calc{'X'}) {
882 # check if we have to increase the new value.
883 $newinnerloop1 += 1;
884 if ($newinnerloop1 >= $pattern->{every1}) {
885 $newinnerloop1 = 0;
886 $newlastvalue1 += $pattern->{add1};
888 # reset counter if needed.
889 $newlastvalue1 = $pattern->{setto1} if ($newlastvalue1 > $pattern->{whenmorethan1});
891 if($calc{'Y'}) {
892 # check if we have to increase the new value.
893 $newinnerloop2 += 1;
894 if ($newinnerloop2 >= $pattern->{every2}) {
895 $newinnerloop2 = 0;
896 $newlastvalue2 += $pattern->{add2};
898 # reset counter if needed.
899 $newlastvalue2 = $pattern->{setto2} if ($newlastvalue2 > $pattern->{whenmorethan2});
901 if($calc{'Z'}) {
902 # check if we have to increase the new value.
903 $newinnerloop3 += 1;
904 if ($newinnerloop3 >= $pattern->{every3}) {
905 $newinnerloop3 = 0;
906 $newlastvalue3 += $pattern->{add3};
908 # reset counter if needed.
909 $newlastvalue3 = $pattern->{setto3} if ($newlastvalue3 > $pattern->{whenmorethan3});
912 if($calc{'X'}) {
913 my $newlastvalue1string = _numeration( $newlastvalue1, $pattern->{numbering1}, $locale );
914 $calculated =~ s/\{X\}/$newlastvalue1string/g;
916 if($calc{'Y'}) {
917 my $newlastvalue2string = _numeration( $newlastvalue2, $pattern->{numbering2}, $locale );
918 $calculated =~ s/\{Y\}/$newlastvalue2string/g;
920 if($calc{'Z'}) {
921 my $newlastvalue3string = _numeration( $newlastvalue3, $pattern->{numbering3}, $locale );
922 $calculated =~ s/\{Z\}/$newlastvalue3string/g;
926 return ($calculated,
927 $newlastvalue1, $newlastvalue2, $newlastvalue3,
928 $newinnerloop1, $newinnerloop2, $newinnerloop3);
931 =head2 GetSeq
933 $calculated = GetSeq($subscription, $pattern)
934 $subscription is a hashref containing all the attributes of the table 'subscription'
935 $pattern is a hashref containing all the attributes of the table 'subscription_numberpatterns'
936 this function transforms {X},{Y},{Z} to 150,0,0 for example.
937 return:
938 the sequence in string format
940 =cut
942 sub GetSeq {
943 my ($subscription, $pattern) = @_;
945 return unless ($subscription and $pattern);
947 my $locale = $subscription->{locale};
949 my $calculated = $pattern->{numberingmethod};
951 my $newlastvalue1 = $subscription->{'lastvalue1'} || 0;
952 $newlastvalue1 = _numeration($newlastvalue1, $pattern->{numbering1}, $locale) if ($pattern->{numbering1}); # reset counter if needed.
953 $calculated =~ s/\{X\}/$newlastvalue1/g;
955 my $newlastvalue2 = $subscription->{'lastvalue2'} || 0;
956 $newlastvalue2 = _numeration($newlastvalue2, $pattern->{numbering2}, $locale) if ($pattern->{numbering2}); # reset counter if needed.
957 $calculated =~ s/\{Y\}/$newlastvalue2/g;
959 my $newlastvalue3 = $subscription->{'lastvalue3'} || 0;
960 $newlastvalue3 = _numeration($newlastvalue3, $pattern->{numbering3}, $locale) if ($pattern->{numbering3}); # reset counter if needed.
961 $calculated =~ s/\{Z\}/$newlastvalue3/g;
962 return $calculated;
965 =head2 GetExpirationDate
967 $enddate = GetExpirationDate($subscriptionid, [$startdate])
969 this function return the next expiration date for a subscription given on input args.
971 return
972 the enddate or undef
974 =cut
976 sub GetExpirationDate {
977 my ( $subscriptionid, $startdate ) = @_;
979 return unless ($subscriptionid);
981 my $dbh = C4::Context->dbh;
982 my $subscription = GetSubscription($subscriptionid);
983 my $enddate;
985 # we don't do the same test if the subscription is based on X numbers or on X weeks/months
986 $enddate = $startdate || $subscription->{startdate};
987 my @date = split( /-/, $enddate );
989 return if ( scalar(@date) != 3 || not check_date(@date) );
991 my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
992 if ( $frequency and $frequency->{unit} ) {
994 # If Not Irregular
995 if ( my $length = $subscription->{numberlength} ) {
997 #calculate the date of the last issue.
998 for ( my $i = 1 ; $i <= $length ; $i++ ) {
999 $enddate = GetNextDate( $subscription, $enddate );
1001 } elsif ( $subscription->{monthlength} ) {
1002 if ( $$subscription{startdate} ) {
1003 my @enddate = Add_Delta_YM( $date[0], $date[1], $date[2], 0, $subscription->{monthlength} );
1004 $enddate = sprintf( "%04d-%02d-%02d", $enddate[0], $enddate[1], $enddate[2] );
1006 } elsif ( $subscription->{weeklength} ) {
1007 if ( $$subscription{startdate} ) {
1008 my @date = split( /-/, $subscription->{startdate} );
1009 my @enddate = Add_Delta_Days( $date[0], $date[1], $date[2], $subscription->{weeklength} * 7 );
1010 $enddate = sprintf( "%04d-%02d-%02d", $enddate[0], $enddate[1], $enddate[2] );
1012 } else {
1013 $enddate = $subscription->{enddate};
1015 return $enddate;
1016 } else {
1017 return $subscription->{enddate};
1021 =head2 CountSubscriptionFromBiblionumber
1023 $subscriptionsnumber = CountSubscriptionFromBiblionumber($biblionumber)
1024 this returns a count of the subscriptions for a given biblionumber
1025 return :
1026 the number of subscriptions
1028 =cut
1030 sub CountSubscriptionFromBiblionumber {
1031 my ($biblionumber) = @_;
1033 return unless ($biblionumber);
1035 my $dbh = C4::Context->dbh;
1036 my $query = "SELECT count(*) FROM subscription WHERE biblionumber=?";
1037 my $sth = $dbh->prepare($query);
1038 $sth->execute($biblionumber);
1039 my $subscriptionsnumber = $sth->fetchrow;
1040 return $subscriptionsnumber;
1043 =head2 ModSubscriptionHistory
1045 ModSubscriptionHistory($subscriptionid,$histstartdate,$enddate,$recievedlist,$missinglist,$opacnote,$librariannote);
1047 this function modifies the history of a subscription. Put your new values on input arg.
1048 returns the number of rows affected
1050 =cut
1052 sub ModSubscriptionHistory {
1053 my ( $subscriptionid, $histstartdate, $enddate, $receivedlist, $missinglist, $opacnote, $librariannote ) = @_;
1055 return unless ($subscriptionid);
1057 my $dbh = C4::Context->dbh;
1058 my $query = "UPDATE subscriptionhistory
1059 SET histstartdate=?,histenddate=?,recievedlist=?,missinglist=?,opacnote=?,librariannote=?
1060 WHERE subscriptionid=?
1062 my $sth = $dbh->prepare($query);
1063 $receivedlist =~ s/^; // if $receivedlist;
1064 $missinglist =~ s/^; // if $missinglist;
1065 $opacnote =~ s/^; // if $opacnote;
1066 $sth->execute( $histstartdate, $enddate, $receivedlist, $missinglist, $opacnote, $librariannote, $subscriptionid );
1067 return $sth->rows;
1070 =head2 ModSerialStatus
1072 ModSerialStatus($serialid, $serialseq, $planneddate, $publisheddate,
1073 $publisheddatetext, $status, $notes);
1075 This function modify the serial status. Serial status is a number.(eg 2 is "arrived")
1076 Note : if we change from "waited" to something else,then we will have to create a new "waited" entry
1078 =cut
1080 sub ModSerialStatus {
1081 my ($serialid, $serialseq, $planneddate, $publisheddate, $publisheddatetext,
1082 $status, $notes) = @_;
1084 return unless ($serialid);
1086 #It is a usual serial
1087 # 1st, get previous status :
1088 my $dbh = C4::Context->dbh;
1089 my $query = "SELECT serial.subscriptionid,serial.status,subscription.periodicity,serial.routingnotes
1090 FROM serial, subscription
1091 WHERE serial.subscriptionid=subscription.subscriptionid
1092 AND serialid=?";
1093 my $sth = $dbh->prepare($query);
1094 $sth->execute($serialid);
1095 my ( $subscriptionid, $oldstatus, $periodicity, $routingnotes ) = $sth->fetchrow;
1096 my $frequency = GetSubscriptionFrequency($periodicity);
1098 # change status & update subscriptionhistory
1099 my $val;
1100 if ( $status == DELETED ) {
1101 DelIssue( { 'serialid' => $serialid, 'subscriptionid' => $subscriptionid, 'serialseq' => $serialseq } );
1102 } else {
1103 my $query = '
1104 UPDATE serial
1105 SET serialseq = ?, publisheddate = ?, publisheddatetext = ?,
1106 planneddate = ?, status = ?, notes = ?, routingnotes = ?
1107 WHERE serialid = ?
1109 $sth = $dbh->prepare($query);
1110 $sth->execute( $serialseq, $publisheddate, $publisheddatetext,
1111 $planneddate, $status, $notes, $routingnotes, $serialid );
1112 $query = "SELECT * FROM subscription WHERE subscriptionid = ?";
1113 $sth = $dbh->prepare($query);
1114 $sth->execute($subscriptionid);
1115 my $val = $sth->fetchrow_hashref;
1116 unless ( $val->{manualhistory} ) {
1117 $query = "SELECT missinglist,recievedlist FROM subscriptionhistory WHERE subscriptionid=?";
1118 $sth = $dbh->prepare($query);
1119 $sth->execute($subscriptionid);
1120 my ( $missinglist, $recievedlist ) = $sth->fetchrow;
1122 if ( $status == ARRIVED || ($oldstatus == ARRIVED && $status != ARRIVED) ) {
1123 $recievedlist .= "; $serialseq"
1124 if ($recievedlist !~ /(^|;)\s*$serialseq(?=;|$)/);
1127 # in case serial has been previously marked as missing
1128 if (grep /$status/, (EXPECTED, ARRIVED, LATE, CLAIMED)) {
1129 $missinglist=~ s/(^|;)\s*$serialseq(?=;|$)//g;
1132 $missinglist .= "; $serialseq"
1133 if ( ( grep { $_ == $status } ( MISSING_STATUSES ) ) && ( $missinglist !~/(^|;)\s*$serialseq(?=;|$)/ ) );
1134 $missinglist .= "; not issued $serialseq"
1135 if ( $status == NOT_ISSUED && $missinglist !~ /(^|;)\s*$serialseq(?=;|$)/ );
1137 $query = "UPDATE subscriptionhistory SET recievedlist=?, missinglist=? WHERE subscriptionid=?";
1138 $sth = $dbh->prepare($query);
1139 $recievedlist =~ s/^; //;
1140 $missinglist =~ s/^; //;
1141 $sth->execute( $recievedlist, $missinglist, $subscriptionid );
1145 # create new expected entry if needed (ie : was "expected" and has changed)
1146 my $otherIssueExpected = scalar findSerialsByStatus(EXPECTED, $subscriptionid);
1147 if ( !$otherIssueExpected && $oldstatus == EXPECTED && $status != EXPECTED ) {
1148 my $subscription = GetSubscription($subscriptionid);
1149 my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
1151 # next issue number
1152 my (
1153 $newserialseq, $newlastvalue1, $newlastvalue2, $newlastvalue3,
1154 $newinnerloop1, $newinnerloop2, $newinnerloop3
1156 = GetNextSeq( $subscription, $pattern, $publisheddate );
1158 # next date (calculated from actual date & frequency parameters)
1159 my $nextpublisheddate = GetNextDate($subscription, $publisheddate, 1);
1160 my $nextpubdate = $nextpublisheddate;
1161 $query = "UPDATE subscription SET lastvalue1=?, lastvalue2=?, lastvalue3=?, innerloop1=?, innerloop2=?, innerloop3=?
1162 WHERE subscriptionid = ?";
1163 $sth = $dbh->prepare($query);
1164 $sth->execute( $newlastvalue1, $newlastvalue2, $newlastvalue3, $newinnerloop1, $newinnerloop2, $newinnerloop3, $subscriptionid );
1165 NewIssue( $newserialseq, $subscriptionid, $subscription->{'biblionumber'}, 1, $nextpubdate, $nextpubdate, undef, $notes, $routingnotes );
1166 # check if an alert must be sent... (= a letter is defined & status became "arrived"
1167 if ( $subscription->{letter} && $status == ARRIVED && $oldstatus != ARRIVED ) {
1168 require C4::Letters;
1169 C4::Letters::SendAlerts( 'issue', $serialid, $subscription->{letter} );
1173 return;
1176 =head2 GetNextExpected
1178 $nextexpected = GetNextExpected($subscriptionid)
1180 Get the planneddate for the current expected issue of the subscription.
1182 returns a hashref:
1184 $nextexepected = {
1185 serialid => int
1186 planneddate => ISO date
1189 =cut
1191 sub GetNextExpected {
1192 my ($subscriptionid) = @_;
1194 my $dbh = C4::Context->dbh;
1195 my $query = qq{
1196 SELECT *
1197 FROM serial
1198 WHERE subscriptionid = ?
1199 AND status = ?
1200 LIMIT 1
1202 my $sth = $dbh->prepare($query);
1204 # Each subscription has only one 'expected' issue.
1205 $sth->execute( $subscriptionid, EXPECTED );
1206 my $nextissue = $sth->fetchrow_hashref;
1207 if ( !$nextissue ) {
1208 $query = qq{
1209 SELECT *
1210 FROM serial
1211 WHERE subscriptionid = ?
1212 ORDER BY publisheddate DESC
1213 LIMIT 1
1215 $sth = $dbh->prepare($query);
1216 $sth->execute($subscriptionid);
1217 $nextissue = $sth->fetchrow_hashref;
1219 foreach(qw/planneddate publisheddate/) {
1220 if ( !defined $nextissue->{$_} ) {
1221 # or should this default to 1st Jan ???
1222 $nextissue->{$_} = strftime( '%Y-%m-%d', localtime );
1224 $nextissue->{$_} = ($nextissue->{$_} ne '0000-00-00')
1225 ? $nextissue->{$_}
1226 : undef;
1229 return $nextissue;
1232 =head2 ModNextExpected
1234 ModNextExpected($subscriptionid,$date)
1236 Update the planneddate for the current expected issue of the subscription.
1237 This will modify all future prediction results.
1239 C<$date> is an ISO date.
1241 returns 0
1243 =cut
1245 sub ModNextExpected {
1246 my ( $subscriptionid, $date ) = @_;
1247 my $dbh = C4::Context->dbh;
1249 #FIXME: Would expect to only set planneddate, but we set both on new issue creation, so updating it here
1250 my $sth = $dbh->prepare('UPDATE serial SET planneddate=?,publisheddate=? WHERE subscriptionid=? AND status=?');
1252 # Each subscription has only one 'expected' issue.
1253 $sth->execute( $date, $date, $subscriptionid, EXPECTED );
1254 return 0;
1258 =head2 GetSubscriptionIrregularities
1260 =over 4
1262 =item @irreg = &GetSubscriptionIrregularities($subscriptionid);
1263 get the list of irregularities for a subscription
1265 =back
1267 =cut
1269 sub GetSubscriptionIrregularities {
1270 my $subscriptionid = shift;
1272 return unless $subscriptionid;
1274 my $dbh = C4::Context->dbh;
1275 my $query = qq{
1276 SELECT irregularity
1277 FROM subscription
1278 WHERE subscriptionid = ?
1280 my $sth = $dbh->prepare($query);
1281 $sth->execute($subscriptionid);
1283 my ($result) = $sth->fetchrow_array;
1284 my @irreg = split /;/, $result;
1286 return @irreg;
1289 =head2 ModSubscription
1291 this function modifies a subscription. Put all new values on input args.
1292 returns the number of rows affected
1294 =cut
1296 sub ModSubscription {
1297 my (
1298 $auser, $branchcode, $aqbooksellerid, $cost, $aqbudgetid, $startdate,
1299 $periodicity, $firstacquidate, $irregularity, $numberpattern, $locale,
1300 $numberlength, $weeklength, $monthlength, $lastvalue1, $innerloop1,
1301 $lastvalue2, $innerloop2, $lastvalue3, $innerloop3, $status,
1302 $biblionumber, $callnumber, $notes, $letter, $manualhistory,
1303 $internalnotes, $serialsadditems, $staffdisplaycount, $opacdisplaycount,
1304 $graceperiod, $location, $enddate, $subscriptionid, $skip_serialseq,
1305 $itemtype, $previousitemtype
1306 ) = @_;
1308 my $dbh = C4::Context->dbh;
1309 my $query = "UPDATE subscription
1310 SET librarian=?, branchcode=?, aqbooksellerid=?, cost=?, aqbudgetid=?,
1311 startdate=?, periodicity=?, firstacquidate=?, irregularity=?,
1312 numberpattern=?, locale=?, numberlength=?, weeklength=?, monthlength=?,
1313 lastvalue1=?, innerloop1=?, lastvalue2=?, innerloop2=?,
1314 lastvalue3=?, innerloop3=?, status=?, biblionumber=?,
1315 callnumber=?, notes=?, letter=?, manualhistory=?,
1316 internalnotes=?, serialsadditems=?, staffdisplaycount=?,
1317 opacdisplaycount=?, graceperiod=?, location = ?, enddate=?,
1318 skip_serialseq=?, itemtype=?, previousitemtype=?
1319 WHERE subscriptionid = ?";
1321 my $sth = $dbh->prepare($query);
1322 $sth->execute(
1323 $auser, $branchcode, $aqbooksellerid, $cost,
1324 $aqbudgetid, $startdate, $periodicity, $firstacquidate,
1325 $irregularity, $numberpattern, $locale, $numberlength,
1326 $weeklength, $monthlength, $lastvalue1, $innerloop1,
1327 $lastvalue2, $innerloop2, $lastvalue3, $innerloop3,
1328 $status, $biblionumber, $callnumber, $notes,
1329 $letter, ($manualhistory ? $manualhistory : 0),
1330 $internalnotes, $serialsadditems, $staffdisplaycount, $opacdisplaycount,
1331 $graceperiod, $location, $enddate, $skip_serialseq,
1332 $itemtype, $previousitemtype,
1333 $subscriptionid
1335 my $rows = $sth->rows;
1337 logaction( "SERIAL", "MODIFY", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1338 return $rows;
1341 =head2 NewSubscription
1343 $subscriptionid = &NewSubscription($auser,branchcode,$aqbooksellerid,$cost,$aqbudgetid,$biblionumber,
1344 $startdate,$periodicity,$numberlength,$weeklength,$monthlength,
1345 $lastvalue1,$innerloop1,$lastvalue2,$innerloop2,$lastvalue3,$innerloop3,
1346 $status, $notes, $letter, $firstacquidate, $irregularity, $numberpattern,
1347 $locale, $callnumber, $manualhistory, $internalnotes, $serialsadditems,
1348 $staffdisplaycount, $opacdisplaycount, $graceperiod, $location, $enddate,
1349 $skip_serialseq, $itemtype, $previousitemtype);
1351 Create a new subscription with value given on input args.
1353 return :
1354 the id of this new subscription
1356 =cut
1358 sub NewSubscription {
1359 my (
1360 $auser, $branchcode, $aqbooksellerid, $cost, $aqbudgetid, $biblionumber,
1361 $startdate, $periodicity, $numberlength, $weeklength, $monthlength,
1362 $lastvalue1, $innerloop1, $lastvalue2, $innerloop2, $lastvalue3,
1363 $innerloop3, $status, $notes, $letter, $firstacquidate, $irregularity,
1364 $numberpattern, $locale, $callnumber, $manualhistory, $internalnotes,
1365 $serialsadditems, $staffdisplaycount, $opacdisplaycount, $graceperiod,
1366 $location, $enddate, $skip_serialseq, $itemtype, $previousitemtype
1367 ) = @_;
1368 my $dbh = C4::Context->dbh;
1370 my $subscription = Koha::Subscription->new(
1372 librarian => $auser,
1373 branchcode => $branchcode,
1374 aqbooksellerid => $aqbooksellerid,
1375 cost => $cost,
1376 aqbudgetid => $aqbudgetid,
1377 biblionumber => $biblionumber,
1378 startdate => $startdate,
1379 periodicity => $periodicity,
1380 numberlength => $numberlength,
1381 weeklength => $weeklength,
1382 monthlength => $monthlength,
1383 lastvalue1 => $lastvalue1,
1384 innerloop1 => $innerloop1,
1385 lastvalue2 => $lastvalue2,
1386 innerloop2 => $innerloop2,
1387 lastvalue3 => $lastvalue3,
1388 innerloop3 => $innerloop3,
1389 status => $status,
1390 notes => $notes,
1391 letter => $letter,
1392 firstacquidate => $firstacquidate,
1393 irregularity => $irregularity,
1394 numberpattern => $numberpattern,
1395 locale => $locale,
1396 callnumber => $callnumber,
1397 manualhistory => $manualhistory,
1398 internalnotes => $internalnotes,
1399 serialsadditems => $serialsadditems,
1400 staffdisplaycount => $staffdisplaycount,
1401 opacdisplaycount => $opacdisplaycount,
1402 graceperiod => $graceperiod,
1403 location => $location,
1404 enddate => $enddate,
1405 skip_serialseq => $skip_serialseq,
1406 itemtype => $itemtype,
1407 previousitemtype => $previousitemtype,
1409 )->store;
1410 $subscription->discard_changes;
1411 my $subscriptionid = $subscription->subscriptionid;
1412 my ( $query, $sth );
1413 unless ($enddate) {
1414 $enddate = GetExpirationDate( $subscriptionid, $startdate );
1415 $query = qq|
1416 UPDATE subscription
1417 SET enddate=?
1418 WHERE subscriptionid=?
1420 $sth = $dbh->prepare($query);
1421 $sth->execute( $enddate, $subscriptionid );
1424 # then create the 1st expected number
1425 $query = qq(
1426 INSERT INTO subscriptionhistory
1427 (biblionumber, subscriptionid, histstartdate, missinglist, recievedlist)
1428 VALUES (?,?,?, '', '')
1430 $sth = $dbh->prepare($query);
1431 $sth->execute( $biblionumber, $subscriptionid, $startdate);
1433 # reread subscription to get a hash (for calculation of the 1st issue number)
1434 $subscription = GetSubscription($subscriptionid); # We should not do that
1435 my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
1437 # calculate issue number
1438 my $serialseq = GetSeq($subscription, $pattern) || q{};
1440 Koha::Serial->new(
1442 serialseq => $serialseq,
1443 serialseq_x => $subscription->{'lastvalue1'},
1444 serialseq_y => $subscription->{'lastvalue2'},
1445 serialseq_z => $subscription->{'lastvalue3'},
1446 subscriptionid => $subscriptionid,
1447 biblionumber => $biblionumber,
1448 status => EXPECTED,
1449 planneddate => $firstacquidate,
1450 publisheddate => $firstacquidate,
1452 )->store();
1454 logaction( "SERIAL", "ADD", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1456 #set serial flag on biblio if not already set.
1457 my $biblio = Koha::Biblios->find( $biblionumber );
1458 if ( $biblio and !$biblio->serial ) {
1459 my $record = GetMarcBiblio({ biblionumber => $biblionumber });
1460 my ( $tag, $subf ) = GetMarcFromKohaField( 'biblio.serial', $biblio->frameworkcode );
1461 if ($tag) {
1462 eval { $record->field($tag)->update( $subf => 1 ); };
1464 ModBiblio( $record, $biblionumber, $biblio->frameworkcode );
1466 return $subscriptionid;
1469 =head2 ReNewSubscription
1471 ReNewSubscription($subscriptionid,$user,$startdate,$numberlength,$weeklength,$monthlength,$note)
1473 this function renew a subscription with values given on input args.
1475 =cut
1477 sub ReNewSubscription {
1478 my ( $subscriptionid, $user, $startdate, $numberlength, $weeklength, $monthlength, $note ) = @_;
1479 my $dbh = C4::Context->dbh;
1480 my $subscription = GetSubscription($subscriptionid);
1481 my $query = qq|
1482 SELECT *
1483 FROM biblio
1484 LEFT JOIN biblioitems ON biblio.biblionumber=biblioitems.biblionumber
1485 WHERE biblio.biblionumber=?
1487 my $sth = $dbh->prepare($query);
1488 $sth->execute( $subscription->{biblionumber} );
1489 my $biblio = $sth->fetchrow_hashref;
1491 if ( C4::Context->preference("RenewSerialAddsSuggestion") ) {
1492 require C4::Suggestions;
1493 C4::Suggestions::NewSuggestion(
1494 { 'suggestedby' => $user,
1495 'title' => $subscription->{bibliotitle},
1496 'author' => $biblio->{author},
1497 'publishercode' => $biblio->{publishercode},
1498 'note' => $biblio->{note},
1499 'biblionumber' => $subscription->{biblionumber}
1504 $numberlength ||= 0; # Should not we raise an exception instead?
1505 $weeklength ||= 0;
1507 # renew subscription
1508 $query = qq|
1509 UPDATE subscription
1510 SET startdate=?,numberlength=?,weeklength=?,monthlength=?,reneweddate=NOW()
1511 WHERE subscriptionid=?
1513 $sth = $dbh->prepare($query);
1514 $sth->execute( $startdate, $numberlength, $weeklength, $monthlength, $subscriptionid );
1515 my $enddate = GetExpirationDate($subscriptionid);
1516 $debug && warn "enddate :$enddate";
1517 $query = qq|
1518 UPDATE subscription
1519 SET enddate=?
1520 WHERE subscriptionid=?
1522 $sth = $dbh->prepare($query);
1523 $sth->execute( $enddate, $subscriptionid );
1525 logaction( "SERIAL", "RENEW", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1526 return;
1529 =head2 NewIssue
1531 NewIssue($serialseq,$subscriptionid,$biblionumber,$status, $planneddate, $publisheddate, $notes, $routingnotes)
1533 Create a new issue stored on the database.
1534 Note : we have to update the recievedlist and missinglist on subscriptionhistory for this subscription.
1535 returns the serial id
1537 =cut
1539 sub NewIssue {
1540 my ( $serialseq, $subscriptionid, $biblionumber, $status, $planneddate,
1541 $publisheddate, $publisheddatetext, $notes, $routingnotes ) = @_;
1542 ### FIXME biblionumber CAN be provided by subscriptionid. So Do we STILL NEED IT ?
1544 return unless ($subscriptionid);
1546 my $schema = Koha::Database->new()->schema();
1548 my $subscription = Koha::Subscriptions->find( $subscriptionid );
1550 my $serial = Koha::Serial->new(
1552 serialseq => $serialseq,
1553 serialseq_x => $subscription->lastvalue1(),
1554 serialseq_y => $subscription->lastvalue2(),
1555 serialseq_z => $subscription->lastvalue3(),
1556 subscriptionid => $subscriptionid,
1557 biblionumber => $biblionumber,
1558 status => $status,
1559 planneddate => $planneddate,
1560 publisheddate => $publisheddate,
1561 publisheddatetext => $publisheddatetext,
1562 notes => $notes,
1563 routingnotes => $routingnotes
1565 )->store();
1567 my $serialid = $serial->id();
1569 my $subscription_history = Koha::Subscription::Histories->find($subscriptionid);
1570 my $missinglist = $subscription_history->missinglist();
1571 my $recievedlist = $subscription_history->recievedlist();
1573 if ( $status == ARRIVED ) {
1574 ### TODO Add a feature that improves recognition and description.
1575 ### As such count (serialseq) i.e. : N18,2(N19),N20
1576 ### Would use substr and index But be careful to previous presence of ()
1577 $recievedlist .= "; $serialseq" unless ( index( $recievedlist, $serialseq ) > 0 );
1579 if ( grep { /^$status$/ } (MISSING_STATUSES) ) {
1580 $missinglist .= "; $serialseq" unless ( index( $missinglist, $serialseq ) > 0 );
1583 $recievedlist =~ s/^; //;
1584 $missinglist =~ s/^; //;
1586 $subscription_history->recievedlist($recievedlist);
1587 $subscription_history->missinglist($missinglist);
1588 $subscription_history->store();
1590 return $serialid;
1593 =head2 HasSubscriptionStrictlyExpired
1595 1 or 0 = HasSubscriptionStrictlyExpired($subscriptionid)
1597 the subscription has stricly expired when today > the end subscription date
1599 return :
1600 1 if true, 0 if false, -1 if the expiration date is not set.
1602 =cut
1604 sub HasSubscriptionStrictlyExpired {
1606 # Getting end of subscription date
1607 my ($subscriptionid) = @_;
1609 return unless ($subscriptionid);
1611 my $dbh = C4::Context->dbh;
1612 my $subscription = GetSubscription($subscriptionid);
1613 my $expirationdate = $subscription->{enddate} || GetExpirationDate($subscriptionid);
1615 # If the expiration date is set
1616 if ( $expirationdate != 0 ) {
1617 my ( $endyear, $endmonth, $endday ) = split( '-', $expirationdate );
1619 # Getting today's date
1620 my ( $nowyear, $nowmonth, $nowday ) = Today();
1622 # if today's date > expiration date, then the subscription has stricly expired
1623 if ( Delta_Days( $nowyear, $nowmonth, $nowday, $endyear, $endmonth, $endday ) < 0 ) {
1624 return 1;
1625 } else {
1626 return 0;
1628 } else {
1630 # There are some cases where the expiration date is not set
1631 # As we can't determine if the subscription has expired on a date-basis,
1632 # we return -1;
1633 return -1;
1637 =head2 HasSubscriptionExpired
1639 $has_expired = HasSubscriptionExpired($subscriptionid)
1641 the subscription has expired when the next issue to arrive is out of subscription limit.
1643 return :
1644 0 if the subscription has not expired
1645 1 if the subscription has expired
1646 2 if has subscription does not have a valid expiration date set
1648 =cut
1650 sub HasSubscriptionExpired {
1651 my ($subscriptionid) = @_;
1653 return unless ($subscriptionid);
1655 my $dbh = C4::Context->dbh;
1656 my $subscription = GetSubscription($subscriptionid);
1657 my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($subscription->{periodicity});
1658 if ( $frequency and $frequency->{unit} ) {
1659 my $expirationdate = $subscription->{enddate} || GetExpirationDate($subscriptionid);
1660 if (!defined $expirationdate) {
1661 $expirationdate = q{};
1663 my $query = qq|
1664 SELECT max(planneddate)
1665 FROM serial
1666 WHERE subscriptionid=?
1668 my $sth = $dbh->prepare($query);
1669 $sth->execute($subscriptionid);
1670 my ($res) = $sth->fetchrow;
1671 if (!$res || $res=~m/^0000/) {
1672 return 0;
1674 my @res = split( /-/, $res );
1675 my @endofsubscriptiondate = split( /-/, $expirationdate );
1676 return 2 if ( scalar(@res) != 3 || scalar(@endofsubscriptiondate) != 3 || not check_date(@res) || not check_date(@endofsubscriptiondate) );
1677 return 1
1678 if ( ( @endofsubscriptiondate && Delta_Days( $res[0], $res[1], $res[2], $endofsubscriptiondate[0], $endofsubscriptiondate[1], $endofsubscriptiondate[2] ) <= 0 )
1679 || ( !$res ) );
1680 return 0;
1681 } else {
1682 # Irregular
1683 if ( $subscription->{'numberlength'} ) {
1684 my $countreceived = countissuesfrom( $subscriptionid, $subscription->{'startdate'} );
1685 return 1 if ( $countreceived > $subscription->{'numberlength'} );
1686 return 0;
1687 } else {
1688 return 0;
1691 return 0; # Notice that you'll never get here.
1694 =head2 DelSubscription
1696 DelSubscription($subscriptionid)
1697 this function deletes subscription which has $subscriptionid as id.
1699 =cut
1701 sub DelSubscription {
1702 my ($subscriptionid) = @_;
1703 my $dbh = C4::Context->dbh;
1704 $dbh->do("DELETE FROM subscription WHERE subscriptionid=?", undef, $subscriptionid);
1705 $dbh->do("DELETE FROM subscriptionhistory WHERE subscriptionid=?", undef, $subscriptionid);
1706 $dbh->do("DELETE FROM serial WHERE subscriptionid=?", undef, $subscriptionid);
1708 my $afs = Koha::AdditionalField->all({tablename => 'subscription'});
1709 foreach my $af (@$afs) {
1710 $af->delete_values({record_id => $subscriptionid});
1713 logaction( "SERIAL", "DELETE", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
1716 =head2 DelIssue
1718 DelIssue($serialseq,$subscriptionid)
1719 this function deletes an issue which has $serialseq and $subscriptionid given on input arg.
1721 returns the number of rows affected
1723 =cut
1725 sub DelIssue {
1726 my ($dataissue) = @_;
1727 my $dbh = C4::Context->dbh;
1728 ### TODO Add itemdeletion. Would need to get itemnumbers. Should be in a pref ?
1730 my $query = qq|
1731 DELETE FROM serial
1732 WHERE serialid= ?
1733 AND subscriptionid= ?
1735 my $mainsth = $dbh->prepare($query);
1736 $mainsth->execute( $dataissue->{'serialid'}, $dataissue->{'subscriptionid'} );
1738 #Delete element from subscription history
1739 $query = "SELECT * FROM subscription WHERE subscriptionid = ?";
1740 my $sth = $dbh->prepare($query);
1741 $sth->execute( $dataissue->{'subscriptionid'} );
1742 my $val = $sth->fetchrow_hashref;
1743 unless ( $val->{manualhistory} ) {
1744 my $query = qq|
1745 SELECT * FROM subscriptionhistory
1746 WHERE subscriptionid= ?
1748 my $sth = $dbh->prepare($query);
1749 $sth->execute( $dataissue->{'subscriptionid'} );
1750 my $data = $sth->fetchrow_hashref;
1751 my $serialseq = $dataissue->{'serialseq'};
1752 $data->{'missinglist'} =~ s/\b$serialseq\b//;
1753 $data->{'recievedlist'} =~ s/\b$serialseq\b//;
1754 my $strsth = "UPDATE subscriptionhistory SET " . join( ",", map { join( "=", $_, $dbh->quote( $data->{$_} ) ) } keys %$data ) . " WHERE subscriptionid=?";
1755 $sth = $dbh->prepare($strsth);
1756 $sth->execute( $dataissue->{'subscriptionid'} );
1759 return $mainsth->rows;
1762 =head2 GetLateOrMissingIssues
1764 @issuelist = GetLateMissingIssues($supplierid,$serialid)
1766 this function selects missing issues on database - where serial.status = MISSING* or serial.status = LATE or planneddate<now
1768 return :
1769 the issuelist as an array of hash refs. Each element of this array contains
1770 name,title,planneddate,serialseq,serial.subscriptionid from tables : subscription, serial & biblio
1772 =cut
1774 sub GetLateOrMissingIssues {
1775 my ( $supplierid, $serialid, $order ) = @_;
1777 return unless ( $supplierid or $serialid );
1779 my $dbh = C4::Context->dbh;
1781 my $sth;
1782 my $byserial = '';
1783 if ($serialid) {
1784 $byserial = "and serialid = " . $serialid;
1786 if ($order) {
1787 $order .= ", title";
1788 } else {
1789 $order = "title";
1791 my $missing_statuses_string = join ',', (MISSING_STATUSES);
1792 if ($supplierid) {
1793 $sth = $dbh->prepare(
1794 "SELECT
1795 serialid, aqbooksellerid, name,
1796 biblio.title, biblioitems.issn, planneddate, serialseq,
1797 serial.status, serial.subscriptionid, claimdate, claims_count,
1798 subscription.branchcode
1799 FROM serial
1800 LEFT JOIN subscription ON serial.subscriptionid=subscription.subscriptionid
1801 LEFT JOIN biblio ON subscription.biblionumber=biblio.biblionumber
1802 LEFT JOIN biblioitems ON subscription.biblionumber=biblioitems.biblionumber
1803 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
1804 WHERE subscription.subscriptionid = serial.subscriptionid
1805 AND (serial.STATUS IN ($missing_statuses_string) OR ((planneddate < now() AND serial.STATUS = ?) OR serial.STATUS = ? OR serial.STATUS = ?))
1806 AND subscription.aqbooksellerid=$supplierid
1807 $byserial
1808 ORDER BY $order"
1810 } else {
1811 $sth = $dbh->prepare(
1812 "SELECT
1813 serialid, aqbooksellerid, name,
1814 biblio.title, planneddate, serialseq,
1815 serial.status, serial.subscriptionid, claimdate, claims_count,
1816 subscription.branchcode
1817 FROM serial
1818 LEFT JOIN subscription ON serial.subscriptionid=subscription.subscriptionid
1819 LEFT JOIN biblio ON subscription.biblionumber=biblio.biblionumber
1820 LEFT JOIN aqbooksellers ON subscription.aqbooksellerid = aqbooksellers.id
1821 WHERE subscription.subscriptionid = serial.subscriptionid
1822 AND (serial.STATUS IN ($missing_statuses_string) OR ((planneddate < now() AND serial.STATUS = ?) OR serial.STATUS = ? OR serial.STATUS = ?))
1823 $byserial
1824 ORDER BY $order"
1827 $sth->execute( EXPECTED, LATE, CLAIMED );
1828 my @issuelist;
1829 while ( my $line = $sth->fetchrow_hashref ) {
1831 if ($line->{planneddate} && $line->{planneddate} !~/^0+\-/) {
1832 $line->{planneddateISO} = $line->{planneddate};
1833 $line->{planneddate} = output_pref( { dt => dt_from_string( $line->{"planneddate"} ), dateonly => 1 } );
1835 if ($line->{claimdate} && $line->{claimdate} !~/^0+\-/) {
1836 $line->{claimdateISO} = $line->{claimdate};
1837 $line->{claimdate} = output_pref( { dt => dt_from_string( $line->{"claimdate"} ), dateonly => 1 } );
1839 $line->{"status".$line->{status}} = 1;
1841 my $additional_field_values = Koha::AdditionalField->fetch_all_values({
1842 record_id => $line->{subscriptionid},
1843 tablename => 'subscription'
1845 %$line = ( %$line, additional_fields => $additional_field_values->{$line->{subscriptionid}} );
1847 push @issuelist, $line;
1849 return @issuelist;
1852 =head2 updateClaim
1854 &updateClaim($serialid)
1856 this function updates the time when a claim is issued for late/missing items
1858 called from claims.pl file
1860 =cut
1862 sub updateClaim {
1863 my ($serialids) = @_;
1864 return unless $serialids;
1865 unless ( ref $serialids ) {
1866 $serialids = [ $serialids ];
1868 my $dbh = C4::Context->dbh;
1869 return $dbh->do(q|
1870 UPDATE serial
1871 SET claimdate = NOW(),
1872 claims_count = claims_count + 1,
1873 status = ?
1874 WHERE serialid in (| . join( q|,|, (q|?|) x @$serialids ) . q|)|,
1875 {}, CLAIMED, @$serialids );
1878 =head2 check_routing
1880 $result = &check_routing($subscriptionid)
1882 this function checks to see if a serial has a routing list and returns the count of routingid
1883 used to show either an 'add' or 'edit' link
1885 =cut
1887 sub check_routing {
1888 my ($subscriptionid) = @_;
1890 return unless ($subscriptionid);
1892 my $dbh = C4::Context->dbh;
1893 my $sth = $dbh->prepare(
1894 "SELECT count(routingid) routingids FROM subscription LEFT JOIN subscriptionroutinglist
1895 ON subscription.subscriptionid = subscriptionroutinglist.subscriptionid
1896 WHERE subscription.subscriptionid = ? GROUP BY routingid ORDER BY ranking ASC
1899 $sth->execute($subscriptionid);
1900 my $line = $sth->fetchrow_hashref;
1901 my $result = $line->{'routingids'};
1902 return $result;
1905 =head2 addroutingmember
1907 addroutingmember($borrowernumber,$subscriptionid)
1909 this function takes a borrowernumber and subscriptionid and adds the member to the
1910 routing list for that serial subscription and gives them a rank on the list
1911 of either 1 or highest current rank + 1
1913 =cut
1915 sub addroutingmember {
1916 my ( $borrowernumber, $subscriptionid ) = @_;
1918 return unless ($borrowernumber and $subscriptionid);
1920 my $rank;
1921 my $dbh = C4::Context->dbh;
1922 my $sth = $dbh->prepare( "SELECT max(ranking) rank FROM subscriptionroutinglist WHERE subscriptionid = ?" );
1923 $sth->execute($subscriptionid);
1924 while ( my $line = $sth->fetchrow_hashref ) {
1925 if ( $line->{'rank'} > 0 ) {
1926 $rank = $line->{'rank'} + 1;
1927 } else {
1928 $rank = 1;
1931 $sth = $dbh->prepare( "INSERT INTO subscriptionroutinglist (subscriptionid,borrowernumber,ranking) VALUES (?,?,?)" );
1932 $sth->execute( $subscriptionid, $borrowernumber, $rank );
1935 =head2 reorder_members
1937 reorder_members($subscriptionid,$routingid,$rank)
1939 this function is used to reorder the routing list
1941 it takes the routingid of the member one wants to re-rank and the rank it is to move to
1942 - it gets all members on list puts their routingid's into an array
1943 - removes the one in the array that is $routingid
1944 - then reinjects $routingid at point indicated by $rank
1945 - then update the database with the routingids in the new order
1947 =cut
1949 sub reorder_members {
1950 my ( $subscriptionid, $routingid, $rank ) = @_;
1951 my $dbh = C4::Context->dbh;
1952 my $sth = $dbh->prepare( "SELECT * FROM subscriptionroutinglist WHERE subscriptionid = ? ORDER BY ranking ASC" );
1953 $sth->execute($subscriptionid);
1954 my @result;
1955 while ( my $line = $sth->fetchrow_hashref ) {
1956 push( @result, $line->{'routingid'} );
1959 # To find the matching index
1960 my $i;
1961 my $key = -1; # to allow for 0 being a valid response
1962 for ( $i = 0 ; $i < @result ; $i++ ) {
1963 if ( $routingid == $result[$i] ) {
1964 $key = $i; # save the index
1965 last;
1969 # if index exists in array then move it to new position
1970 if ( $key > -1 && $rank > 0 ) {
1971 my $new_rank = $rank - 1; # $new_rank is what you want the new index to be in the array
1972 my $moving_item = splice( @result, $key, 1 );
1973 splice( @result, $new_rank, 0, $moving_item );
1975 for ( my $j = 0 ; $j < @result ; $j++ ) {
1976 my $sth = $dbh->prepare( "UPDATE subscriptionroutinglist SET ranking = '" . ( $j + 1 ) . "' WHERE routingid = '" . $result[$j] . "'" );
1977 $sth->execute;
1979 return;
1982 =head2 delroutingmember
1984 delroutingmember($routingid,$subscriptionid)
1986 this function either deletes one member from routing list if $routingid exists otherwise
1987 deletes all members from the routing list
1989 =cut
1991 sub delroutingmember {
1993 # if $routingid exists then deletes that row otherwise deletes all with $subscriptionid
1994 my ( $routingid, $subscriptionid ) = @_;
1995 my $dbh = C4::Context->dbh;
1996 if ($routingid) {
1997 my $sth = $dbh->prepare("DELETE FROM subscriptionroutinglist WHERE routingid = ?");
1998 $sth->execute($routingid);
1999 reorder_members( $subscriptionid, $routingid );
2000 } else {
2001 my $sth = $dbh->prepare("DELETE FROM subscriptionroutinglist WHERE subscriptionid = ?");
2002 $sth->execute($subscriptionid);
2004 return;
2007 =head2 getroutinglist
2009 @routinglist = getroutinglist($subscriptionid)
2011 this gets the info from the subscriptionroutinglist for $subscriptionid
2013 return :
2014 the routinglist as an array. Each element of the array contains a hash_ref containing
2015 routingid - a unique id, borrowernumber, ranking, and biblionumber of subscription
2017 =cut
2019 sub getroutinglist {
2020 my ($subscriptionid) = @_;
2021 my $dbh = C4::Context->dbh;
2022 my $sth = $dbh->prepare(
2023 'SELECT routingid, borrowernumber, ranking, biblionumber
2024 FROM subscription
2025 JOIN subscriptionroutinglist ON subscription.subscriptionid = subscriptionroutinglist.subscriptionid
2026 WHERE subscription.subscriptionid = ? ORDER BY ranking ASC'
2028 $sth->execute($subscriptionid);
2029 my $routinglist = $sth->fetchall_arrayref({});
2030 return @{$routinglist};
2033 =head2 countissuesfrom
2035 $result = countissuesfrom($subscriptionid,$startdate)
2037 Returns a count of serial rows matching the given subsctiptionid
2038 with published date greater than startdate
2040 =cut
2042 sub countissuesfrom {
2043 my ( $subscriptionid, $startdate ) = @_;
2044 my $dbh = C4::Context->dbh;
2045 my $query = qq|
2046 SELECT count(*)
2047 FROM serial
2048 WHERE subscriptionid=?
2049 AND serial.publisheddate>?
2051 my $sth = $dbh->prepare($query);
2052 $sth->execute( $subscriptionid, $startdate );
2053 my ($countreceived) = $sth->fetchrow;
2054 return $countreceived;
2057 =head2 CountIssues
2059 $result = CountIssues($subscriptionid)
2061 Returns a count of serial rows matching the given subsctiptionid
2063 =cut
2065 sub CountIssues {
2066 my ($subscriptionid) = @_;
2067 my $dbh = C4::Context->dbh;
2068 my $query = qq|
2069 SELECT count(*)
2070 FROM serial
2071 WHERE subscriptionid=?
2073 my $sth = $dbh->prepare($query);
2074 $sth->execute($subscriptionid);
2075 my ($countreceived) = $sth->fetchrow;
2076 return $countreceived;
2079 =head2 HasItems
2081 $result = HasItems($subscriptionid)
2083 returns a count of items from serial matching the subscriptionid
2085 =cut
2087 sub HasItems {
2088 my ($subscriptionid) = @_;
2089 my $dbh = C4::Context->dbh;
2090 my $query = q|
2091 SELECT COUNT(serialitems.itemnumber)
2092 FROM serial
2093 LEFT JOIN serialitems USING(serialid)
2094 WHERE subscriptionid=? AND serialitems.serialid IS NOT NULL
2096 my $sth=$dbh->prepare($query);
2097 $sth->execute($subscriptionid);
2098 my ($countitems)=$sth->fetchrow_array();
2099 return $countitems;
2102 =head2 abouttoexpire
2104 $result = abouttoexpire($subscriptionid)
2106 this function alerts you to the penultimate issue for a serial subscription
2108 returns 1 - if this is the penultimate issue
2109 returns 0 - if not
2111 =cut
2113 sub abouttoexpire {
2114 my ($subscriptionid) = @_;
2115 my $dbh = C4::Context->dbh;
2116 my $subscription = GetSubscription($subscriptionid);
2117 my $per = $subscription->{'periodicity'};
2118 my $frequency = C4::Serials::Frequency::GetSubscriptionFrequency($per);
2119 if ($frequency and $frequency->{unit}){
2121 my $expirationdate = GetExpirationDate($subscriptionid);
2123 my ($res) = $dbh->selectrow_array('select max(planneddate) from serial where subscriptionid = ?', undef, $subscriptionid);
2124 my $nextdate = GetNextDate($subscription, $res);
2126 # only compare dates if both dates exist.
2127 if ($nextdate and $expirationdate) {
2128 if(Date::Calc::Delta_Days(
2129 split( /-/, $nextdate ),
2130 split( /-/, $expirationdate )
2131 ) <= 0) {
2132 return 1;
2136 } elsif ($subscription->{numberlength}>0) {
2137 return (countissuesfrom($subscriptionid,$subscription->{'startdate'}) >=$subscription->{numberlength}-1);
2140 return 0;
2143 =head2 GetFictiveIssueNumber
2145 $issueno = GetFictiveIssueNumber($subscription, $publishedate);
2147 Get the position of the issue published at $publisheddate, considering the
2148 first issue (at firstacquidate) is at position 1, the next is at position 2, etc...
2149 This issuenumber doesn't take into account irregularities, so, for instance, if the 3rd
2150 issue is declared as 'irregular' (will be skipped at receipt), the next issue number
2151 will be 4, not 3. It's why it is called 'fictive'. It is NOT a serial seq, and is not
2152 depending on how many rows are in serial table.
2153 The issue number calculation is based on subscription frequency, first acquisition
2154 date, and $publisheddate.
2156 Returns undef when called for irregular frequencies.
2158 The routine is used to skip irregularities when calculating the next issue
2159 date (in GetNextDate) or the next issue number (in GetNextSeq).
2161 =cut
2163 sub GetFictiveIssueNumber {
2164 my ($subscription, $publisheddate) = @_;
2166 my $frequency = GetSubscriptionFrequency($subscription->{'periodicity'});
2167 my $unit = $frequency->{unit} ? lc $frequency->{'unit'} : undef;
2168 return if !$unit;
2169 my $issueno;
2171 my ( $year, $month, $day ) = split /-/, $publisheddate;
2172 my ( $fa_year, $fa_month, $fa_day ) = split /-/, $subscription->{'firstacquidate'};
2173 my $delta = _delta_units( [$fa_year, $fa_month, $fa_day], [$year, $month, $day], $unit );
2175 if( $frequency->{'unitsperissue'} == 1 ) {
2176 $issueno = $delta * $frequency->{'issuesperunit'} + $subscription->{'countissuesperunit'};
2177 } else { # issuesperunit == 1
2178 $issueno = 1 + int( $delta / $frequency->{'unitsperissue'} );
2180 return $issueno;
2183 sub _delta_units {
2184 my ( $date1, $date2, $unit ) = @_;
2185 # date1 and date2 are array refs in the form [ yy, mm, dd ]
2187 if( $unit eq 'day' ) {
2188 return Delta_Days( @$date1, @$date2 );
2189 } elsif( $unit eq 'week' ) {
2190 return int( Delta_Days( @$date1, @$date2 ) / 7 );
2193 # In case of months or years, this is a wrapper around N_Delta_YMD.
2194 # Note that N_Delta_YMD returns 29 days between e.g. 22-2-72 and 22-3-72
2195 # while we expect 1 month.
2196 my @delta = N_Delta_YMD( @$date1, @$date2 );
2197 if( $delta[2] > 27 ) {
2198 # Check if we could add a month
2199 my @jump = Add_Delta_YM( @$date1, $delta[0], 1 + $delta[1] );
2200 if( Delta_Days( @jump, @$date2 ) >= 0 ) {
2201 $delta[1]++;
2204 if( $delta[1] >= 12 ) {
2205 $delta[0]++;
2206 $delta[1] -= 12;
2208 # if unit is year, we only return full years
2209 return $unit eq 'month' ? $delta[0] * 12 + $delta[1] : $delta[0];
2212 sub _get_next_date_day {
2213 my ($subscription, $freqdata, $year, $month, $day) = @_;
2215 my @newissue; # ( yy, mm, dd )
2216 # We do not need $delta_days here, since it would be zero where used
2218 if( $freqdata->{issuesperunit} == 1 ) {
2219 # Add full days
2220 @newissue = Add_Delta_Days(
2221 $year, $month, $day, $freqdata->{"unitsperissue"} );
2222 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2223 # Add zero days
2224 @newissue = ( $year, $month, $day );
2225 $subscription->{countissuesperunit}++;
2226 } else {
2227 # We finished a cycle of issues within a unit.
2228 # No subtraction of zero needed, just add one day
2229 @newissue = Add_Delta_Days( $year, $month, $day, 1 );
2230 $subscription->{countissuesperunit} = 1;
2232 return @newissue;
2235 sub _get_next_date_week {
2236 my ($subscription, $freqdata, $year, $month, $day) = @_;
2238 my @newissue; # ( yy, mm, dd )
2239 my $delta_days = int( 7 / $freqdata->{issuesperunit} );
2241 if( $freqdata->{issuesperunit} == 1 ) {
2242 # Add full weeks (of 7 days)
2243 @newissue = Add_Delta_Days(
2244 $year, $month, $day, 7 * $freqdata->{"unitsperissue"} );
2245 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2246 # Add rounded number of days based on frequency.
2247 @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
2248 $subscription->{countissuesperunit}++;
2249 } else {
2250 # We finished a cycle of issues within a unit.
2251 # Subtract delta * (issues - 1), add 1 week
2252 @newissue = Add_Delta_Days( $year, $month, $day,
2253 -$delta_days * ($freqdata->{issuesperunit} - 1) );
2254 @newissue = Add_Delta_Days( @newissue, 7 );
2255 $subscription->{countissuesperunit} = 1;
2257 return @newissue;
2260 sub _get_next_date_month {
2261 my ($subscription, $freqdata, $year, $month, $day) = @_;
2263 my @newissue; # ( yy, mm, dd )
2264 my $delta_days = int( 30 / $freqdata->{issuesperunit} );
2266 if( $freqdata->{issuesperunit} == 1 ) {
2267 # Add full months
2268 @newissue = Add_Delta_YM(
2269 $year, $month, $day, 0, $freqdata->{"unitsperissue"} );
2270 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2271 # Add rounded number of days based on frequency.
2272 @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
2273 $subscription->{countissuesperunit}++;
2274 } else {
2275 # We finished a cycle of issues within a unit.
2276 # Subtract delta * (issues - 1), add 1 month
2277 @newissue = Add_Delta_Days( $year, $month, $day,
2278 -$delta_days * ($freqdata->{issuesperunit} - 1) );
2279 @newissue = Add_Delta_YM( @newissue, 0, 1 );
2280 $subscription->{countissuesperunit} = 1;
2282 return @newissue;
2285 sub _get_next_date_year {
2286 my ($subscription, $freqdata, $year, $month, $day) = @_;
2288 my @newissue; # ( yy, mm, dd )
2289 my $delta_days = int( 365 / $freqdata->{issuesperunit} );
2291 if( $freqdata->{issuesperunit} == 1 ) {
2292 # Add full years
2293 @newissue = Add_Delta_YM( $year, $month, $day, $freqdata->{"unitsperissue"}, 0 );
2294 } elsif ( $subscription->{countissuesperunit} < $freqdata->{issuesperunit} ) {
2295 # Add rounded number of days based on frequency.
2296 @newissue = Add_Delta_Days( $year, $month, $day, $delta_days );
2297 $subscription->{countissuesperunit}++;
2298 } else {
2299 # We finished a cycle of issues within a unit.
2300 # Subtract delta * (issues - 1), add 1 year
2301 @newissue = Add_Delta_Days( $year, $month, $day, -$delta_days * ($freqdata->{issuesperunit} - 1) );
2302 @newissue = Add_Delta_YM( @newissue, 1, 0 );
2303 $subscription->{countissuesperunit} = 1;
2305 return @newissue;
2308 =head2 GetNextDate
2310 $resultdate = GetNextDate($publisheddate,$subscription)
2312 this function it takes the publisheddate and will return the next issue's date
2313 and will skip dates if there exists an irregularity.
2314 $publisheddate has to be an ISO date
2315 $subscription is a hashref containing at least 'periodicity', 'firstacquidate', 'irregularity', and 'countissuesperunit'
2316 $updatecount is a boolean value which, when set to true, update the 'countissuesperunit' in database
2317 - eg if periodicity is monthly and $publisheddate is 2007-02-10 but if March and April is to be
2318 skipped then the returned date will be 2007-05-10
2320 return :
2321 $resultdate - then next date in the sequence (ISO date)
2323 Return undef if subscription is irregular
2325 =cut
2327 sub GetNextDate {
2328 my ( $subscription, $publisheddate, $updatecount ) = @_;
2330 return unless $subscription and $publisheddate;
2332 my $freqdata = GetSubscriptionFrequency($subscription->{'periodicity'});
2334 if ($freqdata->{'unit'}) {
2335 my ( $year, $month, $day ) = split /-/, $publisheddate;
2337 # Process an irregularity Hash
2338 # Suppose that irregularities are stored in a string with this structure
2339 # irreg1;irreg2;irreg3
2340 # where irregX is the number of issue which will not be received
2341 # (the first issue takes the number 1, the 2nd the number 2 and so on)
2342 my %irregularities;
2343 if ( $subscription->{irregularity} ) {
2344 my @irreg = split /;/, $subscription->{'irregularity'} ;
2345 foreach my $irregularity (@irreg) {
2346 $irregularities{$irregularity} = 1;
2350 # Get the 'fictive' next issue number
2351 # It is used to check if next issue is an irregular issue.
2352 my $issueno = GetFictiveIssueNumber($subscription, $publisheddate) + 1;
2354 # Then get the next date
2355 my $unit = lc $freqdata->{'unit'};
2356 if ($unit eq 'day') {
2357 while ($irregularities{$issueno}) {
2358 ($year, $month, $day) = _get_next_date_day($subscription,
2359 $freqdata, $year, $month, $day);
2360 $issueno++;
2362 ($year, $month, $day) = _get_next_date_day($subscription, $freqdata,
2363 $year, $month, $day);
2365 elsif ($unit eq 'week') {
2366 while ($irregularities{$issueno}) {
2367 ($year, $month, $day) = _get_next_date_week($subscription,
2368 $freqdata, $year, $month, $day);
2369 $issueno++;
2371 ($year, $month, $day) = _get_next_date_week($subscription,
2372 $freqdata, $year, $month, $day);
2374 elsif ($unit eq 'month') {
2375 while ($irregularities{$issueno}) {
2376 ($year, $month, $day) = _get_next_date_month($subscription,
2377 $freqdata, $year, $month, $day);
2378 $issueno++;
2380 ($year, $month, $day) = _get_next_date_month($subscription,
2381 $freqdata, $year, $month, $day);
2383 elsif ($unit eq 'year') {
2384 while ($irregularities{$issueno}) {
2385 ($year, $month, $day) = _get_next_date_year($subscription,
2386 $freqdata, $year, $month, $day);
2387 $issueno++;
2389 ($year, $month, $day) = _get_next_date_year($subscription,
2390 $freqdata, $year, $month, $day);
2393 if ($updatecount){
2394 my $dbh = C4::Context->dbh;
2395 my $query = qq{
2396 UPDATE subscription
2397 SET countissuesperunit = ?
2398 WHERE subscriptionid = ?
2400 my $sth = $dbh->prepare($query);
2401 $sth->execute($subscription->{'countissuesperunit'}, $subscription->{'subscriptionid'});
2404 return sprintf("%04d-%02d-%02d", $year, $month, $day);
2408 =head2 _numeration
2410 $string = &_numeration($value,$num_type,$locale);
2412 _numeration returns the string corresponding to $value in the num_type
2413 num_type can take :
2414 -dayname
2415 -dayabrv
2416 -monthname
2417 -monthabrv
2418 -season
2419 -seasonabrv
2421 =cut
2423 sub _numeration {
2424 my ($value, $num_type, $locale) = @_;
2425 $value ||= 0;
2426 $num_type //= '';
2427 $locale ||= 'en';
2428 my $string;
2429 if ( $num_type =~ /^dayname$/ or $num_type =~ /^dayabrv$/ ) {
2430 # 1970-11-01 was a Sunday
2431 $value = $value % 7;
2432 my $dt = DateTime->new(
2433 year => 1970,
2434 month => 11,
2435 day => $value + 1,
2436 locale => $locale,
2438 $string = $num_type =~ /^dayname$/
2439 ? $dt->strftime("%A")
2440 : $dt->strftime("%a");
2441 } elsif ( $num_type =~ /^monthname$/ or $num_type =~ /^monthabrv$/ ) {
2442 $value = $value % 12;
2443 my $dt = DateTime->new(
2444 year => 1970,
2445 month => $value + 1,
2446 locale => $locale,
2448 $string = $num_type =~ /^monthname$/
2449 ? $dt->strftime("%B")
2450 : $dt->strftime("%b");
2451 } elsif ( $num_type =~ /^season$/ ) {
2452 my @seasons= qw( Spring Summer Fall Winter );
2453 $value = $value % 4;
2454 $string = $seasons[$value];
2455 } elsif ( $num_type =~ /^seasonabrv$/ ) {
2456 my @seasonsabrv= qw( Spr Sum Fal Win );
2457 $value = $value % 4;
2458 $string = $seasonsabrv[$value];
2459 } else {
2460 $string = $value;
2463 return $string;
2466 =head2 CloseSubscription
2468 Close a subscription given a subscriptionid
2470 =cut
2472 sub CloseSubscription {
2473 my ( $subscriptionid ) = @_;
2474 return unless $subscriptionid;
2475 my $dbh = C4::Context->dbh;
2476 my $sth = $dbh->prepare( q{
2477 UPDATE subscription
2478 SET closed = 1
2479 WHERE subscriptionid = ?
2480 } );
2481 $sth->execute( $subscriptionid );
2483 # Set status = missing when status = stopped
2484 $sth = $dbh->prepare( q{
2485 UPDATE serial
2486 SET status = ?
2487 WHERE subscriptionid = ?
2488 AND status = ?
2489 } );
2490 $sth->execute( STOPPED, $subscriptionid, EXPECTED );
2493 =head2 ReopenSubscription
2495 Reopen a subscription given a subscriptionid
2497 =cut
2499 sub ReopenSubscription {
2500 my ( $subscriptionid ) = @_;
2501 return unless $subscriptionid;
2502 my $dbh = C4::Context->dbh;
2503 my $sth = $dbh->prepare( q{
2504 UPDATE subscription
2505 SET closed = 0
2506 WHERE subscriptionid = ?
2507 } );
2508 $sth->execute( $subscriptionid );
2510 # Set status = expected when status = stopped
2511 $sth = $dbh->prepare( q{
2512 UPDATE serial
2513 SET status = ?
2514 WHERE subscriptionid = ?
2515 AND status = ?
2516 } );
2517 $sth->execute( EXPECTED, $subscriptionid, STOPPED );
2520 =head2 subscriptionCurrentlyOnOrder
2522 $bool = subscriptionCurrentlyOnOrder( $subscriptionid );
2524 Return 1 if subscription is currently on order else 0.
2526 =cut
2528 sub subscriptionCurrentlyOnOrder {
2529 my ( $subscriptionid ) = @_;
2530 my $dbh = C4::Context->dbh;
2531 my $query = qq|
2532 SELECT COUNT(*) FROM aqorders
2533 WHERE subscriptionid = ?
2534 AND datereceived IS NULL
2535 AND datecancellationprinted IS NULL
2537 my $sth = $dbh->prepare( $query );
2538 $sth->execute($subscriptionid);
2539 return $sth->fetchrow_array;
2542 =head2 can_claim_subscription
2544 $can = can_claim_subscription( $subscriptionid[, $userid] );
2546 Return 1 if the subscription can be claimed by the current logged user (or a given $userid), else 0.
2548 =cut
2550 sub can_claim_subscription {
2551 my ( $subscription, $userid ) = @_;
2552 return _can_do_on_subscription( $subscription, $userid, 'claim_serials' );
2555 =head2 can_edit_subscription
2557 $can = can_edit_subscription( $subscriptionid[, $userid] );
2559 Return 1 if the subscription can be edited by the current logged user (or a given $userid), else 0.
2561 =cut
2563 sub can_edit_subscription {
2564 my ( $subscription, $userid ) = @_;
2565 return _can_do_on_subscription( $subscription, $userid, 'edit_subscription' );
2568 =head2 can_show_subscription
2570 $can = can_show_subscription( $subscriptionid[, $userid] );
2572 Return 1 if the subscription can be shown by the current logged user (or a given $userid), else 0.
2574 =cut
2576 sub can_show_subscription {
2577 my ( $subscription, $userid ) = @_;
2578 return _can_do_on_subscription( $subscription, $userid, '*' );
2581 sub _can_do_on_subscription {
2582 my ( $subscription, $userid, $permission ) = @_;
2583 return 0 unless C4::Context->userenv;
2584 my $flags = C4::Context->userenv->{flags};
2585 $userid ||= C4::Context->userenv->{'id'};
2587 if ( C4::Context->preference('IndependentBranches') ) {
2588 return 1
2589 if C4::Context->IsSuperLibrarian()
2591 C4::Auth::haspermission( $userid, { serials => 'superserials' } )
2592 or (
2593 C4::Auth::haspermission( $userid,
2594 { serials => $permission } )
2595 and ( not defined $subscription->{branchcode}
2596 or $subscription->{branchcode} eq ''
2597 or $subscription->{branchcode} eq
2598 C4::Context->userenv->{'branch'} )
2601 else {
2602 return 1
2603 if C4::Context->IsSuperLibrarian()
2605 C4::Auth::haspermission( $userid, { serials => 'superserials' } )
2606 or C4::Auth::haspermission(
2607 $userid, { serials => $permission }
2611 return 0;
2614 =head2 findSerialsByStatus
2616 @serials = findSerialsByStatus($status, $subscriptionid);
2618 Returns an array of serials matching a given status and subscription id.
2620 =cut
2622 sub findSerialsByStatus {
2623 my ( $status, $subscriptionid ) = @_;
2624 my $dbh = C4::Context->dbh;
2625 my $query = q| SELECT * from serial
2626 WHERE status = ?
2627 AND subscriptionid = ?
2629 my $serials = $dbh->selectall_arrayref( $query, { Slice => {} }, $status, $subscriptionid );
2630 return @$serials;
2634 __END__
2636 =head1 AUTHOR
2638 Koha Development Team <http://koha-community.org/>
2640 =cut