Bug 12896: Move the bookseller-related code into Koha::Acquisition::Bookseller
[koha.git] / C4 / Acquisition.pm
1 package C4::Acquisition;
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
21 use Modern::Perl;
22 use Carp;
23 use C4::Context;
24 use C4::Debug;
25 use C4::Dates qw(format_date format_date_in_iso);
26 use MARC::Record;
27 use C4::Suggestions;
28 use C4::Biblio;
29 use C4::Contract;
30 use C4::Debug;
31 use C4::Templates qw(gettemplate);
32 use Koha::DateUtils qw( dt_from_string output_pref );
33 use Koha::Acquisition::Order;
34 use Koha::Acquisition::Bookseller;
35
36 use Time::localtime;
37 use HTML::Entities;
38
39 use vars qw($VERSION @ISA @EXPORT);
40
41 BEGIN {
42     # set the version for version checking
43     $VERSION = 3.07.00.049;
44     require Exporter;
45     @ISA    = qw(Exporter);
46     @EXPORT = qw(
47         &GetBasket &NewBasket &CloseBasket &ReopenBasket &DelBasket &ModBasket
48         &GetBasketAsCSV &GetBasketGroupAsCSV
49         &GetBasketsByBookseller &GetBasketsByBasketgroup
50         &GetBasketsInfosByBookseller
51
52         &GetBasketUsers &ModBasketUsers
53         &CanUserManageBasket
54
55         &ModBasketHeader
56
57         &ModBasketgroup &NewBasketgroup &DelBasketgroup &GetBasketgroup &CloseBasketgroup
58         &GetBasketgroups &ReOpenBasketgroup
59
60         &DelOrder &ModOrder &GetOrder &GetOrders &GetOrdersByBiblionumber
61         &GetLateOrders &GetOrderFromItemnumber
62         &SearchOrders &GetHistory &GetRecentAcqui
63         &ModReceiveOrder &CancelReceipt
64         &GetCancelledOrders &TransferOrder
65         &GetLastOrderNotReceivedFromSubscriptionid &GetLastOrderReceivedFromSubscriptionid
66         &ModItemOrder
67
68         &GetParcels &GetParcel
69
70         &GetInvoices
71         &GetInvoice
72         &GetInvoiceDetails
73         &AddInvoice
74         &ModInvoice
75         &CloseInvoice
76         &ReopenInvoice
77         &DelInvoice
78         &MergeInvoices
79
80         &GetItemnumbersFromOrder
81
82         &AddClaim
83         &GetBiblioCountByBasketno
84     );
85 }
86
87
88
89
90
91 sub GetOrderFromItemnumber {
92     my ($itemnumber) = @_;
93     my $dbh          = C4::Context->dbh;
94     my $query        = qq|
95
96     SELECT  * from aqorders    LEFT JOIN aqorders_items
97     ON (     aqorders.ordernumber = aqorders_items.ordernumber   )
98     WHERE itemnumber = ?  |;
99
100     my $sth = $dbh->prepare($query);
101
102 #    $sth->trace(3);
103
104     $sth->execute($itemnumber);
105
106     my $order = $sth->fetchrow_hashref;
107     return ( $order  );
108
109 }
110
111 # Returns the itemnumber(s) associated with the ordernumber given in parameter
112 sub GetItemnumbersFromOrder {
113     my ($ordernumber) = @_;
114     my $dbh          = C4::Context->dbh;
115     my $query        = "SELECT itemnumber FROM aqorders_items WHERE ordernumber=?";
116     my $sth = $dbh->prepare($query);
117     $sth->execute($ordernumber);
118     my @tab;
119
120     while (my $order = $sth->fetchrow_hashref) {
121     push @tab, $order->{'itemnumber'};
122     }
123
124     return @tab;
125
126 }
127
128
129
130
131
132
133 =head1 NAME
134
135 C4::Acquisition - Koha functions for dealing with orders and acquisitions
136
137 =head1 SYNOPSIS
138
139 use C4::Acquisition;
140
141 =head1 DESCRIPTION
142
143 The functions in this module deal with acquisitions, managing book
144 orders, basket and parcels.
145
146 =head1 FUNCTIONS
147
148 =head2 FUNCTIONS ABOUT BASKETS
149
150 =head3 GetBasket
151
152   $aqbasket = &GetBasket($basketnumber);
153
154 get all basket informations in aqbasket for a given basket
155
156 B<returns:> informations for a given basket returned as a hashref.
157
158 =cut
159
160 sub GetBasket {
161     my ($basketno) = @_;
162     my $dbh        = C4::Context->dbh;
163     my $query = "
164         SELECT  aqbasket.*,
165                 concat( b.firstname,' ',b.surname) AS authorisedbyname
166         FROM    aqbasket
167         LEFT JOIN borrowers b ON aqbasket.authorisedby=b.borrowernumber
168         WHERE basketno=?
169     ";
170     my $sth=$dbh->prepare($query);
171     $sth->execute($basketno);
172     my $basket = $sth->fetchrow_hashref;
173     return ( $basket );
174 }
175
176 #------------------------------------------------------------#
177
178 =head3 NewBasket
179
180   $basket = &NewBasket( $booksellerid, $authorizedby, $basketname, 
181       $basketnote, $basketbooksellernote, $basketcontractnumber, $deliveryplace, $billingplace );
182
183 Create a new basket in aqbasket table
184
185 =over
186
187 =item C<$booksellerid> is a foreign key in the aqbasket table
188
189 =item C<$authorizedby> is the username of who created the basket
190
191 =back
192
193 The other parameters are optional, see ModBasketHeader for more info on them.
194
195 =cut
196
197 sub NewBasket {
198     my ( $booksellerid, $authorisedby, $basketname, $basketnote,
199         $basketbooksellernote, $basketcontractnumber, $deliveryplace,
200         $billingplace ) = @_;
201     my $dbh = C4::Context->dbh;
202     my $query =
203         'INSERT INTO aqbasket (creationdate,booksellerid,authorisedby) '
204       . 'VALUES  (now(),?,?)';
205     $dbh->do( $query, {}, $booksellerid, $authorisedby );
206
207     my $basket = $dbh->{mysql_insertid};
208     $basketname           ||= q{}; # default to empty strings
209     $basketnote           ||= q{};
210     $basketbooksellernote ||= q{};
211     ModBasketHeader( $basket, $basketname, $basketnote, $basketbooksellernote,
212         $basketcontractnumber, $booksellerid, $deliveryplace, $billingplace );
213     return $basket;
214 }
215
216 #------------------------------------------------------------#
217
218 =head3 CloseBasket
219
220   &CloseBasket($basketno);
221
222 close a basket (becomes unmodifiable, except for receives)
223
224 =cut
225
226 sub CloseBasket {
227     my ($basketno) = @_;
228     my $dbh        = C4::Context->dbh;
229     my $query = "
230         UPDATE aqbasket
231         SET    closedate=now()
232         WHERE  basketno=?
233     ";
234     my $sth = $dbh->prepare($query);
235     $sth->execute($basketno);
236
237     my @orders = GetOrders($basketno);
238     foreach my $order (@orders) {
239         $query = qq{
240             UPDATE aqorders
241             SET orderstatus = 'ordered'
242             WHERE ordernumber = ?;
243         };
244         $sth = $dbh->prepare($query);
245         $sth->execute($order->{'ordernumber'});
246     }
247 }
248
249 =head3 ReopenBasket
250
251   &ReopenBasket($basketno);
252
253 reopen a basket
254
255 =cut
256
257 sub ReopenBasket {
258     my ($basketno) = @_;
259     my $dbh        = C4::Context->dbh;
260     my $query = "
261         UPDATE aqbasket
262         SET    closedate=NULL
263         WHERE  basketno=?
264     ";
265     my $sth = $dbh->prepare($query);
266     $sth->execute($basketno);
267
268     my @orders = GetOrders($basketno);
269     foreach my $order (@orders) {
270         $query = qq{
271             UPDATE aqorders
272             SET orderstatus = 'new'
273             WHERE ordernumber = ?;
274         };
275         $sth = $dbh->prepare($query);
276         $sth->execute($order->{'ordernumber'});
277     }
278 }
279
280 #------------------------------------------------------------#
281
282 =head3 GetBasketAsCSV
283
284   &GetBasketAsCSV($basketno);
285
286 Export a basket as CSV
287
288 $cgi parameter is needed for column name translation
289
290 =cut
291
292 sub GetBasketAsCSV {
293     my ($basketno, $cgi) = @_;
294     my $basket = GetBasket($basketno);
295     my @orders = GetOrders($basketno);
296     my $contract = GetContract({
297         contractnumber => $basket->{'contractnumber'}
298     });
299
300     my $template = C4::Templates::gettemplate("acqui/csv/basket.tt", "intranet", $cgi);
301
302     my @rows;
303     foreach my $order (@orders) {
304         my $bd = GetBiblioData( $order->{'biblionumber'} );
305         my $row = {
306             contractname => $contract->{'contractname'},
307             ordernumber => $order->{'ordernumber'},
308             entrydate => $order->{'entrydate'},
309             isbn => $order->{'isbn'},
310             author => $bd->{'author'},
311             title => $bd->{'title'},
312             publicationyear => $bd->{'publicationyear'},
313             publishercode => $bd->{'publishercode'},
314             collectiontitle => $bd->{'collectiontitle'},
315             notes => $order->{'order_vendornote'},
316             quantity => $order->{'quantity'},
317             rrp => $order->{'rrp'},
318             deliveryplace => C4::Branch::GetBranchName( $basket->{'deliveryplace'} ),
319             billingplace => C4::Branch::GetBranchName( $basket->{'billingplace'} ),
320         };
321         foreach(qw(
322             contractname author title publishercode collectiontitle notes
323             deliveryplace billingplace
324         ) ) {
325             # Double the quotes to not be interpreted as a field end
326             $row->{$_} =~ s/"/""/g if $row->{$_};
327         }
328         push @rows, $row;
329     }
330
331     @rows = sort {
332         if(defined $a->{publishercode} and defined $b->{publishercode}) {
333             $a->{publishercode} cmp $b->{publishercode};
334         }
335     } @rows;
336
337     $template->param(rows => \@rows);
338
339     return $template->output;
340 }
341
342
343 =head3 GetBasketGroupAsCSV
344
345 =over
346
347 &GetBasketGroupAsCSV($basketgroupid);
348
349 Export a basket group as CSV
350
351 $cgi parameter is needed for column name translation
352
353 =back
354
355 =cut
356
357 sub GetBasketGroupAsCSV {
358     my ($basketgroupid, $cgi) = @_;
359     my $baskets = GetBasketsByBasketgroup($basketgroupid);
360
361     my $template = C4::Templates::gettemplate('acqui/csv/basketgroup.tt', 'intranet', $cgi);
362
363     my @rows;
364     for my $basket (@$baskets) {
365         my @orders     = GetOrders( $basket->{basketno} );
366         my $contract   = GetContract({
367             contractnumber => $basket->{contractnumber}
368         });
369         my $bookseller = Koha::Acquisition::Bookseller->fetch({ id => $basket->{booksellerid} });
370         my $basketgroup = GetBasketgroup( $$basket{basketgroupid} );
371
372         foreach my $order (@orders) {
373             my $bd = GetBiblioData( $order->{'biblionumber'} );
374             my $row = {
375                 clientnumber => $bookseller->{accountnumber},
376                 basketname => $basket->{basketname},
377                 ordernumber => $order->{ordernumber},
378                 author => $bd->{author},
379                 title => $bd->{title},
380                 publishercode => $bd->{publishercode},
381                 publicationyear => $bd->{publicationyear},
382                 collectiontitle => $bd->{collectiontitle},
383                 isbn => $order->{isbn},
384                 quantity => $order->{quantity},
385                 rrp => $order->{rrp},
386                 discount => $bookseller->{discount},
387                 ecost => $order->{ecost},
388                 notes => $order->{order_vendornote},
389                 entrydate => $order->{entrydate},
390                 booksellername => $bookseller->{name},
391                 bookselleraddress => $bookseller->{address1},
392                 booksellerpostal => $bookseller->{postal},
393                 contractnumber => $contract->{contractnumber},
394                 contractname => $contract->{contractname},
395                 basketgroupdeliveryplace => C4::Branch::GetBranchName( $basketgroup->{deliveryplace} ),
396                 basketgroupbillingplace => C4::Branch::GetBranchName( $basketgroup->{billingplace} ),
397                 basketdeliveryplace => C4::Branch::GetBranchName( $basket->{deliveryplace} ),
398                 basketbillingplace => C4::Branch::GetBranchName( $basket->{billingplace} ),
399             };
400             foreach(qw(
401                 basketname author title publishercode collectiontitle notes
402                 booksellername bookselleraddress booksellerpostal contractname
403                 basketgroupdeliveryplace basketgroupbillingplace
404                 basketdeliveryplace basketbillingplace
405             ) ) {
406                 # Double the quotes to not be interpreted as a field end
407                 $row->{$_} =~ s/"/""/g if $row->{$_};
408             }
409             push @rows, $row;
410          }
411      }
412     $template->param(rows => \@rows);
413
414     return $template->output;
415
416 }
417
418 =head3 CloseBasketgroup
419
420   &CloseBasketgroup($basketgroupno);
421
422 close a basketgroup
423
424 =cut
425
426 sub CloseBasketgroup {
427     my ($basketgroupno) = @_;
428     my $dbh        = C4::Context->dbh;
429     my $sth = $dbh->prepare("
430         UPDATE aqbasketgroups
431         SET    closed=1
432         WHERE  id=?
433     ");
434     $sth->execute($basketgroupno);
435 }
436
437 #------------------------------------------------------------#
438
439 =head3 ReOpenBaskergroup($basketgroupno)
440
441   &ReOpenBaskergroup($basketgroupno);
442
443 reopen a basketgroup
444
445 =cut
446
447 sub ReOpenBasketgroup {
448     my ($basketgroupno) = @_;
449     my $dbh        = C4::Context->dbh;
450     my $sth = $dbh->prepare("
451         UPDATE aqbasketgroups
452         SET    closed=0
453         WHERE  id=?
454     ");
455     $sth->execute($basketgroupno);
456 }
457
458 #------------------------------------------------------------#
459
460
461 =head3 DelBasket
462
463   &DelBasket($basketno);
464
465 Deletes the basket that has basketno field $basketno in the aqbasket table.
466
467 =over
468
469 =item C<$basketno> is the primary key of the basket in the aqbasket table.
470
471 =back
472
473 =cut
474
475 sub DelBasket {
476     my ( $basketno ) = @_;
477     my $query = "DELETE FROM aqbasket WHERE basketno=?";
478     my $dbh = C4::Context->dbh;
479     my $sth = $dbh->prepare($query);
480     $sth->execute($basketno);
481     return;
482 }
483
484 #------------------------------------------------------------#
485
486 =head3 ModBasket
487
488   &ModBasket($basketinfo);
489
490 Modifies a basket, using a hashref $basketinfo for the relevant information, only $basketinfo->{'basketno'} is required.
491
492 =over
493
494 =item C<$basketno> is the primary key of the basket in the aqbasket table.
495
496 =back
497
498 =cut
499
500 sub ModBasket {
501     my $basketinfo = shift;
502     my $query = "UPDATE aqbasket SET ";
503     my @params;
504     foreach my $key (keys %$basketinfo){
505         if ($key ne 'basketno'){
506             $query .= "$key=?, ";
507             push(@params, $basketinfo->{$key} || undef );
508         }
509     }
510 # get rid of the "," at the end of $query
511     if (substr($query, length($query)-2) eq ', '){
512         chop($query);
513         chop($query);
514         $query .= ' ';
515     }
516     $query .= "WHERE basketno=?";
517     push(@params, $basketinfo->{'basketno'});
518     my $dbh = C4::Context->dbh;
519     my $sth = $dbh->prepare($query);
520     $sth->execute(@params);
521
522     return;
523 }
524
525 #------------------------------------------------------------#
526
527 =head3 ModBasketHeader
528
529   &ModBasketHeader($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid);
530
531 Modifies a basket's header.
532
533 =over
534
535 =item C<$basketno> is the "basketno" field in the "aqbasket" table;
536
537 =item C<$basketname> is the "basketname" field in the "aqbasket" table;
538
539 =item C<$note> is the "note" field in the "aqbasket" table;
540
541 =item C<$booksellernote> is the "booksellernote" field in the "aqbasket" table;
542
543 =item C<$contractnumber> is the "contractnumber" (foreign) key in the "aqbasket" table.
544
545 =item C<$booksellerid> is the id (foreign) key in the "aqbooksellers" table for the vendor.
546
547 =item C<$deliveryplace> is the "deliveryplace" field in the aqbasket table.
548
549 =item C<$billingplace> is the "billingplace" field in the aqbasket table.
550
551 =back
552
553 =cut
554
555 sub ModBasketHeader {
556     my ($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid, $deliveryplace, $billingplace) = @_;
557     my $query = qq{
558         UPDATE aqbasket
559         SET basketname=?, note=?, booksellernote=?, booksellerid=?, deliveryplace=?, billingplace=?
560         WHERE basketno=?
561     };
562
563     my $dbh = C4::Context->dbh;
564     my $sth = $dbh->prepare($query);
565     $sth->execute($basketname, $note, $booksellernote, $booksellerid, $deliveryplace, $billingplace, $basketno);
566
567     if ( $contractnumber ) {
568         my $query2 ="UPDATE aqbasket SET contractnumber=? WHERE basketno=?";
569         my $sth2 = $dbh->prepare($query2);
570         $sth2->execute($contractnumber,$basketno);
571     }
572     return;
573 }
574
575 #------------------------------------------------------------#
576
577 =head3 GetBasketsByBookseller
578
579   @results = &GetBasketsByBookseller($booksellerid, $extra);
580
581 Returns a list of hashes of all the baskets that belong to bookseller 'booksellerid'.
582
583 =over
584
585 =item C<$booksellerid> is the 'id' field of the bookseller in the aqbooksellers table
586
587 =item C<$extra> is the extra sql parameters, can be
588
589  $extra->{groupby}: group baskets by column
590     ex. $extra->{groupby} = aqbasket.basketgroupid
591  $extra->{orderby}: order baskets by column
592  $extra->{limit}: limit number of results (can be helpful for pagination)
593
594 =back
595
596 =cut
597
598 sub GetBasketsByBookseller {
599     my ($booksellerid, $extra) = @_;
600     my $query = "SELECT * FROM aqbasket WHERE booksellerid=?";
601     if ($extra){
602         if ($extra->{groupby}) {
603             $query .= " GROUP by $extra->{groupby}";
604         }
605         if ($extra->{orderby}){
606             $query .= " ORDER by $extra->{orderby}";
607         }
608         if ($extra->{limit}){
609             $query .= " LIMIT $extra->{limit}";
610         }
611     }
612     my $dbh = C4::Context->dbh;
613     my $sth = $dbh->prepare($query);
614     $sth->execute($booksellerid);
615     return $sth->fetchall_arrayref({});
616 }
617
618 =head3 GetBasketsInfosByBookseller
619
620     my $baskets = GetBasketsInfosByBookseller($supplierid, $allbaskets);
621
622 The optional second parameter allbaskets is a boolean allowing you to
623 select all baskets from the supplier; by default only active baskets (open or 
624 closed but still something to receive) are returned.
625
626 Returns in a arrayref of hashref all about booksellers baskets, plus:
627     total_biblios: Number of distinct biblios in basket
628     total_items: Number of items in basket
629     expected_items: Number of non-received items in basket
630
631 =cut
632
633 sub GetBasketsInfosByBookseller {
634     my ($supplierid, $allbaskets) = @_;
635
636     return unless $supplierid;
637
638     my $dbh = C4::Context->dbh;
639     my $query = q{
640         SELECT aqbasket.*,
641           SUM(aqorders.quantity) AS total_items,
642           SUM(
643             IF ( aqorders.orderstatus = 'cancelled', aqorders.quantity, 0 )
644           ) AS total_items_cancelled,
645           COUNT(DISTINCT aqorders.biblionumber) AS total_biblios,
646           SUM(
647             IF(aqorders.datereceived IS NULL
648               AND aqorders.datecancellationprinted IS NULL
649             , aqorders.quantity
650             , 0)
651           ) AS expected_items
652         FROM aqbasket
653           LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
654         WHERE booksellerid = ?};
655
656     unless ( $allbaskets ) {
657         $query.=" AND (closedate IS NULL OR (aqorders.quantity > aqorders.quantityreceived AND datecancellationprinted IS NULL))";
658     }
659     $query.=" GROUP BY aqbasket.basketno";
660
661     my $sth = $dbh->prepare($query);
662     $sth->execute($supplierid);
663     my $baskets = $sth->fetchall_arrayref({});
664
665     # Retrieve the number of biblios cancelled
666     my $cancelled_biblios = $dbh->selectall_hashref( q|
667         SELECT COUNT(DISTINCT(biblionumber)) AS total_biblios_cancelled, aqbasket.basketno
668         FROM aqbasket
669         LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
670         WHERE booksellerid = ?
671         AND aqorders.orderstatus = 'cancelled'
672         GROUP BY aqbasket.basketno
673     |, 'basketno', {}, $supplierid );
674     map {
675         $_->{total_biblios_cancelled} = $cancelled_biblios->{$_->{basketno}}{total_biblios_cancelled} || 0
676     } @$baskets;
677
678     return $baskets;
679 }
680
681 =head3 GetBasketUsers
682
683     $basketusers_ids = &GetBasketUsers($basketno);
684
685 Returns a list of all borrowernumbers that are in basket users list
686
687 =cut
688
689 sub GetBasketUsers {
690     my $basketno = shift;
691
692     return unless $basketno;
693
694     my $query = qq{
695         SELECT borrowernumber
696         FROM aqbasketusers
697         WHERE basketno = ?
698     };
699     my $dbh = C4::Context->dbh;
700     my $sth = $dbh->prepare($query);
701     $sth->execute($basketno);
702     my $results = $sth->fetchall_arrayref( {} );
703
704     my @borrowernumbers;
705     foreach (@$results) {
706         push @borrowernumbers, $_->{'borrowernumber'};
707     }
708
709     return @borrowernumbers;
710 }
711
712 =head3 ModBasketUsers
713
714     my @basketusers_ids = (1, 2, 3);
715     &ModBasketUsers($basketno, @basketusers_ids);
716
717 Delete all users from basket users list, and add users in C<@basketusers_ids>
718 to this users list.
719
720 =cut
721
722 sub ModBasketUsers {
723     my ($basketno, @basketusers_ids) = @_;
724
725     return unless $basketno;
726
727     my $dbh = C4::Context->dbh;
728     my $query = qq{
729         DELETE FROM aqbasketusers
730         WHERE basketno = ?
731     };
732     my $sth = $dbh->prepare($query);
733     $sth->execute($basketno);
734
735     $query = qq{
736         INSERT INTO aqbasketusers (basketno, borrowernumber)
737         VALUES (?, ?)
738     };
739     $sth = $dbh->prepare($query);
740     foreach my $basketuser_id (@basketusers_ids) {
741         $sth->execute($basketno, $basketuser_id);
742     }
743     return;
744 }
745
746 =head3 CanUserManageBasket
747
748     my $bool = CanUserManageBasket($borrower, $basket[, $userflags]);
749     my $bool = CanUserManageBasket($borrowernumber, $basketno[, $userflags]);
750
751 Check if a borrower can manage a basket, according to system preference
752 AcqViewBaskets, user permissions and basket properties (creator, users list,
753 branch).
754
755 First parameter can be either a borrowernumber or a hashref as returned by
756 C4::Members::GetMember.
757
758 Second parameter can be either a basketno or a hashref as returned by
759 C4::Acquisition::GetBasket.
760
761 The third parameter is optional. If given, it should be a hashref as returned
762 by C4::Auth::getuserflags. If not, getuserflags is called.
763
764 If user is authorised to manage basket, returns 1.
765 Otherwise returns 0.
766
767 =cut
768
769 sub CanUserManageBasket {
770     my ($borrower, $basket, $userflags) = @_;
771
772     if (!ref $borrower) {
773         $borrower = C4::Members::GetMember(borrowernumber => $borrower);
774     }
775     if (!ref $basket) {
776         $basket = GetBasket($basket);
777     }
778
779     return 0 unless ($basket and $borrower);
780
781     my $borrowernumber = $borrower->{borrowernumber};
782     my $basketno = $basket->{basketno};
783
784     my $AcqViewBaskets = C4::Context->preference('AcqViewBaskets');
785
786     if (!defined $userflags) {
787         my $dbh = C4::Context->dbh;
788         my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE borrowernumber = ?");
789         $sth->execute($borrowernumber);
790         my ($flags) = $sth->fetchrow_array;
791         $sth->finish;
792
793         $userflags = C4::Auth::getuserflags($flags, $borrower->{userid}, $dbh);
794     }
795
796     unless ($userflags->{superlibrarian}
797     || (ref $userflags->{acquisition} && $userflags->{acquisition}->{order_manage_all})
798     || (!ref $userflags->{acquisition} && $userflags->{acquisition}))
799     {
800         if (not exists $userflags->{acquisition}) {
801             return 0;
802         }
803
804         if ( (ref $userflags->{acquisition} && !$userflags->{acquisition}->{order_manage})
805         || (!ref $userflags->{acquisition} && !$userflags->{acquisition}) ) {
806             return 0;
807         }
808
809         if ($AcqViewBaskets eq 'user'
810         && $basket->{authorisedby} != $borrowernumber
811         && grep($borrowernumber, GetBasketUsers($basketno)) == 0) {
812             return 0;
813         }
814
815         if ($AcqViewBaskets eq 'branch' && defined $basket->{branch}
816         && $basket->{branch} ne $borrower->{branchcode}) {
817             return 0;
818         }
819     }
820
821     return 1;
822 }
823
824 #------------------------------------------------------------#
825
826 =head3 GetBasketsByBasketgroup
827
828   $baskets = &GetBasketsByBasketgroup($basketgroupid);
829
830 Returns a reference to all baskets that belong to basketgroup $basketgroupid.
831
832 =cut
833
834 sub GetBasketsByBasketgroup {
835     my $basketgroupid = shift;
836     my $query = qq{
837         SELECT *, aqbasket.booksellerid as booksellerid
838         FROM aqbasket
839         LEFT JOIN aqcontract USING(contractnumber) WHERE basketgroupid=?
840     };
841     my $dbh = C4::Context->dbh;
842     my $sth = $dbh->prepare($query);
843     $sth->execute($basketgroupid);
844     return $sth->fetchall_arrayref({});
845 }
846
847 #------------------------------------------------------------#
848
849 =head3 NewBasketgroup
850
851   $basketgroupid = NewBasketgroup(\%hashref);
852
853 Adds a basketgroup to the aqbasketgroups table, and add the initial baskets to it.
854
855 $hashref->{'booksellerid'} is the 'id' field of the bookseller in the aqbooksellers table,
856
857 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
858
859 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
860
861 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
862
863 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
864
865 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
866
867 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
868
869 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
870
871 =cut
872
873 sub NewBasketgroup {
874     my $basketgroupinfo = shift;
875     die "booksellerid is required to create a basketgroup" unless $basketgroupinfo->{'booksellerid'};
876     my $query = "INSERT INTO aqbasketgroups (";
877     my @params;
878     foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
879         if ( defined $basketgroupinfo->{$field} ) {
880             $query .= "$field, ";
881             push(@params, $basketgroupinfo->{$field});
882         }
883     }
884     $query .= "booksellerid) VALUES (";
885     foreach (@params) {
886         $query .= "?, ";
887     }
888     $query .= "?)";
889     push(@params, $basketgroupinfo->{'booksellerid'});
890     my $dbh = C4::Context->dbh;
891     my $sth = $dbh->prepare($query);
892     $sth->execute(@params);
893     my $basketgroupid = $dbh->{'mysql_insertid'};
894     if( $basketgroupinfo->{'basketlist'} ) {
895         foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
896             my $query2 = "UPDATE aqbasket SET basketgroupid=? WHERE basketno=?";
897             my $sth2 = $dbh->prepare($query2);
898             $sth2->execute($basketgroupid, $basketno);
899         }
900     }
901     return $basketgroupid;
902 }
903
904 #------------------------------------------------------------#
905
906 =head3 ModBasketgroup
907
908   ModBasketgroup(\%hashref);
909
910 Modifies a basketgroup in the aqbasketgroups table, and add the baskets to it.
911
912 $hashref->{'id'} is the 'id' field of the basketgroup in the aqbasketgroup table, this parameter is mandatory,
913
914 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
915
916 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
917
918 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
919
920 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
921
922 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
923
924 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
925
926 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
927
928 =cut
929
930 sub ModBasketgroup {
931     my $basketgroupinfo = shift;
932     die "basketgroup id is required to edit a basketgroup" unless $basketgroupinfo->{'id'};
933     my $dbh = C4::Context->dbh;
934     my $query = "UPDATE aqbasketgroups SET ";
935     my @params;
936     foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
937         if ( defined $basketgroupinfo->{$field} ) {
938             $query .= "$field=?, ";
939             push(@params, $basketgroupinfo->{$field});
940         }
941     }
942     chop($query);
943     chop($query);
944     $query .= " WHERE id=?";
945     push(@params, $basketgroupinfo->{'id'});
946     my $sth = $dbh->prepare($query);
947     $sth->execute(@params);
948
949     $sth = $dbh->prepare('UPDATE aqbasket SET basketgroupid = NULL WHERE basketgroupid = ?');
950     $sth->execute($basketgroupinfo->{'id'});
951
952     if($basketgroupinfo->{'basketlist'} && @{$basketgroupinfo->{'basketlist'}}){
953         $sth = $dbh->prepare("UPDATE aqbasket SET basketgroupid=? WHERE basketno=?");
954         foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
955             $sth->execute($basketgroupinfo->{'id'}, $basketno);
956         }
957     }
958     return;
959 }
960
961 #------------------------------------------------------------#
962
963 =head3 DelBasketgroup
964
965   DelBasketgroup($basketgroupid);
966
967 Deletes a basketgroup in the aqbasketgroups table, and removes the reference to it from the baskets,
968
969 =over
970
971 =item C<$basketgroupid> is the 'id' field of the basket in the aqbasketgroup table
972
973 =back
974
975 =cut
976
977 sub DelBasketgroup {
978     my $basketgroupid = shift;
979     die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
980     my $query = "DELETE FROM aqbasketgroups WHERE id=?";
981     my $dbh = C4::Context->dbh;
982     my $sth = $dbh->prepare($query);
983     $sth->execute($basketgroupid);
984     return;
985 }
986
987 #------------------------------------------------------------#
988
989
990 =head2 FUNCTIONS ABOUT ORDERS
991
992 =head3 GetBasketgroup
993
994   $basketgroup = &GetBasketgroup($basketgroupid);
995
996 Returns a reference to the hash containing all information about the basketgroup.
997
998 =cut
999
1000 sub GetBasketgroup {
1001     my $basketgroupid = shift;
1002     die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
1003     my $dbh = C4::Context->dbh;
1004     my $result_set = $dbh->selectall_arrayref(
1005         'SELECT * FROM aqbasketgroups WHERE id=?',
1006         { Slice => {} },
1007         $basketgroupid
1008     );
1009     return $result_set->[0];    # id is unique
1010 }
1011
1012 #------------------------------------------------------------#
1013
1014 =head3 GetBasketgroups
1015
1016   $basketgroups = &GetBasketgroups($booksellerid);
1017
1018 Returns a reference to the array of all the basketgroups of bookseller $booksellerid.
1019
1020 =cut
1021
1022 sub GetBasketgroups {
1023     my $booksellerid = shift;
1024     die 'bookseller id is required to edit a basketgroup' unless $booksellerid;
1025     my $query = 'SELECT * FROM aqbasketgroups WHERE booksellerid=? ORDER BY id DESC';
1026     my $dbh = C4::Context->dbh;
1027     my $sth = $dbh->prepare($query);
1028     $sth->execute($booksellerid);
1029     return $sth->fetchall_arrayref({});
1030 }
1031
1032 #------------------------------------------------------------#
1033
1034 =head2 FUNCTIONS ABOUT ORDERS
1035
1036 =head3 GetOrders
1037
1038   @orders = &GetOrders($basketnumber, $orderby);
1039
1040 Looks up the pending (non-cancelled) orders with the given basket
1041 number. If C<$booksellerID> is non-empty, only orders from that seller
1042 are returned.
1043
1044 return :
1045 C<&basket> returns a two-element array. C<@orders> is an array of
1046 references-to-hash, whose keys are the fields from the aqorders,
1047 biblio, and biblioitems tables in the Koha database.
1048
1049 =cut
1050
1051 sub GetOrders {
1052     my ( $basketno, $orderby ) = @_;
1053     return () unless $basketno;
1054     my $dbh   = C4::Context->dbh;
1055     my $query  ="
1056         SELECT biblio.*,biblioitems.*,
1057                 aqorders.*,
1058                 aqbudgets.*,
1059                 aqorders_transfers.ordernumber_from AS transferred_from,
1060                 aqorders_transfers.timestamp AS transferred_from_timestamp
1061         FROM    aqorders
1062             LEFT JOIN aqbudgets        ON aqbudgets.budget_id = aqorders.budget_id
1063             LEFT JOIN biblio           ON biblio.biblionumber = aqorders.biblionumber
1064             LEFT JOIN biblioitems      ON biblioitems.biblionumber =biblio.biblionumber
1065             LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1066         WHERE   basketno=?
1067             AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')
1068     ";
1069
1070     $orderby = "biblioitems.publishercode,biblio.title" unless $orderby;
1071     $query .= " ORDER BY $orderby";
1072     my $result_set =
1073       $dbh->selectall_arrayref( $query, { Slice => {} }, $basketno );
1074     return @{$result_set};
1075
1076 }
1077
1078 #------------------------------------------------------------#
1079 =head3 GetOrdersByBiblionumber
1080
1081   @orders = &GetOrdersByBiblionumber($biblionumber);
1082
1083 Looks up the orders with linked to a specific $biblionumber, including
1084 cancelled orders and received orders.
1085
1086 return :
1087 C<@orders> is an array of references-to-hash, whose keys are the
1088 fields from the aqorders, biblio, and biblioitems tables in the Koha database.
1089
1090 =cut
1091
1092 sub GetOrdersByBiblionumber {
1093     my $biblionumber = shift;
1094     return unless $biblionumber;
1095     my $dbh   = C4::Context->dbh;
1096     my $query  ="
1097         SELECT biblio.*,biblioitems.*,
1098                 aqorders.*,
1099                 aqbudgets.*
1100         FROM    aqorders
1101             LEFT JOIN aqbudgets        ON aqbudgets.budget_id = aqorders.budget_id
1102             LEFT JOIN biblio           ON biblio.biblionumber = aqorders.biblionumber
1103             LEFT JOIN biblioitems      ON biblioitems.biblionumber =biblio.biblionumber
1104         WHERE   aqorders.biblionumber=?
1105     ";
1106     my $result_set =
1107       $dbh->selectall_arrayref( $query, { Slice => {} }, $biblionumber );
1108     return @{$result_set};
1109
1110 }
1111
1112 #------------------------------------------------------------#
1113
1114 =head3 GetOrder
1115
1116   $order = &GetOrder($ordernumber);
1117
1118 Looks up an order by order number.
1119
1120 Returns a reference-to-hash describing the order. The keys of
1121 C<$order> are fields from the biblio, biblioitems, aqorders tables of the Koha database.
1122
1123 =cut
1124
1125 sub GetOrder {
1126     my ($ordernumber) = @_;
1127     return unless $ordernumber;
1128
1129     my $dbh      = C4::Context->dbh;
1130     my $query = qq{SELECT
1131                 aqorders.*,
1132                 biblio.title,
1133                 biblio.author,
1134                 aqbasket.basketname,
1135                 borrowers.branchcode,
1136                 biblioitems.publicationyear,
1137                 biblio.copyrightdate,
1138                 biblioitems.editionstatement,
1139                 biblioitems.isbn,
1140                 biblioitems.ean,
1141                 biblio.seriestitle,
1142                 biblioitems.publishercode,
1143                 aqorders.rrp              AS unitpricesupplier,
1144                 aqorders.ecost            AS unitpricelib,
1145                 aqorders.claims_count     AS claims_count,
1146                 aqorders.claimed_date     AS claimed_date,
1147                 aqbudgets.budget_name     AS budget,
1148                 aqbooksellers.name        AS supplier,
1149                 aqbooksellers.id          AS supplierid,
1150                 biblioitems.publishercode AS publisher,
1151                 ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
1152                 DATE(aqbasket.closedate)  AS orderdate,
1153                 aqorders.quantity - COALESCE(aqorders.quantityreceived,0)                 AS quantity_to_receive,
1154                 (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
1155                 DATEDIFF(CURDATE( ),closedate) AS latesince
1156                 FROM aqorders LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1157                 LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
1158                 LEFT JOIN aqbudgets ON aqorders.budget_id = aqbudgets.budget_id,
1159                 aqbasket LEFT JOIN borrowers  ON aqbasket.authorisedby = borrowers.borrowernumber
1160                 LEFT JOIN aqbooksellers       ON aqbasket.booksellerid = aqbooksellers.id
1161                 WHERE aqorders.basketno = aqbasket.basketno
1162                     AND ordernumber=?};
1163     my $result_set =
1164       $dbh->selectall_arrayref( $query, { Slice => {} }, $ordernumber );
1165
1166     # result_set assumed to contain 1 match
1167     return $result_set->[0];
1168 }
1169
1170 =head3 GetLastOrderNotReceivedFromSubscriptionid
1171
1172   $order = &GetLastOrderNotReceivedFromSubscriptionid($subscriptionid);
1173
1174 Returns a reference-to-hash describing the last order not received for a subscription.
1175
1176 =cut
1177
1178 sub GetLastOrderNotReceivedFromSubscriptionid {
1179     my ( $subscriptionid ) = @_;
1180     my $dbh                = C4::Context->dbh;
1181     my $query              = qq|
1182         SELECT * FROM aqorders
1183         LEFT JOIN subscription
1184             ON ( aqorders.subscriptionid = subscription.subscriptionid )
1185         WHERE aqorders.subscriptionid = ?
1186             AND aqorders.datereceived IS NULL
1187         LIMIT 1
1188     |;
1189     my $result_set =
1190       $dbh->selectall_arrayref( $query, { Slice => {} }, $subscriptionid );
1191
1192     # result_set assumed to contain 1 match
1193     return $result_set->[0];
1194 }
1195
1196 =head3 GetLastOrderReceivedFromSubscriptionid
1197
1198   $order = &GetLastOrderReceivedFromSubscriptionid($subscriptionid);
1199
1200 Returns a reference-to-hash describing the last order received for a subscription.
1201
1202 =cut
1203
1204 sub GetLastOrderReceivedFromSubscriptionid {
1205     my ( $subscriptionid ) = @_;
1206     my $dbh                = C4::Context->dbh;
1207     my $query              = qq|
1208         SELECT * FROM aqorders
1209         LEFT JOIN subscription
1210             ON ( aqorders.subscriptionid = subscription.subscriptionid )
1211         WHERE aqorders.subscriptionid = ?
1212             AND aqorders.datereceived =
1213                 (
1214                     SELECT MAX( aqorders.datereceived )
1215                     FROM aqorders
1216                     LEFT JOIN subscription
1217                         ON ( aqorders.subscriptionid = subscription.subscriptionid )
1218                         WHERE aqorders.subscriptionid = ?
1219                             AND aqorders.datereceived IS NOT NULL
1220                 )
1221         ORDER BY ordernumber DESC
1222         LIMIT 1
1223     |;
1224     my $result_set =
1225       $dbh->selectall_arrayref( $query, { Slice => {} }, $subscriptionid, $subscriptionid );
1226
1227     # result_set assumed to contain 1 match
1228     return $result_set->[0];
1229
1230 }
1231
1232 #------------------------------------------------------------#
1233
1234 =head3 ModOrder
1235
1236   &ModOrder(\%hashref);
1237
1238 Modifies an existing order. Updates the order with order number
1239 $hashref->{'ordernumber'} and biblionumber $hashref->{'biblionumber'}. All 
1240 other keys of the hash update the fields with the same name in the aqorders 
1241 table of the Koha database.
1242
1243 =cut
1244
1245 sub ModOrder {
1246     my $orderinfo = shift;
1247
1248     die "Ordernumber is required"     if $orderinfo->{'ordernumber'} eq  '' ;
1249     die "Biblionumber is required"  if  $orderinfo->{'biblionumber'} eq '';
1250
1251     my $dbh = C4::Context->dbh;
1252     my @params;
1253
1254     # update uncertainprice to an integer, just in case (under FF, checked boxes have the value "ON" by default)
1255     $orderinfo->{uncertainprice}=1 if $orderinfo->{uncertainprice};
1256
1257 #    delete($orderinfo->{'branchcode'});
1258     # the hash contains a lot of entries not in aqorders, so get the columns ...
1259     my $sth = $dbh->prepare("SELECT * FROM aqorders LIMIT 1;");
1260     $sth->execute;
1261     my $colnames = $sth->{NAME};
1262         #FIXME Be careful. If aqorders would have columns with diacritics,
1263         #you should need to decode what you get back from NAME.
1264         #See report 10110 and guided_reports.pl
1265     my $query = "UPDATE aqorders SET ";
1266
1267     foreach my $orderinfokey (grep(!/ordernumber/, keys %$orderinfo)){
1268         # ... and skip hash entries that are not in the aqorders table
1269         # FIXME : probably not the best way to do it (would be better to have a correct hash)
1270         next unless grep(/^$orderinfokey$/, @$colnames);
1271             $query .= "$orderinfokey=?, ";
1272             push(@params, $orderinfo->{$orderinfokey});
1273     }
1274
1275     $query .= "timestamp=NOW()  WHERE  ordernumber=?";
1276     push(@params, $orderinfo->{'ordernumber'} );
1277     $sth = $dbh->prepare($query);
1278     $sth->execute(@params);
1279     return;
1280 }
1281
1282 #------------------------------------------------------------#
1283
1284 =head3 ModItemOrder
1285
1286     ModItemOrder($itemnumber, $ordernumber);
1287
1288 Modifies the ordernumber of an item in aqorders_items.
1289
1290 =cut
1291
1292 sub ModItemOrder {
1293     my ($itemnumber, $ordernumber) = @_;
1294
1295     return unless ($itemnumber and $ordernumber);
1296
1297     my $dbh = C4::Context->dbh;
1298     my $query = qq{
1299         UPDATE aqorders_items
1300         SET ordernumber = ?
1301         WHERE itemnumber = ?
1302     };
1303     my $sth = $dbh->prepare($query);
1304     return $sth->execute($ordernumber, $itemnumber);
1305 }
1306
1307 #------------------------------------------------------------#
1308
1309 =head3 GetCancelledOrders
1310
1311   my @orders = GetCancelledOrders($basketno, $orderby);
1312
1313 Returns cancelled orders for a basket
1314
1315 =cut
1316
1317 sub GetCancelledOrders {
1318     my ( $basketno, $orderby ) = @_;
1319
1320     return () unless $basketno;
1321
1322     my $dbh   = C4::Context->dbh;
1323     my $query = "
1324         SELECT
1325             biblio.*,
1326             biblioitems.*,
1327             aqorders.*,
1328             aqbudgets.*,
1329             aqorders_transfers.ordernumber_to AS transferred_to,
1330             aqorders_transfers.timestamp AS transferred_to_timestamp
1331         FROM aqorders
1332           LEFT JOIN aqbudgets   ON aqbudgets.budget_id = aqorders.budget_id
1333           LEFT JOIN biblio      ON biblio.biblionumber = aqorders.biblionumber
1334           LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
1335           LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_from = aqorders.ordernumber
1336         WHERE basketno = ?
1337           AND (datecancellationprinted IS NOT NULL
1338                AND datecancellationprinted <> '0000-00-00')
1339     ";
1340
1341     $orderby = "aqorders.datecancellationprinted desc, aqorders.timestamp desc"
1342         unless $orderby;
1343     $query .= " ORDER BY $orderby";
1344     my $sth = $dbh->prepare($query);
1345     $sth->execute($basketno);
1346     my $results = $sth->fetchall_arrayref( {} );
1347
1348     return @$results;
1349 }
1350
1351
1352 #------------------------------------------------------------#
1353
1354 =head3 ModReceiveOrder
1355
1356   &ModReceiveOrder({
1357     biblionumber => $biblionumber,
1358     ordernumber => $ordernumber,
1359     quantityreceived => $quantityreceived,
1360     user => $user,
1361     cost => $cost,
1362     ecost => $ecost,
1363     invoiceid => $invoiceid,
1364     rrp => $rrp,
1365     budget_id => $budget_id,
1366     datereceived => $datereceived,
1367     received_itemnumbers => \@received_itemnumbers,
1368     order_internalnote => $order_internalnote,
1369     order_vendornote => $order_vendornote,
1370    });
1371
1372 Updates an order, to reflect the fact that it was received, at least
1373 in part. All arguments not mentioned below update the fields with the
1374 same name in the aqorders table of the Koha database.
1375
1376 If a partial order is received, splits the order into two.
1377
1378 Updates the order with bibilionumber C<$biblionumber> and ordernumber
1379 C<$ordernumber>.
1380
1381 =cut
1382
1383
1384 sub ModReceiveOrder {
1385     my ( $params ) = @_;
1386     my $biblionumber = $params->{biblionumber};
1387     my $ordernumber = $params->{ordernumber};
1388     my $quantrec = $params->{quantityreceived};
1389     my $user = $params->{user};
1390     my $cost = $params->{cost};
1391     my $ecost = $params->{ecost};
1392     my $invoiceid = $params->{invoiceid};
1393     my $rrp = $params->{rrp};
1394     my $budget_id = $params->{budget_id};
1395     my $datereceived = $params->{datereceived};
1396     my $received_items = $params->{received_items};
1397     my $order_internalnote = $params->{order_internalnote};
1398     my $order_vendornote = $params->{order_vendornote};
1399
1400     my $dbh = C4::Context->dbh;
1401     $datereceived = C4::Dates->output('iso') unless $datereceived;
1402     my $suggestionid = GetSuggestionFromBiblionumber( $biblionumber );
1403     if ($suggestionid) {
1404         ModSuggestion( {suggestionid=>$suggestionid,
1405                         STATUS=>'AVAILABLE',
1406                         biblionumber=> $biblionumber}
1407                         );
1408     }
1409
1410     my $result_set = $dbh->selectall_arrayref(
1411 q{SELECT * FROM aqorders WHERE biblionumber=? AND aqorders.ordernumber=?},
1412         { Slice => {} }, $biblionumber, $ordernumber
1413     );
1414
1415     # we assume we have a unique order
1416     my $order = $result_set->[0];
1417
1418     my $new_ordernumber = $ordernumber;
1419     if ( $order->{quantity} > $quantrec ) {
1420         # Split order line in two parts: the first is the original order line
1421         # without received items (the quantity is decreased),
1422         # the second part is a new order line with quantity=quantityrec
1423         # (entirely received)
1424         my $query = q|
1425             UPDATE aqorders
1426             SET quantity = ?,
1427                 orderstatus = 'partial'|;
1428         $query .= q|, order_internalnote = ?| if defined $order_internalnote;
1429         $query .= q|, order_vendornote = ?| if defined $order_vendornote;
1430         $query .= q| WHERE ordernumber = ?|;
1431         my $sth = $dbh->prepare($query);
1432
1433         $sth->execute(
1434             $order->{quantity} - $quantrec,
1435             ( defined $order_internalnote ? $order_internalnote : () ),
1436             ( defined $order_vendornote ? $order_vendornote : () ),
1437             $ordernumber
1438         );
1439
1440         delete $order->{'ordernumber'};
1441         $order->{'budget_id'} = ( $budget_id || $order->{'budget_id'} );
1442         $order->{'quantity'} = $quantrec;
1443         $order->{'quantityreceived'} = $quantrec;
1444         $order->{'datereceived'} = $datereceived;
1445         $order->{'invoiceid'} = $invoiceid;
1446         $order->{'unitprice'} = $cost;
1447         $order->{'rrp'} = $rrp;
1448         $order->{ecost} = $ecost;
1449         $order->{'orderstatus'} = 'complete';
1450         $new_ordernumber = Koha::Acquisition::Order->new($order)->insert->{ordernumber};
1451
1452         if ($received_items) {
1453             foreach my $itemnumber (@$received_items) {
1454                 ModItemOrder($itemnumber, $new_ordernumber);
1455             }
1456         }
1457     } else {
1458         my $query = q|
1459             update aqorders
1460             set quantityreceived=?,datereceived=?,invoiceid=?,
1461                 unitprice=?,rrp=?,ecost=?,budget_id=?,orderstatus='complete'|;
1462         $query .= q|, order_internalnote = ?| if defined $order_internalnote;
1463         $query .= q|, order_vendornote = ?| if defined $order_vendornote;
1464         $query .= q| where biblionumber=? and ordernumber=?|;
1465         my $sth = $dbh->prepare( $query );
1466         $sth->execute(
1467             $quantrec,
1468             $datereceived,
1469             $invoiceid,
1470             $cost,
1471             $rrp,
1472             $ecost,
1473             $budget_id,
1474             ( defined $order_internalnote ? $order_internalnote : () ),
1475             ( defined $order_vendornote ? $order_vendornote : () ),
1476             $biblionumber,
1477             $ordernumber
1478         );
1479     }
1480     return ($datereceived, $new_ordernumber);
1481 }
1482
1483 =head3 CancelReceipt
1484
1485     my $parent_ordernumber = CancelReceipt($ordernumber);
1486
1487     Cancel an order line receipt and update the parent order line, as if no
1488     receipt was made.
1489     If items are created at receipt (AcqCreateItem = receiving) then delete
1490     these items.
1491
1492 =cut
1493
1494 sub CancelReceipt {
1495     my $ordernumber = shift;
1496
1497     return unless $ordernumber;
1498
1499     my $dbh = C4::Context->dbh;
1500     my $query = qq{
1501         SELECT datereceived, parent_ordernumber, quantity
1502         FROM aqorders
1503         WHERE ordernumber = ?
1504     };
1505     my $sth = $dbh->prepare($query);
1506     $sth->execute($ordernumber);
1507     my $order = $sth->fetchrow_hashref;
1508     unless($order) {
1509         warn "CancelReceipt: order $ordernumber does not exist";
1510         return;
1511     }
1512     unless($order->{'datereceived'}) {
1513         warn "CancelReceipt: order $ordernumber is not received";
1514         return;
1515     }
1516
1517     my $parent_ordernumber = $order->{'parent_ordernumber'};
1518
1519     my @itemnumbers = GetItemnumbersFromOrder( $ordernumber );
1520
1521     if($parent_ordernumber == $ordernumber || not $parent_ordernumber) {
1522         # The order line has no parent, just mark it as not received
1523         $query = qq{
1524             UPDATE aqorders
1525             SET quantityreceived = ?,
1526                 datereceived = ?,
1527                 invoiceid = ?,
1528                 orderstatus = 'ordered'
1529             WHERE ordernumber = ?
1530         };
1531         $sth = $dbh->prepare($query);
1532         $sth->execute(0, undef, undef, $ordernumber);
1533         _cancel_items_receipt( $ordernumber );
1534     } else {
1535         # The order line has a parent, increase parent quantity and delete
1536         # the order line.
1537         $query = qq{
1538             SELECT quantity, datereceived
1539             FROM aqorders
1540             WHERE ordernumber = ?
1541         };
1542         $sth = $dbh->prepare($query);
1543         $sth->execute($parent_ordernumber);
1544         my $parent_order = $sth->fetchrow_hashref;
1545         unless($parent_order) {
1546             warn "Parent order $parent_ordernumber does not exist.";
1547             return;
1548         }
1549         if($parent_order->{'datereceived'}) {
1550             warn "CancelReceipt: parent order is received.".
1551                 " Can't cancel receipt.";
1552             return;
1553         }
1554         $query = qq{
1555             UPDATE aqorders
1556             SET quantity = ?,
1557                 orderstatus = 'ordered'
1558             WHERE ordernumber = ?
1559         };
1560         $sth = $dbh->prepare($query);
1561         my $rv = $sth->execute(
1562             $order->{'quantity'} + $parent_order->{'quantity'},
1563             $parent_ordernumber
1564         );
1565         unless($rv) {
1566             warn "Cannot update parent order line, so do not cancel".
1567                 " receipt";
1568             return;
1569         }
1570         _cancel_items_receipt( $ordernumber, $parent_ordernumber );
1571         # Delete order line
1572         $query = qq{
1573             DELETE FROM aqorders
1574             WHERE ordernumber = ?
1575         };
1576         $sth = $dbh->prepare($query);
1577         $sth->execute($ordernumber);
1578
1579     }
1580
1581     if(C4::Context->preference('AcqCreateItem') eq 'ordering') {
1582         my @affects = split q{\|}, C4::Context->preference("AcqItemSetSubfieldsWhenReceiptIsCancelled");
1583         if ( @affects ) {
1584             for my $in ( @itemnumbers ) {
1585                 my $biblionumber = C4::Biblio::GetBiblionumberFromItemnumber( $in );
1586                 my $frameworkcode = GetFrameworkCode($biblionumber);
1587                 my ( $itemfield ) = GetMarcFromKohaField( 'items.itemnumber', $frameworkcode );
1588                 my $item = C4::Items::GetMarcItem( $biblionumber, $in );
1589                 for my $affect ( @affects ) {
1590                     my ( $sf, $v ) = split q{=}, $affect, 2;
1591                     foreach ( $item->field($itemfield) ) {
1592                         $_->update( $sf => $v );
1593                     }
1594                 }
1595                 C4::Items::ModItemFromMarc( $item, $biblionumber, $in );
1596             }
1597         }
1598     }
1599
1600     return $parent_ordernumber;
1601 }
1602
1603 sub _cancel_items_receipt {
1604     my ( $ordernumber, $parent_ordernumber ) = @_;
1605     $parent_ordernumber ||= $ordernumber;
1606
1607     my @itemnumbers = GetItemnumbersFromOrder($ordernumber);
1608     if(C4::Context->preference('AcqCreateItem') eq 'receiving') {
1609         # Remove items that were created at receipt
1610         my $query = qq{
1611             DELETE FROM items, aqorders_items
1612             USING items, aqorders_items
1613             WHERE items.itemnumber = ? AND aqorders_items.itemnumber = ?
1614         };
1615         my $dbh = C4::Context->dbh;
1616         my $sth = $dbh->prepare($query);
1617         foreach my $itemnumber (@itemnumbers) {
1618             $sth->execute($itemnumber, $itemnumber);
1619         }
1620     } else {
1621         # Update items
1622         foreach my $itemnumber (@itemnumbers) {
1623             ModItemOrder($itemnumber, $parent_ordernumber);
1624         }
1625     }
1626 }
1627
1628 #------------------------------------------------------------#
1629
1630 =head3 SearchOrders
1631
1632 @results = &SearchOrders({
1633     ordernumber => $ordernumber,
1634     search => $search,
1635     biblionumber => $biblionumber,
1636     ean => $ean,
1637     booksellerid => $booksellerid,
1638     basketno => $basketno,
1639     owner => $owner,
1640     pending => $pending
1641     ordered => $ordered
1642 });
1643
1644 Searches for orders.
1645
1646 C<$owner> Finds order for the logged in user.
1647 C<$pending> Finds pending orders. Ignores completed and cancelled orders.
1648 C<$ordered> Finds orders to receive only (status 'ordered' or 'partial').
1649
1650
1651 C<@results> is an array of references-to-hash with the keys are fields
1652 from aqorders, biblio, biblioitems and aqbasket tables.
1653
1654 =cut
1655
1656 sub SearchOrders {
1657     my ( $params ) = @_;
1658     my $ordernumber = $params->{ordernumber};
1659     my $search = $params->{search};
1660     my $ean = $params->{ean};
1661     my $booksellerid = $params->{booksellerid};
1662     my $basketno = $params->{basketno};
1663     my $basketname = $params->{basketname};
1664     my $basketgroupname = $params->{basketgroupname};
1665     my $owner = $params->{owner};
1666     my $pending = $params->{pending};
1667     my $ordered = $params->{ordered};
1668     my $biblionumber = $params->{biblionumber};
1669     my $budget_id = $params->{budget_id};
1670
1671     my $dbh = C4::Context->dbh;
1672     my @args = ();
1673     my $query = q{
1674         SELECT aqbasket.basketno,
1675                borrowers.surname,
1676                borrowers.firstname,
1677                biblio.*,
1678                biblioitems.isbn,
1679                biblioitems.biblioitemnumber,
1680                aqbasket.authorisedby,
1681                aqbasket.booksellerid,
1682                aqbasket.closedate,
1683                aqbasket.creationdate,
1684                aqbasket.basketname,
1685                aqbasketgroups.id as basketgroupid,
1686                aqbasketgroups.name as basketgroupname,
1687                aqorders.*
1688         FROM aqorders
1689             LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
1690             LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
1691             LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
1692             LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
1693             LEFT JOIN biblioitems ON biblioitems.biblionumber=biblio.biblionumber
1694     };
1695
1696     # If we search on ordernumber, we retrieve the transfered order if a transfer has been done.
1697     $query .= q{
1698             LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1699     } if $ordernumber;
1700
1701     $query .= q{
1702         WHERE (datecancellationprinted is NULL)
1703     };
1704
1705     if ( $pending or $ordered ) {
1706         $query .= q{ AND (quantity > quantityreceived OR quantityreceived is NULL)};
1707     }
1708     if ( $ordered ) {
1709         $query .= q{ AND aqorders.orderstatus IN ( "ordered", "partial" )};
1710     }
1711
1712     my $userenv = C4::Context->userenv;
1713     if ( C4::Context->preference("IndependentBranches") ) {
1714         unless ( C4::Context->IsSuperLibrarian() ) {
1715             $query .= q{
1716                 AND (
1717                     borrowers.branchcode = ?
1718                     OR borrowers.branchcode  = ''
1719                 )
1720             };
1721             push @args, $userenv->{branch};
1722         }
1723     }
1724
1725     if ( $ordernumber ) {
1726         $query .= ' AND ( aqorders.ordernumber = ? OR aqorders_transfers.ordernumber_from = ? ) ';
1727         push @args, ( $ordernumber, $ordernumber );
1728     }
1729     if ( $biblionumber ) {
1730         $query .= 'AND aqorders.biblionumber = ?';
1731         push @args, $biblionumber;
1732     }
1733     if( $search ) {
1734         $query .= ' AND (biblio.title LIKE ? OR biblio.author LIKE ? OR biblioitems.isbn LIKE ?)';
1735         push @args, ("%$search%","%$search%","%$search%");
1736     }
1737     if ( $ean ) {
1738         $query .= ' AND biblioitems.ean = ?';
1739         push @args, $ean;
1740     }
1741     if ( $booksellerid ) {
1742         $query .= 'AND aqbasket.booksellerid = ?';
1743         push @args, $booksellerid;
1744     }
1745     if( $basketno ) {
1746         $query .= 'AND aqbasket.basketno = ?';
1747         push @args, $basketno;
1748     }
1749     if( $basketname ) {
1750         $query .= 'AND aqbasket.basketname LIKE ?';
1751         push @args, "%$basketname%";
1752     }
1753     if( $basketgroupname ) {
1754         $query .= ' AND aqbasketgroups.name LIKE ?';
1755         push @args, "%$basketgroupname%";
1756     }
1757
1758     if ( $owner ) {
1759         $query .= ' AND aqbasket.authorisedby=? ';
1760         push @args, $userenv->{'number'};
1761     }
1762
1763     if ( $budget_id ) {
1764         $query .= ' AND aqorders.budget_id = ?';
1765         push @args, $budget_id;
1766     }
1767
1768     $query .= ' ORDER BY aqbasket.basketno';
1769
1770     my $sth = $dbh->prepare($query);
1771     $sth->execute(@args);
1772     return $sth->fetchall_arrayref({});
1773 }
1774
1775 #------------------------------------------------------------#
1776
1777 =head3 DelOrder
1778
1779   &DelOrder($biblionumber, $ordernumber);
1780
1781 Cancel the order with the given order and biblio numbers. It does not
1782 delete any entries in the aqorders table, it merely marks them as
1783 cancelled.
1784
1785 =cut
1786
1787 sub DelOrder {
1788     my ( $bibnum, $ordernumber, $delete_biblio, $reason ) = @_;
1789
1790     my $error;
1791     my $dbh = C4::Context->dbh;
1792     my $query = "
1793         UPDATE aqorders
1794         SET    datecancellationprinted=now(), orderstatus='cancelled'
1795     ";
1796     if($reason) {
1797         $query .= ", cancellationreason = ? ";
1798     }
1799     $query .= "
1800         WHERE biblionumber=? AND ordernumber=?
1801     ";
1802     my $sth = $dbh->prepare($query);
1803     if($reason) {
1804         $sth->execute($reason, $bibnum, $ordernumber);
1805     } else {
1806         $sth->execute( $bibnum, $ordernumber );
1807     }
1808     $sth->finish;
1809
1810     my @itemnumbers = GetItemnumbersFromOrder( $ordernumber );
1811     foreach my $itemnumber (@itemnumbers){
1812         my $delcheck = C4::Items::DelItemCheck( $dbh, $bibnum, $itemnumber );
1813
1814         if($delcheck != 1) {
1815             $error->{'delitem'} = 1;
1816         }
1817     }
1818
1819     if($delete_biblio) {
1820         # We get the number of remaining items
1821         my $itemcount = C4::Items::GetItemsCount($bibnum);
1822
1823         # If there are no items left,
1824         if ( $itemcount == 0 ) {
1825             # We delete the record
1826             my $delcheck = DelBiblio($bibnum);
1827
1828             if($delcheck) {
1829                 $error->{'delbiblio'} = 1;
1830             }
1831         }
1832     }
1833
1834     return $error;
1835 }
1836
1837 =head3 TransferOrder
1838
1839     my $newordernumber = TransferOrder($ordernumber, $basketno);
1840
1841 Transfer an order line to a basket.
1842 Mark $ordernumber as cancelled with an internal note 'Cancelled and transfered
1843 to BOOKSELLER on DATE' and create new order with internal note
1844 'Transfered from BOOKSELLER on DATE'.
1845 Move all attached items to the new order.
1846 Received orders cannot be transfered.
1847 Return the ordernumber of created order.
1848
1849 =cut
1850
1851 sub TransferOrder {
1852     my ($ordernumber, $basketno) = @_;
1853
1854     return unless ($ordernumber and $basketno);
1855
1856     my $order = GetOrder( $ordernumber );
1857     return if $order->{datereceived};
1858     my $basket = GetBasket($basketno);
1859     return unless $basket;
1860
1861     my $dbh = C4::Context->dbh;
1862     my ($query, $sth, $rv);
1863
1864     $query = q{
1865         UPDATE aqorders
1866         SET datecancellationprinted = CAST(NOW() AS date)
1867         WHERE ordernumber = ?
1868     };
1869     $sth = $dbh->prepare($query);
1870     $rv = $sth->execute($ordernumber);
1871
1872     delete $order->{'ordernumber'};
1873     delete $order->{parent_ordernumber};
1874     $order->{'basketno'} = $basketno;
1875
1876     my $newordernumber = Koha::Acquisition::Order->new($order)->insert->{ordernumber};
1877
1878     $query = q{
1879         UPDATE aqorders_items
1880         SET ordernumber = ?
1881         WHERE ordernumber = ?
1882     };
1883     $sth = $dbh->prepare($query);
1884     $sth->execute($newordernumber, $ordernumber);
1885
1886     $query = q{
1887         INSERT INTO aqorders_transfers (ordernumber_from, ordernumber_to)
1888         VALUES (?, ?)
1889     };
1890     $sth = $dbh->prepare($query);
1891     $sth->execute($ordernumber, $newordernumber);
1892
1893     return $newordernumber;
1894 }
1895
1896 =head2 FUNCTIONS ABOUT PARCELS
1897
1898 =cut
1899
1900 #------------------------------------------------------------#
1901
1902 =head3 GetParcel
1903
1904   @results = &GetParcel($booksellerid, $code, $date);
1905
1906 Looks up all of the received items from the supplier with the given
1907 bookseller ID at the given date, for the given code (bookseller Invoice number). Ignores cancelled and completed orders.
1908
1909 C<@results> is an array of references-to-hash. The keys of each element are fields from
1910 the aqorders, biblio, and biblioitems tables of the Koha database.
1911
1912 C<@results> is sorted alphabetically by book title.
1913
1914 =cut
1915
1916 sub GetParcel {
1917     #gets all orders from a certain supplier, orders them alphabetically
1918     my ( $supplierid, $code, $datereceived ) = @_;
1919     my $dbh     = C4::Context->dbh;
1920     my @results = ();
1921     $code .= '%'
1922     if $code;  # add % if we search on a given code (otherwise, let him empty)
1923     my $strsth ="
1924         SELECT  authorisedby,
1925                 creationdate,
1926                 aqbasket.basketno,
1927                 closedate,surname,
1928                 firstname,
1929                 aqorders.biblionumber,
1930                 aqorders.ordernumber,
1931                 aqorders.parent_ordernumber,
1932                 aqorders.quantity,
1933                 aqorders.quantityreceived,
1934                 aqorders.unitprice,
1935                 aqorders.listprice,
1936                 aqorders.rrp,
1937                 aqorders.ecost,
1938                 aqorders.gstrate,
1939                 biblio.title
1940         FROM aqorders
1941         LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
1942         LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
1943         LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
1944         LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
1945         WHERE
1946             aqbasket.booksellerid = ?
1947             AND aqinvoices.invoicenumber LIKE ?
1948             AND aqorders.datereceived = ? ";
1949
1950     my @query_params = ( $supplierid, $code, $datereceived );
1951     if ( C4::Context->preference("IndependentBranches") ) {
1952         unless ( C4::Context->IsSuperLibrarian() ) {
1953             $strsth .= " and (borrowers.branchcode = ?
1954                         or borrowers.branchcode  = '')";
1955             push @query_params, C4::Context->userenv->{branch};
1956         }
1957     }
1958     $strsth .= " ORDER BY aqbasket.basketno";
1959     my $result_set = $dbh->selectall_arrayref(
1960         $strsth,
1961         { Slice => {} },
1962         @query_params);
1963
1964     return @{$result_set};
1965 }
1966
1967 #------------------------------------------------------------#
1968
1969 =head3 GetParcels
1970
1971   $results = &GetParcels($bookseller, $order, $code, $datefrom, $dateto);
1972
1973 get a lists of parcels.
1974
1975 * Input arg :
1976
1977 =over
1978
1979 =item $bookseller
1980 is the bookseller this function has to get parcels.
1981
1982 =item $order
1983 To know on what criteria the results list has to be ordered.
1984
1985 =item $code
1986 is the booksellerinvoicenumber.
1987
1988 =item $datefrom & $dateto
1989 to know on what date this function has to filter its search.
1990
1991 =back
1992
1993 * return:
1994 a pointer on a hash list containing parcel informations as such :
1995
1996 =over
1997
1998 =item Creation date
1999
2000 =item Last operation
2001
2002 =item Number of biblio
2003
2004 =item Number of items
2005
2006 =back
2007
2008 =cut
2009
2010 sub GetParcels {
2011     my ($bookseller,$order, $code, $datefrom, $dateto) = @_;
2012     my $dbh    = C4::Context->dbh;
2013     my @query_params = ();
2014     my $strsth ="
2015         SELECT  aqinvoices.invoicenumber,
2016                 datereceived,purchaseordernumber,
2017                 count(DISTINCT biblionumber) AS biblio,
2018                 sum(quantity) AS itemsexpected,
2019                 sum(quantityreceived) AS itemsreceived
2020         FROM   aqorders LEFT JOIN aqbasket ON aqbasket.basketno = aqorders.basketno
2021         LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
2022         WHERE aqbasket.booksellerid = ? and datereceived IS NOT NULL
2023     ";
2024     push @query_params, $bookseller;
2025
2026     if ( defined $code ) {
2027         $strsth .= ' and aqinvoices.invoicenumber like ? ';
2028         # add a % to the end of the code to allow stemming.
2029         push @query_params, "$code%";
2030     }
2031
2032     if ( defined $datefrom ) {
2033         $strsth .= ' and datereceived >= ? ';
2034         push @query_params, $datefrom;
2035     }
2036
2037     if ( defined $dateto ) {
2038         $strsth .=  'and datereceived <= ? ';
2039         push @query_params, $dateto;
2040     }
2041
2042     $strsth .= "group by aqinvoices.invoicenumber,datereceived ";
2043
2044     # can't use a placeholder to place this column name.
2045     # but, we could probably be checking to make sure it is a column that will be fetched.
2046     $strsth .= "order by $order " if ($order);
2047
2048     my $sth = $dbh->prepare($strsth);
2049
2050     $sth->execute( @query_params );
2051     my $results = $sth->fetchall_arrayref({});
2052     return @{$results};
2053 }
2054
2055 #------------------------------------------------------------#
2056
2057 =head3 GetLateOrders
2058
2059   @results = &GetLateOrders;
2060
2061 Searches for bookseller with late orders.
2062
2063 return:
2064 the table of supplier with late issues. This table is full of hashref.
2065
2066 =cut
2067
2068 sub GetLateOrders {
2069     my $delay      = shift;
2070     my $supplierid = shift;
2071     my $branch     = shift;
2072     my $estimateddeliverydatefrom = shift;
2073     my $estimateddeliverydateto = shift;
2074
2075     my $dbh = C4::Context->dbh;
2076
2077     #BEWARE, order of parenthesis and LEFT JOIN is important for speed
2078     my $dbdriver = C4::Context->config("db_scheme") || "mysql";
2079
2080     my @query_params = ();
2081     my $select = "
2082     SELECT aqbasket.basketno,
2083         aqorders.ordernumber,
2084         DATE(aqbasket.closedate)  AS orderdate,
2085         aqbasket.basketname       AS basketname,
2086         aqbasket.basketgroupid    AS basketgroupid,
2087         aqbasketgroups.name       AS basketgroupname,
2088         aqorders.rrp              AS unitpricesupplier,
2089         aqorders.ecost            AS unitpricelib,
2090         aqorders.claims_count     AS claims_count,
2091         aqorders.claimed_date     AS claimed_date,
2092         aqbudgets.budget_name     AS budget,
2093         borrowers.branchcode      AS branch,
2094         aqbooksellers.name        AS supplier,
2095         aqbooksellers.id          AS supplierid,
2096         biblio.author, biblio.title,
2097         biblioitems.publishercode AS publisher,
2098         biblioitems.publicationyear,
2099         ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
2100     ";
2101     my $from = "
2102     FROM
2103         aqorders LEFT JOIN biblio     ON biblio.biblionumber         = aqorders.biblionumber
2104         LEFT JOIN biblioitems         ON biblioitems.biblionumber    = biblio.biblionumber
2105         LEFT JOIN aqbudgets           ON aqorders.budget_id          = aqbudgets.budget_id,
2106         aqbasket LEFT JOIN borrowers  ON aqbasket.authorisedby       = borrowers.borrowernumber
2107         LEFT JOIN aqbooksellers       ON aqbasket.booksellerid       = aqbooksellers.id
2108         LEFT JOIN aqbasketgroups      ON aqbasket.basketgroupid      = aqbasketgroups.id
2109         WHERE aqorders.basketno = aqbasket.basketno
2110         AND ( datereceived = ''
2111             OR datereceived IS NULL
2112             OR aqorders.quantityreceived < aqorders.quantity
2113         )
2114         AND aqbasket.closedate IS NOT NULL
2115         AND (aqorders.datecancellationprinted IS NULL OR aqorders.datecancellationprinted='0000-00-00')
2116     ";
2117     my $having = "";
2118     if ($dbdriver eq "mysql") {
2119         $select .= "
2120         aqorders.quantity - COALESCE(aqorders.quantityreceived,0)                 AS quantity,
2121         (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
2122         DATEDIFF(CAST(now() AS date),closedate) AS latesince
2123         ";
2124         if ( defined $delay ) {
2125             $from .= " AND (closedate <= DATE_SUB(CAST(now() AS date),INTERVAL ? DAY)) " ;
2126             push @query_params, $delay;
2127         }
2128         $having = "
2129         HAVING quantity          <> 0
2130             AND unitpricesupplier <> 0
2131             AND unitpricelib      <> 0
2132         ";
2133     } else {
2134         # FIXME: account for IFNULL as above
2135         $select .= "
2136                 aqorders.quantity                AS quantity,
2137                 aqorders.quantity * aqorders.rrp AS subtotal,
2138                 (CAST(now() AS date) - closedate)            AS latesince
2139         ";
2140         if ( defined $delay ) {
2141             $from .= " AND (closedate <= (CAST(now() AS date) -(INTERVAL ? DAY)) ";
2142             push @query_params, $delay;
2143         }
2144     }
2145     if (defined $supplierid) {
2146         $from .= ' AND aqbasket.booksellerid = ? ';
2147         push @query_params, $supplierid;
2148     }
2149     if (defined $branch) {
2150         $from .= ' AND borrowers.branchcode LIKE ? ';
2151         push @query_params, $branch;
2152     }
2153
2154     if ( defined $estimateddeliverydatefrom or defined $estimateddeliverydateto ) {
2155         $from .= ' AND aqbooksellers.deliverytime IS NOT NULL ';
2156     }
2157     if ( defined $estimateddeliverydatefrom ) {
2158         $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) >= ?';
2159         push @query_params, $estimateddeliverydatefrom;
2160     }
2161     if ( defined $estimateddeliverydateto ) {
2162         $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) <= ?';
2163         push @query_params, $estimateddeliverydateto;
2164     }
2165     if ( defined $estimateddeliverydatefrom and not defined $estimateddeliverydateto ) {
2166         $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) <= CAST(now() AS date)';
2167     }
2168     if (C4::Context->preference("IndependentBranches")
2169             && !C4::Context->IsSuperLibrarian() ) {
2170         $from .= ' AND borrowers.branchcode LIKE ? ';
2171         push @query_params, C4::Context->userenv->{branch};
2172     }
2173     $from .= " AND orderstatus <> 'cancelled' ";
2174     my $query = "$select $from $having\nORDER BY latesince, basketno, borrowers.branchcode, supplier";
2175     $debug and print STDERR "GetLateOrders query: $query\nGetLateOrders args: " . join(" ",@query_params);
2176     my $sth = $dbh->prepare($query);
2177     $sth->execute(@query_params);
2178     my @results;
2179     while (my $data = $sth->fetchrow_hashref) {
2180         push @results, $data;
2181     }
2182     return @results;
2183 }
2184
2185 #------------------------------------------------------------#
2186
2187 =head3 GetHistory
2188
2189   \@order_loop = GetHistory( %params );
2190
2191 Retreives some acquisition history information
2192
2193 params:  
2194   title
2195   author
2196   name
2197   isbn
2198   ean
2199   from_placed_on
2200   to_placed_on
2201   basket                  - search both basket name and number
2202   booksellerinvoicenumber 
2203   basketgroupname
2204   budget
2205   orderstatus (note that orderstatus '' will retrieve orders
2206                of any status except cancelled)
2207   biblionumber
2208   get_canceled_order (if set to a true value, cancelled orders will
2209                       be included)
2210
2211 returns:
2212     $order_loop is a list of hashrefs that each look like this:
2213             {
2214                 'author'           => 'Twain, Mark',
2215                 'basketno'         => '1',
2216                 'biblionumber'     => '215',
2217                 'count'            => 1,
2218                 'creationdate'     => 'MM/DD/YYYY',
2219                 'datereceived'     => undef,
2220                 'ecost'            => '1.00',
2221                 'id'               => '1',
2222                 'invoicenumber'    => undef,
2223                 'name'             => '',
2224                 'ordernumber'      => '1',
2225                 'quantity'         => 1,
2226                 'quantityreceived' => undef,
2227                 'title'            => 'The Adventures of Huckleberry Finn'
2228             }
2229
2230 =cut
2231
2232 sub GetHistory {
2233 # don't run the query if there are no parameters (list would be too long for sure !)
2234     croak "No search params" unless @_;
2235     my %params = @_;
2236     my $title = $params{title};
2237     my $author = $params{author};
2238     my $isbn   = $params{isbn};
2239     my $ean    = $params{ean};
2240     my $name = $params{name};
2241     my $from_placed_on = $params{from_placed_on};
2242     my $to_placed_on = $params{to_placed_on};
2243     my $basket = $params{basket};
2244     my $booksellerinvoicenumber = $params{booksellerinvoicenumber};
2245     my $basketgroupname = $params{basketgroupname};
2246     my $budget = $params{budget};
2247     my $orderstatus = $params{orderstatus};
2248     my $biblionumber = $params{biblionumber};
2249     my $get_canceled_order = $params{get_canceled_order} || 0;
2250     my $ordernumber = $params{ordernumber};
2251     my $search_children_too = $params{search_children_too} || 0;
2252
2253     my @order_loop;
2254     my $total_qty         = 0;
2255     my $total_qtyreceived = 0;
2256     my $total_price       = 0;
2257
2258     my $dbh   = C4::Context->dbh;
2259     my $query ="
2260         SELECT
2261             COALESCE(biblio.title,     deletedbiblio.title)     AS title,
2262             COALESCE(biblio.author,    deletedbiblio.author)    AS author,
2263             COALESCE(biblioitems.isbn, deletedbiblioitems.isbn) AS isbn,
2264             COALESCE(biblioitems.ean,  deletedbiblioitems.ean)  AS ean,
2265             aqorders.basketno,
2266             aqbasket.basketname,
2267             aqbasket.basketgroupid,
2268             aqbasketgroups.name as groupname,
2269             aqbooksellers.name,
2270             aqbasket.creationdate,
2271             aqorders.datereceived,
2272             aqorders.quantity,
2273             aqorders.quantityreceived,
2274             aqorders.ecost,
2275             aqorders.ordernumber,
2276             aqorders.invoiceid,
2277             aqinvoices.invoicenumber,
2278             aqbooksellers.id as id,
2279             aqorders.biblionumber,
2280             aqorders.orderstatus,
2281             aqorders.parent_ordernumber,
2282             aqbudgets.budget_name
2283             ";
2284     $query .= ", aqbudgets.budget_id AS budget" if defined $budget;
2285     $query .= "
2286         FROM aqorders
2287         LEFT JOIN aqbasket ON aqorders.basketno=aqbasket.basketno
2288         LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid=aqbasketgroups.id
2289         LEFT JOIN aqbooksellers ON aqbasket.booksellerid=aqbooksellers.id
2290         LEFT JOIN biblioitems ON biblioitems.biblionumber=aqorders.biblionumber
2291         LEFT JOIN biblio ON biblio.biblionumber=aqorders.biblionumber
2292         LEFT JOIN aqbudgets ON aqorders.budget_id=aqbudgets.budget_id
2293         LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
2294         LEFT JOIN deletedbiblio ON deletedbiblio.biblionumber=aqorders.biblionumber
2295         LEFT JOIN deletedbiblioitems ON deletedbiblioitems.biblionumber=aqorders.biblionumber
2296         ";
2297
2298     if ( C4::Context->preference("IndependentBranches") ) {
2299         $query .= " LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber";
2300     }
2301
2302     $query .= " WHERE 1 ";
2303
2304     unless ($get_canceled_order or (defined $orderstatus and $orderstatus eq 'cancelled')) {
2305         $query .= " AND (datecancellationprinted is NULL or datecancellationprinted='0000-00-00') ";
2306     }
2307
2308     my @query_params  = ();
2309
2310     if ( $biblionumber ) {
2311         $query .= " AND biblio.biblionumber = ?";
2312         push @query_params, $biblionumber;
2313     }
2314
2315     if ( $title ) {
2316         $query .= " AND biblio.title LIKE ? ";
2317         $title =~ s/\s+/%/g;
2318         push @query_params, "%$title%";
2319     }
2320
2321     if ( $author ) {
2322         $query .= " AND biblio.author LIKE ? ";
2323         push @query_params, "%$author%";
2324     }
2325
2326     if ( $isbn ) {
2327         $query .= " AND biblioitems.isbn LIKE ? ";
2328         push @query_params, "%$isbn%";
2329     }
2330     if ( $ean ) {
2331         $query .= " AND biblioitems.ean = ? ";
2332         push @query_params, "$ean";
2333     }
2334     if ( $name ) {
2335         $query .= " AND aqbooksellers.name LIKE ? ";
2336         push @query_params, "%$name%";
2337     }
2338
2339     if ( $budget ) {
2340         $query .= " AND aqbudgets.budget_id = ? ";
2341         push @query_params, "$budget";
2342     }
2343
2344     if ( $from_placed_on ) {
2345         $query .= " AND creationdate >= ? ";
2346         push @query_params, $from_placed_on;
2347     }
2348
2349     if ( $to_placed_on ) {
2350         $query .= " AND creationdate <= ? ";
2351         push @query_params, $to_placed_on;
2352     }
2353
2354     if ( defined $orderstatus and $orderstatus ne '') {
2355         $query .= " AND aqorders.orderstatus = ? ";
2356         push @query_params, "$orderstatus";
2357     }
2358
2359     if ($basket) {
2360         if ($basket =~ m/^\d+$/) {
2361             $query .= " AND aqorders.basketno = ? ";
2362             push @query_params, $basket;
2363         } else {
2364             $query .= " AND aqbasket.basketname LIKE ? ";
2365             push @query_params, "%$basket%";
2366         }
2367     }
2368
2369     if ($booksellerinvoicenumber) {
2370         $query .= " AND aqinvoices.invoicenumber LIKE ? ";
2371         push @query_params, "%$booksellerinvoicenumber%";
2372     }
2373
2374     if ($basketgroupname) {
2375         $query .= " AND aqbasketgroups.name LIKE ? ";
2376         push @query_params, "%$basketgroupname%";
2377     }
2378
2379     if ($ordernumber) {
2380         $query .= " AND (aqorders.ordernumber = ? ";
2381         push @query_params, $ordernumber;
2382         if ($search_children_too) {
2383             $query .= " OR aqorders.parent_ordernumber = ? ";
2384             push @query_params, $ordernumber;
2385         }
2386         $query .= ") ";
2387     }
2388
2389
2390     if ( C4::Context->preference("IndependentBranches") ) {
2391         unless ( C4::Context->IsSuperLibrarian() ) {
2392             $query .= " AND (borrowers.branchcode = ? OR borrowers.branchcode ='' ) ";
2393             push @query_params, C4::Context->userenv->{branch};
2394         }
2395     }
2396     $query .= " ORDER BY id";
2397
2398     return $dbh->selectall_arrayref( $query, { Slice => {} }, @query_params );
2399 }
2400
2401 =head2 GetRecentAcqui
2402
2403   $results = GetRecentAcqui($days);
2404
2405 C<$results> is a ref to a table which containts hashref
2406
2407 =cut
2408
2409 sub GetRecentAcqui {
2410     my $limit  = shift;
2411     my $dbh    = C4::Context->dbh;
2412     my $query = "
2413         SELECT *
2414         FROM   biblio
2415         ORDER BY timestamp DESC
2416         LIMIT  0,".$limit;
2417
2418     my $sth = $dbh->prepare($query);
2419     $sth->execute;
2420     my $results = $sth->fetchall_arrayref({});
2421     return $results;
2422 }
2423
2424 #------------------------------------------------------------#
2425
2426 =head3 AddClaim
2427
2428 =over
2429
2430 &AddClaim($ordernumber);
2431
2432 Add a claim for an order
2433
2434 =back
2435
2436 =cut
2437
2438 sub AddClaim {
2439     my ($ordernumber) = @_;
2440     my $dbh          = C4::Context->dbh;
2441     my $query        = "
2442         UPDATE aqorders SET
2443             claims_count = claims_count + 1,
2444             claimed_date = CURDATE()
2445         WHERE ordernumber = ?
2446         ";
2447     my $sth = $dbh->prepare($query);
2448     $sth->execute($ordernumber);
2449 }
2450
2451 =head3 GetInvoices
2452
2453     my @invoices = GetInvoices(
2454         invoicenumber => $invoicenumber,
2455         supplierid => $supplierid,
2456         suppliername => $suppliername,
2457         shipmentdatefrom => $shipmentdatefrom, # ISO format
2458         shipmentdateto => $shipmentdateto, # ISO format
2459         billingdatefrom => $billingdatefrom, # ISO format
2460         billingdateto => $billingdateto, # ISO format
2461         isbneanissn => $isbn_or_ean_or_issn,
2462         title => $title,
2463         author => $author,
2464         publisher => $publisher,
2465         publicationyear => $publicationyear,
2466         branchcode => $branchcode,
2467         order_by => $order_by
2468     );
2469
2470 Return a list of invoices that match all given criteria.
2471
2472 $order_by is "column_name (asc|desc)", where column_name is any of
2473 'invoicenumber', 'booksellerid', 'shipmentdate', 'billingdate', 'closedate',
2474 'shipmentcost', 'shipmentcost_budgetid'.
2475
2476 asc is the default if omitted
2477
2478 =cut
2479
2480 sub GetInvoices {
2481     my %args = @_;
2482
2483     my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2484         closedate shipmentcost shipmentcost_budgetid);
2485
2486     my $dbh = C4::Context->dbh;
2487     my $query = qq{
2488         SELECT aqinvoices.*, aqbooksellers.name AS suppliername,
2489           COUNT(
2490             DISTINCT IF(
2491               aqorders.datereceived IS NOT NULL,
2492               aqorders.biblionumber,
2493               NULL
2494             )
2495           ) AS receivedbiblios,
2496           COUNT(
2497              DISTINCT IF(
2498               aqorders.subscriptionid IS NOT NULL,
2499               aqorders.subscriptionid,
2500               NULL
2501             )
2502           ) AS is_linked_to_subscriptions,
2503           SUM(aqorders.quantityreceived) AS receiveditems
2504         FROM aqinvoices
2505           LEFT JOIN aqbooksellers ON aqbooksellers.id = aqinvoices.booksellerid
2506           LEFT JOIN aqorders ON aqorders.invoiceid = aqinvoices.invoiceid
2507           LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
2508           LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
2509           LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2510           LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
2511           LEFT JOIN subscription ON biblio.biblionumber = subscription.biblionumber
2512     };
2513
2514     my @bind_args;
2515     my @bind_strs;
2516     if($args{supplierid}) {
2517         push @bind_strs, " aqinvoices.booksellerid = ? ";
2518         push @bind_args, $args{supplierid};
2519     }
2520     if($args{invoicenumber}) {
2521         push @bind_strs, " aqinvoices.invoicenumber LIKE ? ";
2522         push @bind_args, "%$args{invoicenumber}%";
2523     }
2524     if($args{suppliername}) {
2525         push @bind_strs, " aqbooksellers.name LIKE ? ";
2526         push @bind_args, "%$args{suppliername}%";
2527     }
2528     if($args{shipmentdatefrom}) {
2529         push @bind_strs, " aqinvoices.shipmentdate >= ? ";
2530         push @bind_args, $args{shipmentdatefrom};
2531     }
2532     if($args{shipmentdateto}) {
2533         push @bind_strs, " aqinvoices.shipmentdate <= ? ";
2534         push @bind_args, $args{shipmentdateto};
2535     }
2536     if($args{billingdatefrom}) {
2537         push @bind_strs, " aqinvoices.billingdate >= ? ";
2538         push @bind_args, $args{billingdatefrom};
2539     }
2540     if($args{billingdateto}) {
2541         push @bind_strs, " aqinvoices.billingdate <= ? ";
2542         push @bind_args, $args{billingdateto};
2543     }
2544     if($args{isbneanissn}) {
2545         push @bind_strs, " (biblioitems.isbn LIKE CONCAT('%', ?, '%') OR biblioitems.ean LIKE CONCAT('%', ?, '%') OR biblioitems.issn LIKE CONCAT('%', ?, '%') ) ";
2546         push @bind_args, $args{isbneanissn}, $args{isbneanissn}, $args{isbneanissn};
2547     }
2548     if($args{title}) {
2549         push @bind_strs, " biblio.title LIKE CONCAT('%', ?, '%') ";
2550         push @bind_args, $args{title};
2551     }
2552     if($args{author}) {
2553         push @bind_strs, " biblio.author LIKE CONCAT('%', ?, '%') ";
2554         push @bind_args, $args{author};
2555     }
2556     if($args{publisher}) {
2557         push @bind_strs, " biblioitems.publishercode LIKE CONCAT('%', ?, '%') ";
2558         push @bind_args, $args{publisher};
2559     }
2560     if($args{publicationyear}) {
2561         push @bind_strs, " ((biblioitems.publicationyear LIKE CONCAT('%', ?, '%')) OR (biblio.copyrightdate LIKE CONCAT('%', ?, '%'))) ";
2562         push @bind_args, $args{publicationyear}, $args{publicationyear};
2563     }
2564     if($args{branchcode}) {
2565         push @bind_strs, " borrowers.branchcode = ? ";
2566         push @bind_args, $args{branchcode};
2567     }
2568
2569     $query .= " WHERE " . join(" AND ", @bind_strs) if @bind_strs;
2570     $query .= " GROUP BY aqinvoices.invoiceid ";
2571
2572     if($args{order_by}) {
2573         my ($column, $direction) = split / /, $args{order_by};
2574         if(grep /^$column$/, @columns) {
2575             $direction ||= 'ASC';
2576             $query .= " ORDER BY $column $direction";
2577         }
2578     }
2579
2580     my $sth = $dbh->prepare($query);
2581     $sth->execute(@bind_args);
2582
2583     my $results = $sth->fetchall_arrayref({});
2584     return @$results;
2585 }
2586
2587 =head3 GetInvoice
2588
2589     my $invoice = GetInvoice($invoiceid);
2590
2591 Get informations about invoice with given $invoiceid
2592
2593 Return a hash filled with aqinvoices.* fields
2594
2595 =cut
2596
2597 sub GetInvoice {
2598     my ($invoiceid) = @_;
2599     my $invoice;
2600
2601     return unless $invoiceid;
2602
2603     my $dbh = C4::Context->dbh;
2604     my $query = qq{
2605         SELECT *
2606         FROM aqinvoices
2607         WHERE invoiceid = ?
2608     };
2609     my $sth = $dbh->prepare($query);
2610     $sth->execute($invoiceid);
2611
2612     $invoice = $sth->fetchrow_hashref;
2613     return $invoice;
2614 }
2615
2616 =head3 GetInvoiceDetails
2617
2618     my $invoice = GetInvoiceDetails($invoiceid)
2619
2620 Return informations about an invoice + the list of related order lines
2621
2622 Orders informations are in $invoice->{orders} (array ref)
2623
2624 =cut
2625
2626 sub GetInvoiceDetails {
2627     my ($invoiceid) = @_;
2628
2629     if ( !defined $invoiceid ) {
2630         carp 'GetInvoiceDetails called without an invoiceid';
2631         return;
2632     }
2633
2634     my $dbh = C4::Context->dbh;
2635     my $query = q{
2636         SELECT aqinvoices.*, aqbooksellers.name AS suppliername
2637         FROM aqinvoices
2638           LEFT JOIN aqbooksellers ON aqinvoices.booksellerid = aqbooksellers.id
2639         WHERE invoiceid = ?
2640     };
2641     my $sth = $dbh->prepare($query);
2642     $sth->execute($invoiceid);
2643
2644     my $invoice = $sth->fetchrow_hashref;
2645
2646     $query = q{
2647         SELECT aqorders.*, biblio.*,
2648         biblio.copyrightdate,
2649         biblioitems.publishercode,
2650         biblioitems.publicationyear,
2651         aqbasket.basketname
2652         FROM aqorders
2653           LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
2654           LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2655           LEFT JOIN biblioitems ON aqorders.biblionumber = biblioitems.biblionumber
2656         WHERE invoiceid = ?
2657     };
2658     $sth = $dbh->prepare($query);
2659     $sth->execute($invoiceid);
2660     $invoice->{orders} = $sth->fetchall_arrayref({});
2661     $invoice->{orders} ||= []; # force an empty arrayref if fetchall_arrayref fails
2662
2663     return $invoice;
2664 }
2665
2666 =head3 AddInvoice
2667
2668     my $invoiceid = AddInvoice(
2669         invoicenumber => $invoicenumber,
2670         booksellerid => $booksellerid,
2671         shipmentdate => $shipmentdate,
2672         billingdate => $billingdate,
2673         closedate => $closedate,
2674         shipmentcost => $shipmentcost,
2675         shipmentcost_budgetid => $shipmentcost_budgetid
2676     );
2677
2678 Create a new invoice and return its id or undef if it fails.
2679
2680 =cut
2681
2682 sub AddInvoice {
2683     my %invoice = @_;
2684
2685     return unless(%invoice and $invoice{invoicenumber});
2686
2687     my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2688         closedate shipmentcost shipmentcost_budgetid);
2689
2690     my @set_strs;
2691     my @set_args;
2692     foreach my $key (keys %invoice) {
2693         if(0 < grep(/^$key$/, @columns)) {
2694             push @set_strs, "$key = ?";
2695             push @set_args, ($invoice{$key} || undef);
2696         }
2697     }
2698
2699     my $rv;
2700     if(@set_args > 0) {
2701         my $dbh = C4::Context->dbh;
2702         my $query = "INSERT INTO aqinvoices SET ";
2703         $query .= join (",", @set_strs);
2704         my $sth = $dbh->prepare($query);
2705         $rv = $sth->execute(@set_args);
2706         if($rv) {
2707             $rv = $dbh->last_insert_id(undef, undef, 'aqinvoices', undef);
2708         }
2709     }
2710     return $rv;
2711 }
2712
2713 =head3 ModInvoice
2714
2715     ModInvoice(
2716         invoiceid => $invoiceid,    # Mandatory
2717         invoicenumber => $invoicenumber,
2718         booksellerid => $booksellerid,
2719         shipmentdate => $shipmentdate,
2720         billingdate => $billingdate,
2721         closedate => $closedate,
2722         shipmentcost => $shipmentcost,
2723         shipmentcost_budgetid => $shipmentcost_budgetid
2724     );
2725
2726 Modify an invoice, invoiceid is mandatory.
2727
2728 Return undef if it fails.
2729
2730 =cut
2731
2732 sub ModInvoice {
2733     my %invoice = @_;
2734
2735     return unless(%invoice and $invoice{invoiceid});
2736
2737     my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2738         closedate shipmentcost shipmentcost_budgetid);
2739
2740     my @set_strs;
2741     my @set_args;
2742     foreach my $key (keys %invoice) {
2743         if(0 < grep(/^$key$/, @columns)) {
2744             push @set_strs, "$key = ?";
2745             push @set_args, ($invoice{$key} || undef);
2746         }
2747     }
2748
2749     my $dbh = C4::Context->dbh;
2750     my $query = "UPDATE aqinvoices SET ";
2751     $query .= join(",", @set_strs);
2752     $query .= " WHERE invoiceid = ?";
2753
2754     my $sth = $dbh->prepare($query);
2755     $sth->execute(@set_args, $invoice{invoiceid});
2756 }
2757
2758 =head3 CloseInvoice
2759
2760     CloseInvoice($invoiceid);
2761
2762 Close an invoice.
2763
2764 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => undef);
2765
2766 =cut
2767
2768 sub CloseInvoice {
2769     my ($invoiceid) = @_;
2770
2771     return unless $invoiceid;
2772
2773     my $dbh = C4::Context->dbh;
2774     my $query = qq{
2775         UPDATE aqinvoices
2776         SET closedate = CAST(NOW() AS DATE)
2777         WHERE invoiceid = ?
2778     };
2779     my $sth = $dbh->prepare($query);
2780     $sth->execute($invoiceid);
2781 }
2782
2783 =head3 ReopenInvoice
2784
2785     ReopenInvoice($invoiceid);
2786
2787 Reopen an invoice
2788
2789 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => C4::Dates->new()->output('iso'))
2790
2791 =cut
2792
2793 sub ReopenInvoice {
2794     my ($invoiceid) = @_;
2795
2796     return unless $invoiceid;
2797
2798     my $dbh = C4::Context->dbh;
2799     my $query = qq{
2800         UPDATE aqinvoices
2801         SET closedate = NULL
2802         WHERE invoiceid = ?
2803     };
2804     my $sth = $dbh->prepare($query);
2805     $sth->execute($invoiceid);
2806 }
2807
2808 =head3 DelInvoice
2809
2810     DelInvoice($invoiceid);
2811
2812 Delete an invoice if there are no items attached to it.
2813
2814 =cut
2815
2816 sub DelInvoice {
2817     my ($invoiceid) = @_;
2818
2819     return unless $invoiceid;
2820
2821     my $dbh   = C4::Context->dbh;
2822     my $query = qq{
2823         SELECT COUNT(*)
2824         FROM aqorders
2825         WHERE invoiceid = ?
2826     };
2827     my $sth = $dbh->prepare($query);
2828     $sth->execute($invoiceid);
2829     my $res = $sth->fetchrow_arrayref;
2830     if ( $res && $res->[0] == 0 ) {
2831         $query = qq{
2832             DELETE FROM aqinvoices
2833             WHERE invoiceid = ?
2834         };
2835         my $sth = $dbh->prepare($query);
2836         return ( $sth->execute($invoiceid) > 0 );
2837     }
2838     return;
2839 }
2840
2841 =head3 MergeInvoices
2842
2843     MergeInvoices($invoiceid, \@sourceids);
2844
2845 Merge the invoices identified by the IDs in \@sourceids into
2846 the invoice identified by $invoiceid.
2847
2848 =cut
2849
2850 sub MergeInvoices {
2851     my ($invoiceid, $sourceids) = @_;
2852
2853     return unless $invoiceid;
2854     foreach my $sourceid (@$sourceids) {
2855         next if $sourceid == $invoiceid;
2856         my $source = GetInvoiceDetails($sourceid);
2857         foreach my $order (@{$source->{'orders'}}) {
2858             $order->{'invoiceid'} = $invoiceid;
2859             ModOrder($order);
2860         }
2861         DelInvoice($source->{'invoiceid'});
2862     }
2863     return;
2864 }
2865
2866 =head3 GetBiblioCountByBasketno
2867
2868 $biblio_count = &GetBiblioCountByBasketno($basketno);
2869
2870 Looks up the biblio's count that has basketno value $basketno
2871
2872 Returns a quantity
2873
2874 =cut
2875
2876 sub GetBiblioCountByBasketno {
2877     my ($basketno) = @_;
2878     my $dbh          = C4::Context->dbh;
2879     my $query        = "
2880         SELECT COUNT( DISTINCT( biblionumber ) )
2881         FROM   aqorders
2882         WHERE  basketno = ?
2883             AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')
2884         ";
2885
2886     my $sth = $dbh->prepare($query);
2887     $sth->execute($basketno);
2888     return $sth->fetchrow;
2889 }
2890
2891 1;
2892 __END__
2893
2894 =head1 AUTHOR
2895
2896 Koha Development Team <http://koha-community.org/>
2897
2898 =cut