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