Bug 23019: Update database structure and add atomic update
[koha.git] / C4 / RotatingCollections.pm
1 package C4::RotatingCollections;
2
3 # $Id: RotatingCollections.pm,v 0.1 2007/04/20 kylemhall
4
5 # This package is inteded to keep track of what library
6 # Items of a certain collection should be at.
7
8 # Copyright 2007 Kyle Hall
9 #
10 # This file is part of Koha.
11 #
12 # Koha is free software; you can redistribute it and/or modify it
13 # under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
16 #
17 # Koha is distributed in the hope that it will be useful, but
18 # WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with Koha; if not, see <http://www.gnu.org/licenses>.
24
25 use Modern::Perl;
26
27 use C4::Context;
28 use C4::Circulation;
29 use C4::Reserves qw(CheckReserves);
30 use Koha::Database;
31
32 use DBI;
33
34 use Data::Dumper;
35
36 use vars qw(@ISA @EXPORT);
37
38
39 =head1 NAME
40
41 C4::RotatingCollections - Functions for managing rotating collections
42
43 =head1 FUNCTIONS
44
45 =cut
46
47 BEGIN {
48     require Exporter;
49     @ISA    = qw( Exporter );
50     @EXPORT = qw(
51       CreateCollection
52       UpdateCollection
53       DeleteCollection
54
55       GetItemsInCollection
56
57       GetCollection
58       GetCollections
59
60       AddItemToCollection
61       RemoveItemFromCollection
62       TransferCollection
63
64       GetCollectionItemBranches
65     );
66 }
67
68 =head2  CreateCollection
69  ( $success, $errorcode, $errormessage ) = CreateCollection( $title, $description );
70
71 Creates a new collection
72
73  Input:
74    $title: short description of the club or service
75    $description: long description of the club or service
76
77  Output:
78    $success: 1 if all database operations were successful, 0 otherwise
79    $errorCode: Code for reason of failure, good for translating errors in templates
80    $errorMessage: English description of error
81
82 =cut
83
84 sub CreateCollection {
85     my ( $title, $description ) = @_;
86
87     my $schema = Koha::Database->new()->schema();
88     my $duplicate_titles = $schema->resultset('Collection')->count({ colTitle => $title });
89
90     ## Check for all necessary parameters
91     if ( !$title ) {
92         return ( 0, 1, "NO_TITLE" );
93     } elsif ( $duplicate_titles ) {
94         return ( 0, 2, "DUPLICATE_TITLE" );
95     }
96
97     $description ||= q{};
98
99     my $success = 1;
100
101     my $dbh = C4::Context->dbh;
102
103     my $sth;
104     $sth = $dbh->prepare(
105         "INSERT INTO collections ( colId, colTitle, colDesc )
106                         VALUES ( NULL, ?, ? )"
107     );
108     $sth->execute( $title, $description ) or return ( 0, 3, $sth->errstr() );
109
110     return 1;
111
112 }
113
114 =head2 UpdateCollection
115
116  ( $success, $errorcode, $errormessage ) = UpdateCollection( $colId, $title, $description );
117
118 Updates a collection
119
120  Input:
121    $colId: id of the collection to be updated
122    $title: short description of the club or service
123    $description: long description of the club or service
124
125  Output:
126    $success: 1 if all database operations were successful, 0 otherwise
127    $errorCode: Code for reason of failure, good for translating errors in templates
128    $errorMessage: English description of error
129
130 =cut
131
132 sub UpdateCollection {
133     my ( $colId, $title, $description ) = @_;
134
135     my $schema = Koha::Database->new()->schema();
136     my $duplicate_titles = $schema->resultset('Collection')->count({ colTitle => $title,  -not => { colId => $colId } });
137
138     ## Check for all necessary parameters
139     if ( !$colId ) {
140         return ( 0, 1, "NO_ID" );
141     }
142     if ( !$title ) {
143         return ( 0, 2, "NO_TITLE" );
144     }
145     if ( $duplicate_titles ) {
146         return ( 0, 3, "DUPLICATE_TITLE" );
147     }
148
149     my $dbh = C4::Context->dbh;
150
151     $description ||= q{};
152
153     my $sth;
154     $sth = $dbh->prepare(
155         "UPDATE collections
156                         SET 
157                         colTitle = ?, colDesc = ? 
158                         WHERE colId = ?"
159     );
160     $sth->execute( $title, $description, $colId )
161       or return ( 0, 4, $sth->errstr() );
162
163     return 1;
164
165 }
166
167 =head2 DeleteCollection
168
169  ( $success, $errorcode, $errormessage ) = DeleteCollection( $colId );
170
171 Deletes a collection of the given id
172
173  Input:
174    $colId : id of the Archetype to be deleted
175
176  Output:
177    $success: 1 if all database operations were successful, 0 otherwise
178    $errorCode: Code for reason of failure, good for translating errors in templates
179    $errorMessage: English description of error
180
181 =cut
182
183 sub DeleteCollection {
184     my ($colId) = @_;
185
186     ## Parameter check
187     if ( !$colId ) {
188         return ( 0, 1, "NO_ID" );
189     }
190
191     my $dbh = C4::Context->dbh;
192
193     my $sth;
194
195     $sth = $dbh->prepare("DELETE FROM collections WHERE colId = ?");
196     $sth->execute($colId) or return ( 0, 4, $sth->errstr() );
197
198     return 1;
199 }
200
201 =head2 GetCollections
202
203  $collections = GetCollections();
204
205 Returns data about all collections
206
207  Output:
208   On Success:
209    $results: Reference to an array of associated arrays
210   On Failure:
211    $errorCode: Code for reason of failure, good for translating errors in templates
212    $errorMessage: English description of error
213
214 =cut
215
216 sub GetCollections {
217
218     my $dbh = C4::Context->dbh;
219
220     my $sth = $dbh->prepare("SELECT * FROM collections");
221     $sth->execute() or return ( 1, $sth->errstr() );
222
223     my @results;
224     while ( my $row = $sth->fetchrow_hashref ) {
225         push( @results, $row );
226     }
227
228     return \@results;
229 }
230
231 =head2 GetItemsInCollection
232
233  ( $results, $success, $errorcode, $errormessage ) = GetItemsInCollection( $colId );
234
235 Returns information about the items in the given collection
236
237  Input:
238    $colId: The id of the collection
239
240  Output:
241    $results: Reference to an array of associated arrays
242    $success: 1 if all database operations were successful, 0 otherwise
243    $errorCode: Code for reason of failure, good for translating errors in templates
244    $errorMessage: English description of error
245
246 =cut
247
248 sub GetItemsInCollection {
249     my ($colId) = @_;
250
251     ## Parameter check
252     if ( !$colId ) {
253         return ( 0, 0, 1, "NO_ID" );
254     }
255
256     my $dbh = C4::Context->dbh;
257
258     my $sth = $dbh->prepare(
259         "SELECT
260                              biblio.title,
261                              biblio.biblionumber,
262                              items.itemcallnumber,
263                              items.barcode
264                            FROM collections, collections_tracking, items, biblio
265                            WHERE collections.colId = collections_tracking.colId
266                            AND collections_tracking.itemnumber = items.itemnumber
267                            AND items.biblionumber = biblio.biblionumber
268                            AND collections.colId = ? ORDER BY biblio.title"
269     );
270     $sth->execute($colId) or return ( 0, 0, 2, $sth->errstr() );
271
272     my @results;
273     while ( my $row = $sth->fetchrow_hashref ) {
274         push( @results, $row );
275     }
276
277     return \@results;
278 }
279
280 =head2 GetCollection
281
282  ( $colId, $colTitle, $colDesc, $colBranchcode ) = GetCollection( $colId );
283
284 Returns information about a collection
285
286  Input:
287    $colId: Id of the collection
288  Output:
289    $colId, $colTitle, $colDesc, $colBranchcode
290
291 =cut
292
293 sub GetCollection {
294     my ($colId) = @_;
295
296     my $dbh = C4::Context->dbh;
297
298     my ( $sth, @results );
299     $sth = $dbh->prepare("SELECT * FROM collections WHERE colId = ?");
300     $sth->execute($colId) or return 0;
301
302     my $row = $sth->fetchrow_hashref;
303
304     return (
305         $$row{'colId'},   $$row{'colTitle'},
306         $$row{'colDesc'}, $$row{'colBranchcode'}
307     );
308
309 }
310
311 =head2 AddItemToCollection
312
313  ( $success, $errorcode, $errormessage ) = AddItemToCollection( $colId, $itemnumber );
314
315 Adds an item to a rotating collection.
316
317  Input:
318    $colId: Collection to add the item to.
319    $itemnumber: Item to be added to the collection
320  Output:
321    $success: 1 if all database operations were successful, 0 otherwise
322    $errorCode: Code for reason of failure, good for translating errors in templates
323    $errorMessage: English description of error
324
325 =cut
326
327 sub AddItemToCollection {
328     my ( $colId, $itemnumber ) = @_;
329
330     ## Check for all necessary parameters
331     if ( !$colId ) {
332         return ( 0, 1, "NO_ID" );
333     }
334     if ( !$itemnumber ) {
335         return ( 0, 2, "NO_ITEM" );
336     }
337
338     if ( isItemInThisCollection( $itemnumber, $colId ) ) {
339         return ( 0, 2, "IN_COLLECTION" );
340     }
341     elsif ( isItemInAnyCollection($itemnumber) ) {
342         return ( 0, 3, "IN_COLLECTION_OTHER" );
343     }
344
345     my $dbh = C4::Context->dbh;
346
347     my $sth;
348     $sth = $dbh->prepare("
349         INSERT INTO collections_tracking (
350             colId,
351             itemnumber
352         ) VALUES ( ?, ? )
353     ");
354     $sth->execute( $colId, $itemnumber ) or return ( 0, 3, $sth->errstr() );
355
356     return 1;
357
358 }
359
360 =head2  RemoveItemFromCollection
361
362  ( $success, $errorcode, $errormessage ) = RemoveItemFromCollection( $colId, $itemnumber );
363
364 Removes an item to a collection
365
366  Input:
367    $colId: Collection to add the item to.
368    $itemnumber: Item to be removed from collection
369
370  Output:
371    $success: 1 if all database operations were successful, 0 otherwise
372    $errorCode: Code for reason of failure, good for translating errors in templates
373    $errorMessage: English description of error
374
375 =cut
376
377 sub RemoveItemFromCollection {
378     my ( $colId, $itemnumber ) = @_;
379
380     ## Check for all necessary parameters
381     if ( !$itemnumber ) {
382         return ( 0, 2, "NO_ITEM" );
383     }
384
385     if ( !isItemInThisCollection( $itemnumber, $colId ) ) {
386         return ( 0, 2, "NOT_IN_COLLECTION" );
387     }
388
389     my $dbh = C4::Context->dbh;
390
391     my $sth;
392     $sth = $dbh->prepare(
393         "DELETE FROM collections_tracking
394                         WHERE itemnumber = ?"
395     );
396     $sth->execute($itemnumber) or return ( 0, 3, $sth->errstr() );
397
398     return 1;
399 }
400
401 =head2 TransferCollection
402
403  ( $success, $errorcode, $errormessage ) = TransferCollection( $colId, $colBranchcode );
404
405 Transfers a collection to another branch
406
407  Input:
408    $colId: id of the collection to be updated
409    $colBranchcode: branch where collection is moving to
410
411  Output:
412    $success: 1 if all database operations were successful, 0 otherwise
413    $errorCode: Code for reason of failure, good for translating errors in templates
414    $errorMessage: English description of error
415
416 =cut
417
418 sub TransferCollection {
419     my ( $colId, $colBranchcode ) = @_;
420
421     ## Check for all necessary parameters
422     if ( !$colId ) {
423         return ( 0, 1, "NO_ID" );
424     }
425     if ( !$colBranchcode ) {
426         return ( 0, 2, "NO_BRANCHCODE" );
427     }
428
429     my $dbh = C4::Context->dbh;
430
431     my $sth;
432     $sth = $dbh->prepare(
433         "UPDATE collections
434                         SET 
435                         colBranchcode = ? 
436                         WHERE colId = ?"
437     );
438     $sth->execute( $colBranchcode, $colId ) or return ( 0, 4, $sth->errstr() );
439
440     $sth = $dbh->prepare(q{
441         SELECT items.itemnumber, items.barcode FROM collections_tracking
442         LEFT JOIN items ON collections_tracking.itemnumber = items.itemnumber
443         LEFT JOIN issues ON items.itemnumber = issues.itemnumber
444         WHERE issues.borrowernumber IS NULL
445           AND collections_tracking.colId = ?
446     });
447     $sth->execute($colId) or return ( 0, 4, $sth->errstr );
448     my @results;
449     while ( my $item = $sth->fetchrow_hashref ) {
450         my ($status) = CheckReserves( $item->{itemnumber} );
451         my @transfers = C4::Circulation::GetTransfers( $item->{itemnumber} );
452         C4::Circulation::transferbook({
453             from_branch => $item->holdingbranch,
454             to_branch => $colBranchcode,
455             barcode => $item->{barcode},
456             ignore_reserves => 1,
457             trigger => 'RotatingCollection'
458         }) unless ( $status eq 'Waiting' || $status eq 'Processing' || @transfers );
459     }
460
461     return 1;
462 }
463
464 =head2 GetCollectionItemBranches
465
466   my ( $holdingBranch, $collectionBranch ) = GetCollectionItemBranches( $itemnumber );
467
468 =cut
469
470 sub GetCollectionItemBranches {
471     my ($itemnumber) = @_;
472
473     if ( !$itemnumber ) {
474         return;
475     }
476
477     my $dbh = C4::Context->dbh;
478
479     my ( $sth, @results );
480     $sth = $dbh->prepare(
481 "SELECT holdingbranch, colBranchcode FROM items, collections, collections_tracking
482                         WHERE items.itemnumber = collections_tracking.itemnumber
483                         AND collections.colId = collections_tracking.colId
484                         AND items.itemnumber = ?"
485     );
486     $sth->execute($itemnumber);
487
488     my $row = $sth->fetchrow_hashref;
489
490     return ( $$row{'holdingbranch'}, $$row{'colBranchcode'}, );
491 }
492
493 =head2 isItemInThisCollection
494
495   $inCollection = isItemInThisCollection( $itemnumber, $colId );
496
497 =cut
498
499 sub isItemInThisCollection {
500     my ( $itemnumber, $colId ) = @_;
501
502     my $dbh = C4::Context->dbh;
503
504     my $sth = $dbh->prepare(
505 "SELECT COUNT(*) as inCollection FROM collections_tracking WHERE itemnumber = ? AND colId = ?"
506     );
507     $sth->execute( $itemnumber, $colId ) or return (0);
508
509     my $row = $sth->fetchrow_hashref;
510
511     return $$row{'inCollection'};
512 }
513
514 =head2 isItemInAnyCollection
515
516   my $inCollection = isItemInAnyCollection( $itemnumber );
517
518 =cut
519
520 sub isItemInAnyCollection {
521     my ($itemnumber) = @_;
522
523     my $dbh = C4::Context->dbh;
524
525     my $sth = $dbh->prepare(
526         "SELECT itemnumber FROM collections_tracking WHERE itemnumber = ?");
527     $sth->execute($itemnumber) or return (0);
528
529     my $row = $sth->fetchrow_hashref;
530
531     $itemnumber = $row->{itemnumber};
532     if ($itemnumber) {
533         return 1;
534     }
535     else {
536         return 0;
537     }
538 }
539
540 1;
541
542 __END__
543
544 =head1 AUTHOR
545
546 Kyle Hall <kylemhall@gmail.com>
547
548 =cut