Bug 11319: Koha::SimpleMARC should take a hashref for parameters
[koha.git] / C4 / Acquisition.pm
blobf9e8c4966a1adb50188a359a7d2c3957dfc511f5
1 package C4::Acquisition;
3 # Copyright 2000-2002 Katipo Communications
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 use Modern::Perl;
22 use Carp;
23 use C4::Context;
24 use C4::Debug;
25 use C4::Dates qw(format_date format_date_in_iso);
26 use MARC::Record;
27 use C4::Suggestions;
28 use C4::Biblio;
29 use C4::Contract;
30 use C4::Debug;
31 use C4::Bookseller qw(GetBookSellerFromId);
32 use C4::Templates qw(gettemplate);
33 use Koha::DateUtils qw( dt_from_string output_pref );
34 use Koha::Acquisition::Order;
36 use Time::localtime;
37 use HTML::Entities;
39 use vars qw($VERSION @ISA @EXPORT);
41 BEGIN {
42 # set the version for version checking
43 $VERSION = 3.07.00.049;
44 require Exporter;
45 @ISA = qw(Exporter);
46 @EXPORT = qw(
47 &GetBasket &NewBasket &CloseBasket &ReopenBasket &DelBasket &ModBasket
48 &GetBasketAsCSV &GetBasketGroupAsCSV
49 &GetBasketsByBookseller &GetBasketsByBasketgroup
50 &GetBasketsInfosByBookseller
52 &GetBasketUsers &ModBasketUsers
53 &CanUserManageBasket
55 &ModBasketHeader
57 &ModBasketgroup &NewBasketgroup &DelBasketgroup &GetBasketgroup &CloseBasketgroup
58 &GetBasketgroups &ReOpenBasketgroup
60 &DelOrder &ModOrder &GetOrder &GetOrders &GetOrdersByBiblionumber
61 &GetLateOrders &GetOrderFromItemnumber
62 &SearchOrders &GetHistory &GetRecentAcqui
63 &ModReceiveOrder &CancelReceipt
64 &GetCancelledOrders &TransferOrder
65 &GetLastOrderNotReceivedFromSubscriptionid &GetLastOrderReceivedFromSubscriptionid
66 &ModItemOrder
68 &GetParcels &GetParcel
70 &GetInvoices
71 &GetInvoice
72 &GetInvoiceDetails
73 &AddInvoice
74 &ModInvoice
75 &CloseInvoice
76 &ReopenInvoice
77 &DelInvoice
78 &MergeInvoices
80 &GetItemnumbersFromOrder
82 &AddClaim
83 &GetBiblioCountByBasketno
91 sub GetOrderFromItemnumber {
92 my ($itemnumber) = @_;
93 my $dbh = C4::Context->dbh;
94 my $query = qq|
96 SELECT * from aqorders LEFT JOIN aqorders_items
97 ON ( aqorders.ordernumber = aqorders_items.ordernumber )
98 WHERE itemnumber = ? |;
100 my $sth = $dbh->prepare($query);
102 # $sth->trace(3);
104 $sth->execute($itemnumber);
106 my $order = $sth->fetchrow_hashref;
107 return ( $order );
111 # Returns the itemnumber(s) associated with the ordernumber given in parameter
112 sub GetItemnumbersFromOrder {
113 my ($ordernumber) = @_;
114 my $dbh = C4::Context->dbh;
115 my $query = "SELECT itemnumber FROM aqorders_items WHERE ordernumber=?";
116 my $sth = $dbh->prepare($query);
117 $sth->execute($ordernumber);
118 my @tab;
120 while (my $order = $sth->fetchrow_hashref) {
121 push @tab, $order->{'itemnumber'};
124 return @tab;
133 =head1 NAME
135 C4::Acquisition - Koha functions for dealing with orders and acquisitions
137 =head1 SYNOPSIS
139 use C4::Acquisition;
141 =head1 DESCRIPTION
143 The functions in this module deal with acquisitions, managing book
144 orders, basket and parcels.
146 =head1 FUNCTIONS
148 =head2 FUNCTIONS ABOUT BASKETS
150 =head3 GetBasket
152 $aqbasket = &GetBasket($basketnumber);
154 get all basket informations in aqbasket for a given basket
156 B<returns:> informations for a given basket returned as a hashref.
158 =cut
160 sub GetBasket {
161 my ($basketno) = @_;
162 my $dbh = C4::Context->dbh;
163 my $query = "
164 SELECT aqbasket.*,
165 concat( b.firstname,' ',b.surname) AS authorisedbyname
166 FROM aqbasket
167 LEFT JOIN borrowers b ON aqbasket.authorisedby=b.borrowernumber
168 WHERE basketno=?
170 my $sth=$dbh->prepare($query);
171 $sth->execute($basketno);
172 my $basket = $sth->fetchrow_hashref;
173 return ( $basket );
176 #------------------------------------------------------------#
178 =head3 NewBasket
180 $basket = &NewBasket( $booksellerid, $authorizedby, $basketname,
181 $basketnote, $basketbooksellernote, $basketcontractnumber, $deliveryplace, $billingplace );
183 Create a new basket in aqbasket table
185 =over
187 =item C<$booksellerid> is a foreign key in the aqbasket table
189 =item C<$authorizedby> is the username of who created the basket
191 =back
193 The other parameters are optional, see ModBasketHeader for more info on them.
195 =cut
197 sub NewBasket {
198 my ( $booksellerid, $authorisedby, $basketname, $basketnote,
199 $basketbooksellernote, $basketcontractnumber, $deliveryplace,
200 $billingplace ) = @_;
201 my $dbh = C4::Context->dbh;
202 my $query =
203 'INSERT INTO aqbasket (creationdate,booksellerid,authorisedby) '
204 . 'VALUES (now(),?,?)';
205 $dbh->do( $query, {}, $booksellerid, $authorisedby );
207 my $basket = $dbh->{mysql_insertid};
208 $basketname ||= q{}; # default to empty strings
209 $basketnote ||= q{};
210 $basketbooksellernote ||= q{};
211 ModBasketHeader( $basket, $basketname, $basketnote, $basketbooksellernote,
212 $basketcontractnumber, $booksellerid, $deliveryplace, $billingplace );
213 return $basket;
216 #------------------------------------------------------------#
218 =head3 CloseBasket
220 &CloseBasket($basketno);
222 close a basket (becomes unmodifiable, except for receives)
224 =cut
226 sub CloseBasket {
227 my ($basketno) = @_;
228 my $dbh = C4::Context->dbh;
229 my $query = "
230 UPDATE aqbasket
231 SET closedate=now()
232 WHERE basketno=?
234 my $sth = $dbh->prepare($query);
235 $sth->execute($basketno);
237 my @orders = GetOrders($basketno);
238 foreach my $order (@orders) {
239 $query = qq{
240 UPDATE aqorders
241 SET orderstatus = 'ordered'
242 WHERE ordernumber = ?;
244 $sth = $dbh->prepare($query);
245 $sth->execute($order->{'ordernumber'});
249 =head3 ReopenBasket
251 &ReopenBasket($basketno);
253 reopen a basket
255 =cut
257 sub ReopenBasket {
258 my ($basketno) = @_;
259 my $dbh = C4::Context->dbh;
260 my $query = "
261 UPDATE aqbasket
262 SET closedate=NULL
263 WHERE basketno=?
265 my $sth = $dbh->prepare($query);
266 $sth->execute($basketno);
268 my @orders = GetOrders($basketno);
269 foreach my $order (@orders) {
270 $query = qq{
271 UPDATE aqorders
272 SET orderstatus = 'new'
273 WHERE ordernumber = ?;
275 $sth = $dbh->prepare($query);
276 $sth->execute($order->{'ordernumber'});
280 #------------------------------------------------------------#
282 =head3 GetBasketAsCSV
284 &GetBasketAsCSV($basketno);
286 Export a basket as CSV
288 $cgi parameter is needed for column name translation
290 =cut
292 sub GetBasketAsCSV {
293 my ($basketno, $cgi) = @_;
294 my $basket = GetBasket($basketno);
295 my @orders = GetOrders($basketno);
296 my $contract = GetContract({
297 contractnumber => $basket->{'contractnumber'}
300 my $template = C4::Templates::gettemplate("acqui/csv/basket.tt", "intranet", $cgi);
302 my @rows;
303 foreach my $order (@orders) {
304 my $bd = GetBiblioData( $order->{'biblionumber'} );
305 my $row = {
306 contractname => $contract->{'contractname'},
307 ordernumber => $order->{'ordernumber'},
308 entrydate => $order->{'entrydate'},
309 isbn => $order->{'isbn'},
310 author => $bd->{'author'},
311 title => $bd->{'title'},
312 publicationyear => $bd->{'publicationyear'},
313 publishercode => $bd->{'publishercode'},
314 collectiontitle => $bd->{'collectiontitle'},
315 notes => $order->{'order_vendornote'},
316 quantity => $order->{'quantity'},
317 rrp => $order->{'rrp'},
318 deliveryplace => C4::Branch::GetBranchName( $basket->{'deliveryplace'} ),
319 billingplace => C4::Branch::GetBranchName( $basket->{'billingplace'} ),
321 foreach(qw(
322 contractname author title publishercode collectiontitle notes
323 deliveryplace billingplace
324 ) ) {
325 # Double the quotes to not be interpreted as a field end
326 $row->{$_} =~ s/"/""/g if $row->{$_};
328 push @rows, $row;
331 @rows = sort {
332 if(defined $a->{publishercode} and defined $b->{publishercode}) {
333 $a->{publishercode} cmp $b->{publishercode};
335 } @rows;
337 $template->param(rows => \@rows);
339 return $template->output;
343 =head3 GetBasketGroupAsCSV
345 =over
347 &GetBasketGroupAsCSV($basketgroupid);
349 Export a basket group as CSV
351 $cgi parameter is needed for column name translation
353 =back
355 =cut
357 sub GetBasketGroupAsCSV {
358 my ($basketgroupid, $cgi) = @_;
359 my $baskets = GetBasketsByBasketgroup($basketgroupid);
361 my $template = C4::Templates::gettemplate('acqui/csv/basketgroup.tt', 'intranet', $cgi);
363 my @rows;
364 for my $basket (@$baskets) {
365 my @orders = GetOrders( $basket->{basketno} );
366 my $contract = GetContract({
367 contractnumber => $basket->{contractnumber}
369 my $bookseller = GetBookSellerFromId( $$basket{booksellerid} );
370 my $basketgroup = GetBasketgroup( $$basket{basketgroupid} );
372 foreach my $order (@orders) {
373 my $bd = GetBiblioData( $order->{'biblionumber'} );
374 my $row = {
375 clientnumber => $bookseller->{accountnumber},
376 basketname => $basket->{basketname},
377 ordernumber => $order->{ordernumber},
378 author => $bd->{author},
379 title => $bd->{title},
380 publishercode => $bd->{publishercode},
381 publicationyear => $bd->{publicationyear},
382 collectiontitle => $bd->{collectiontitle},
383 isbn => $order->{isbn},
384 quantity => $order->{quantity},
385 rrp => $order->{rrp},
386 discount => $bookseller->{discount},
387 ecost => $order->{ecost},
388 notes => $order->{order_vendornote},
389 entrydate => $order->{entrydate},
390 booksellername => $bookseller->{name},
391 bookselleraddress => $bookseller->{address1},
392 booksellerpostal => $bookseller->{postal},
393 contractnumber => $contract->{contractnumber},
394 contractname => $contract->{contractname},
395 basketgroupdeliveryplace => C4::Branch::GetBranchName( $basketgroup->{deliveryplace} ),
396 basketgroupbillingplace => C4::Branch::GetBranchName( $basketgroup->{billingplace} ),
397 basketdeliveryplace => C4::Branch::GetBranchName( $basket->{deliveryplace} ),
398 basketbillingplace => C4::Branch::GetBranchName( $basket->{billingplace} ),
400 foreach(qw(
401 basketname author title publishercode collectiontitle notes
402 booksellername bookselleraddress booksellerpostal contractname
403 basketgroupdeliveryplace basketgroupbillingplace
404 basketdeliveryplace basketbillingplace
405 ) ) {
406 # Double the quotes to not be interpreted as a field end
407 $row->{$_} =~ s/"/""/g if $row->{$_};
409 push @rows, $row;
412 $template->param(rows => \@rows);
414 return $template->output;
418 =head3 CloseBasketgroup
420 &CloseBasketgroup($basketgroupno);
422 close a basketgroup
424 =cut
426 sub CloseBasketgroup {
427 my ($basketgroupno) = @_;
428 my $dbh = C4::Context->dbh;
429 my $sth = $dbh->prepare("
430 UPDATE aqbasketgroups
431 SET closed=1
432 WHERE id=?
434 $sth->execute($basketgroupno);
437 #------------------------------------------------------------#
439 =head3 ReOpenBaskergroup($basketgroupno)
441 &ReOpenBaskergroup($basketgroupno);
443 reopen a basketgroup
445 =cut
447 sub ReOpenBasketgroup {
448 my ($basketgroupno) = @_;
449 my $dbh = C4::Context->dbh;
450 my $sth = $dbh->prepare("
451 UPDATE aqbasketgroups
452 SET closed=0
453 WHERE id=?
455 $sth->execute($basketgroupno);
458 #------------------------------------------------------------#
461 =head3 DelBasket
463 &DelBasket($basketno);
465 Deletes the basket that has basketno field $basketno in the aqbasket table.
467 =over
469 =item C<$basketno> is the primary key of the basket in the aqbasket table.
471 =back
473 =cut
475 sub DelBasket {
476 my ( $basketno ) = @_;
477 my $query = "DELETE FROM aqbasket WHERE basketno=?";
478 my $dbh = C4::Context->dbh;
479 my $sth = $dbh->prepare($query);
480 $sth->execute($basketno);
481 return;
484 #------------------------------------------------------------#
486 =head3 ModBasket
488 &ModBasket($basketinfo);
490 Modifies a basket, using a hashref $basketinfo for the relevant information, only $basketinfo->{'basketno'} is required.
492 =over
494 =item C<$basketno> is the primary key of the basket in the aqbasket table.
496 =back
498 =cut
500 sub ModBasket {
501 my $basketinfo = shift;
502 my $query = "UPDATE aqbasket SET ";
503 my @params;
504 foreach my $key (keys %$basketinfo){
505 if ($key ne 'basketno'){
506 $query .= "$key=?, ";
507 push(@params, $basketinfo->{$key} || undef );
510 # get rid of the "," at the end of $query
511 if (substr($query, length($query)-2) eq ', '){
512 chop($query);
513 chop($query);
514 $query .= ' ';
516 $query .= "WHERE basketno=?";
517 push(@params, $basketinfo->{'basketno'});
518 my $dbh = C4::Context->dbh;
519 my $sth = $dbh->prepare($query);
520 $sth->execute(@params);
522 return;
525 #------------------------------------------------------------#
527 =head3 ModBasketHeader
529 &ModBasketHeader($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid);
531 Modifies a basket's header.
533 =over
535 =item C<$basketno> is the "basketno" field in the "aqbasket" table;
537 =item C<$basketname> is the "basketname" field in the "aqbasket" table;
539 =item C<$note> is the "note" field in the "aqbasket" table;
541 =item C<$booksellernote> is the "booksellernote" field in the "aqbasket" table;
543 =item C<$contractnumber> is the "contractnumber" (foreign) key in the "aqbasket" table.
545 =item C<$booksellerid> is the id (foreign) key in the "aqbooksellers" table for the vendor.
547 =item C<$deliveryplace> is the "deliveryplace" field in the aqbasket table.
549 =item C<$billingplace> is the "billingplace" field in the aqbasket table.
551 =back
553 =cut
555 sub ModBasketHeader {
556 my ($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid, $deliveryplace, $billingplace) = @_;
557 my $query = qq{
558 UPDATE aqbasket
559 SET basketname=?, note=?, booksellernote=?, booksellerid=?, deliveryplace=?, billingplace=?
560 WHERE basketno=?
563 my $dbh = C4::Context->dbh;
564 my $sth = $dbh->prepare($query);
565 $sth->execute($basketname, $note, $booksellernote, $booksellerid, $deliveryplace, $billingplace, $basketno);
567 if ( $contractnumber ) {
568 my $query2 ="UPDATE aqbasket SET contractnumber=? WHERE basketno=?";
569 my $sth2 = $dbh->prepare($query2);
570 $sth2->execute($contractnumber,$basketno);
572 return;
575 #------------------------------------------------------------#
577 =head3 GetBasketsByBookseller
579 @results = &GetBasketsByBookseller($booksellerid, $extra);
581 Returns a list of hashes of all the baskets that belong to bookseller 'booksellerid'.
583 =over
585 =item C<$booksellerid> is the 'id' field of the bookseller in the aqbooksellers table
587 =item C<$extra> is the extra sql parameters, can be
589 $extra->{groupby}: group baskets by column
590 ex. $extra->{groupby} = aqbasket.basketgroupid
591 $extra->{orderby}: order baskets by column
592 $extra->{limit}: limit number of results (can be helpful for pagination)
594 =back
596 =cut
598 sub GetBasketsByBookseller {
599 my ($booksellerid, $extra) = @_;
600 my $query = "SELECT * FROM aqbasket WHERE booksellerid=?";
601 if ($extra){
602 if ($extra->{groupby}) {
603 $query .= " GROUP by $extra->{groupby}";
605 if ($extra->{orderby}){
606 $query .= " ORDER by $extra->{orderby}";
608 if ($extra->{limit}){
609 $query .= " LIMIT $extra->{limit}";
612 my $dbh = C4::Context->dbh;
613 my $sth = $dbh->prepare($query);
614 $sth->execute($booksellerid);
615 return $sth->fetchall_arrayref({});
618 =head3 GetBasketsInfosByBookseller
620 my $baskets = GetBasketsInfosByBookseller($supplierid, $allbaskets);
622 The optional second parameter allbaskets is a boolean allowing you to
623 select all baskets from the supplier; by default only active baskets (open or
624 closed but still something to receive) are returned.
626 Returns in a arrayref of hashref all about booksellers baskets, plus:
627 total_biblios: Number of distinct biblios in basket
628 total_items: Number of items in basket
629 expected_items: Number of non-received items in basket
631 =cut
633 sub GetBasketsInfosByBookseller {
634 my ($supplierid, $allbaskets) = @_;
636 return unless $supplierid;
638 my $dbh = C4::Context->dbh;
639 my $query = q{
640 SELECT aqbasket.*,
641 SUM(aqorders.quantity) AS total_items,
642 SUM(
643 IF ( aqorders.orderstatus = 'cancelled', aqorders.quantity, 0 )
644 ) AS total_items_cancelled,
645 COUNT(DISTINCT aqorders.biblionumber) AS total_biblios,
646 SUM(
647 IF(aqorders.datereceived IS NULL
648 AND aqorders.datecancellationprinted IS NULL
649 , aqorders.quantity
650 , 0)
651 ) AS expected_items
652 FROM aqbasket
653 LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
654 WHERE booksellerid = ?};
656 unless ( $allbaskets ) {
657 $query.=" AND (closedate IS NULL OR (aqorders.quantity > aqorders.quantityreceived AND datecancellationprinted IS NULL))";
659 $query.=" GROUP BY aqbasket.basketno";
661 my $sth = $dbh->prepare($query);
662 $sth->execute($supplierid);
663 my $baskets = $sth->fetchall_arrayref({});
665 # Retrieve the number of biblios cancelled
666 my $cancelled_biblios = $dbh->selectall_hashref( q|
667 SELECT COUNT(DISTINCT(biblionumber)) AS total_biblios_cancelled, aqbasket.basketno
668 FROM aqbasket
669 LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
670 WHERE booksellerid = ?
671 AND aqorders.orderstatus = 'cancelled'
672 GROUP BY aqbasket.basketno
673 |, 'basketno', {}, $supplierid );
674 map {
675 $_->{total_biblios_cancelled} = $cancelled_biblios->{$_->{basketno}}{total_biblios_cancelled} || 0
676 } @$baskets;
678 return $baskets;
681 =head3 GetBasketUsers
683 $basketusers_ids = &GetBasketUsers($basketno);
685 Returns a list of all borrowernumbers that are in basket users list
687 =cut
689 sub GetBasketUsers {
690 my $basketno = shift;
692 return unless $basketno;
694 my $query = qq{
695 SELECT borrowernumber
696 FROM aqbasketusers
697 WHERE basketno = ?
699 my $dbh = C4::Context->dbh;
700 my $sth = $dbh->prepare($query);
701 $sth->execute($basketno);
702 my $results = $sth->fetchall_arrayref( {} );
704 my @borrowernumbers;
705 foreach (@$results) {
706 push @borrowernumbers, $_->{'borrowernumber'};
709 return @borrowernumbers;
712 =head3 ModBasketUsers
714 my @basketusers_ids = (1, 2, 3);
715 &ModBasketUsers($basketno, @basketusers_ids);
717 Delete all users from basket users list, and add users in C<@basketusers_ids>
718 to this users list.
720 =cut
722 sub ModBasketUsers {
723 my ($basketno, @basketusers_ids) = @_;
725 return unless $basketno;
727 my $dbh = C4::Context->dbh;
728 my $query = qq{
729 DELETE FROM aqbasketusers
730 WHERE basketno = ?
732 my $sth = $dbh->prepare($query);
733 $sth->execute($basketno);
735 $query = qq{
736 INSERT INTO aqbasketusers (basketno, borrowernumber)
737 VALUES (?, ?)
739 $sth = $dbh->prepare($query);
740 foreach my $basketuser_id (@basketusers_ids) {
741 $sth->execute($basketno, $basketuser_id);
743 return;
746 =head3 CanUserManageBasket
748 my $bool = CanUserManageBasket($borrower, $basket[, $userflags]);
749 my $bool = CanUserManageBasket($borrowernumber, $basketno[, $userflags]);
751 Check if a borrower can manage a basket, according to system preference
752 AcqViewBaskets, user permissions and basket properties (creator, users list,
753 branch).
755 First parameter can be either a borrowernumber or a hashref as returned by
756 C4::Members::GetMember.
758 Second parameter can be either a basketno or a hashref as returned by
759 C4::Acquisition::GetBasket.
761 The third parameter is optional. If given, it should be a hashref as returned
762 by C4::Auth::getuserflags. If not, getuserflags is called.
764 If user is authorised to manage basket, returns 1.
765 Otherwise returns 0.
767 =cut
769 sub CanUserManageBasket {
770 my ($borrower, $basket, $userflags) = @_;
772 if (!ref $borrower) {
773 $borrower = C4::Members::GetMember(borrowernumber => $borrower);
775 if (!ref $basket) {
776 $basket = GetBasket($basket);
779 return 0 unless ($basket and $borrower);
781 my $borrowernumber = $borrower->{borrowernumber};
782 my $basketno = $basket->{basketno};
784 my $AcqViewBaskets = C4::Context->preference('AcqViewBaskets');
786 if (!defined $userflags) {
787 my $dbh = C4::Context->dbh;
788 my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE borrowernumber = ?");
789 $sth->execute($borrowernumber);
790 my ($flags) = $sth->fetchrow_array;
791 $sth->finish;
793 $userflags = C4::Auth::getuserflags($flags, $borrower->{userid}, $dbh);
796 unless ($userflags->{superlibrarian}
797 || (ref $userflags->{acquisition} && $userflags->{acquisition}->{order_manage_all})
798 || (!ref $userflags->{acquisition} && $userflags->{acquisition}))
800 if (not exists $userflags->{acquisition}) {
801 return 0;
804 if ( (ref $userflags->{acquisition} && !$userflags->{acquisition}->{order_manage})
805 || (!ref $userflags->{acquisition} && !$userflags->{acquisition}) ) {
806 return 0;
809 if ($AcqViewBaskets eq 'user'
810 && $basket->{authorisedby} != $borrowernumber
811 && grep($borrowernumber, GetBasketUsers($basketno)) == 0) {
812 return 0;
815 if ($AcqViewBaskets eq 'branch' && defined $basket->{branch}
816 && $basket->{branch} ne $borrower->{branchcode}) {
817 return 0;
821 return 1;
824 #------------------------------------------------------------#
826 =head3 GetBasketsByBasketgroup
828 $baskets = &GetBasketsByBasketgroup($basketgroupid);
830 Returns a reference to all baskets that belong to basketgroup $basketgroupid.
832 =cut
834 sub GetBasketsByBasketgroup {
835 my $basketgroupid = shift;
836 my $query = qq{
837 SELECT *, aqbasket.booksellerid as booksellerid
838 FROM aqbasket
839 LEFT JOIN aqcontract USING(contractnumber) WHERE basketgroupid=?
841 my $dbh = C4::Context->dbh;
842 my $sth = $dbh->prepare($query);
843 $sth->execute($basketgroupid);
844 return $sth->fetchall_arrayref({});
847 #------------------------------------------------------------#
849 =head3 NewBasketgroup
851 $basketgroupid = NewBasketgroup(\%hashref);
853 Adds a basketgroup to the aqbasketgroups table, and add the initial baskets to it.
855 $hashref->{'booksellerid'} is the 'id' field of the bookseller in the aqbooksellers table,
857 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
859 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
861 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
863 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
865 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
867 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
869 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
871 =cut
873 sub NewBasketgroup {
874 my $basketgroupinfo = shift;
875 die "booksellerid is required to create a basketgroup" unless $basketgroupinfo->{'booksellerid'};
876 my $query = "INSERT INTO aqbasketgroups (";
877 my @params;
878 foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
879 if ( defined $basketgroupinfo->{$field} ) {
880 $query .= "$field, ";
881 push(@params, $basketgroupinfo->{$field});
884 $query .= "booksellerid) VALUES (";
885 foreach (@params) {
886 $query .= "?, ";
888 $query .= "?)";
889 push(@params, $basketgroupinfo->{'booksellerid'});
890 my $dbh = C4::Context->dbh;
891 my $sth = $dbh->prepare($query);
892 $sth->execute(@params);
893 my $basketgroupid = $dbh->{'mysql_insertid'};
894 if( $basketgroupinfo->{'basketlist'} ) {
895 foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
896 my $query2 = "UPDATE aqbasket SET basketgroupid=? WHERE basketno=?";
897 my $sth2 = $dbh->prepare($query2);
898 $sth2->execute($basketgroupid, $basketno);
901 return $basketgroupid;
904 #------------------------------------------------------------#
906 =head3 ModBasketgroup
908 ModBasketgroup(\%hashref);
910 Modifies a basketgroup in the aqbasketgroups table, and add the baskets to it.
912 $hashref->{'id'} is the 'id' field of the basketgroup in the aqbasketgroup table, this parameter is mandatory,
914 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
916 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
918 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
920 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
922 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
924 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
926 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
928 =cut
930 sub ModBasketgroup {
931 my $basketgroupinfo = shift;
932 die "basketgroup id is required to edit a basketgroup" unless $basketgroupinfo->{'id'};
933 my $dbh = C4::Context->dbh;
934 my $query = "UPDATE aqbasketgroups SET ";
935 my @params;
936 foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
937 if ( defined $basketgroupinfo->{$field} ) {
938 $query .= "$field=?, ";
939 push(@params, $basketgroupinfo->{$field});
942 chop($query);
943 chop($query);
944 $query .= " WHERE id=?";
945 push(@params, $basketgroupinfo->{'id'});
946 my $sth = $dbh->prepare($query);
947 $sth->execute(@params);
949 $sth = $dbh->prepare('UPDATE aqbasket SET basketgroupid = NULL WHERE basketgroupid = ?');
950 $sth->execute($basketgroupinfo->{'id'});
952 if($basketgroupinfo->{'basketlist'} && @{$basketgroupinfo->{'basketlist'}}){
953 $sth = $dbh->prepare("UPDATE aqbasket SET basketgroupid=? WHERE basketno=?");
954 foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
955 $sth->execute($basketgroupinfo->{'id'}, $basketno);
958 return;
961 #------------------------------------------------------------#
963 =head3 DelBasketgroup
965 DelBasketgroup($basketgroupid);
967 Deletes a basketgroup in the aqbasketgroups table, and removes the reference to it from the baskets,
969 =over
971 =item C<$basketgroupid> is the 'id' field of the basket in the aqbasketgroup table
973 =back
975 =cut
977 sub DelBasketgroup {
978 my $basketgroupid = shift;
979 die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
980 my $query = "DELETE FROM aqbasketgroups WHERE id=?";
981 my $dbh = C4::Context->dbh;
982 my $sth = $dbh->prepare($query);
983 $sth->execute($basketgroupid);
984 return;
987 #------------------------------------------------------------#
990 =head2 FUNCTIONS ABOUT ORDERS
992 =head3 GetBasketgroup
994 $basketgroup = &GetBasketgroup($basketgroupid);
996 Returns a reference to the hash containing all information about the basketgroup.
998 =cut
1000 sub GetBasketgroup {
1001 my $basketgroupid = shift;
1002 die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
1003 my $dbh = C4::Context->dbh;
1004 my $result_set = $dbh->selectall_arrayref(
1005 'SELECT * FROM aqbasketgroups WHERE id=?',
1006 { Slice => {} },
1007 $basketgroupid
1009 return $result_set->[0]; # id is unique
1012 #------------------------------------------------------------#
1014 =head3 GetBasketgroups
1016 $basketgroups = &GetBasketgroups($booksellerid);
1018 Returns a reference to the array of all the basketgroups of bookseller $booksellerid.
1020 =cut
1022 sub GetBasketgroups {
1023 my $booksellerid = shift;
1024 die 'bookseller id is required to edit a basketgroup' unless $booksellerid;
1025 my $query = 'SELECT * FROM aqbasketgroups WHERE booksellerid=? ORDER BY id DESC';
1026 my $dbh = C4::Context->dbh;
1027 my $sth = $dbh->prepare($query);
1028 $sth->execute($booksellerid);
1029 return $sth->fetchall_arrayref({});
1032 #------------------------------------------------------------#
1034 =head2 FUNCTIONS ABOUT ORDERS
1036 =head3 GetOrders
1038 @orders = &GetOrders($basketnumber, $orderby);
1040 Looks up the pending (non-cancelled) orders with the given basket
1041 number. If C<$booksellerID> is non-empty, only orders from that seller
1042 are returned.
1044 return :
1045 C<&basket> returns a two-element array. C<@orders> is an array of
1046 references-to-hash, whose keys are the fields from the aqorders,
1047 biblio, and biblioitems tables in the Koha database.
1049 =cut
1051 sub GetOrders {
1052 my ( $basketno, $orderby ) = @_;
1053 return () unless $basketno;
1054 my $dbh = C4::Context->dbh;
1055 my $query ="
1056 SELECT biblio.*,biblioitems.*,
1057 aqorders.*,
1058 aqbudgets.*,
1059 aqorders_transfers.ordernumber_from AS transferred_from,
1060 aqorders_transfers.timestamp AS transferred_from_timestamp
1061 FROM aqorders
1062 LEFT JOIN aqbudgets ON aqbudgets.budget_id = aqorders.budget_id
1063 LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1064 LEFT JOIN biblioitems ON biblioitems.biblionumber =biblio.biblionumber
1065 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1066 WHERE basketno=?
1067 AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')
1070 $orderby = "biblioitems.publishercode,biblio.title" unless $orderby;
1071 $query .= " ORDER BY $orderby";
1072 my $result_set =
1073 $dbh->selectall_arrayref( $query, { Slice => {} }, $basketno );
1074 return @{$result_set};
1078 #------------------------------------------------------------#
1079 =head3 GetOrdersByBiblionumber
1081 @orders = &GetOrdersByBiblionumber($biblionumber);
1083 Looks up the orders with linked to a specific $biblionumber, including
1084 cancelled orders and received orders.
1086 return :
1087 C<@orders> is an array of references-to-hash, whose keys are the
1088 fields from the aqorders, biblio, and biblioitems tables in the Koha database.
1090 =cut
1092 sub GetOrdersByBiblionumber {
1093 my $biblionumber = shift;
1094 return unless $biblionumber;
1095 my $dbh = C4::Context->dbh;
1096 my $query ="
1097 SELECT biblio.*,biblioitems.*,
1098 aqorders.*,
1099 aqbudgets.*
1100 FROM aqorders
1101 LEFT JOIN aqbudgets ON aqbudgets.budget_id = aqorders.budget_id
1102 LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1103 LEFT JOIN biblioitems ON biblioitems.biblionumber =biblio.biblionumber
1104 WHERE aqorders.biblionumber=?
1106 my $result_set =
1107 $dbh->selectall_arrayref( $query, { Slice => {} }, $biblionumber );
1108 return @{$result_set};
1112 #------------------------------------------------------------#
1114 =head3 GetOrder
1116 $order = &GetOrder($ordernumber);
1118 Looks up an order by order number.
1120 Returns a reference-to-hash describing the order. The keys of
1121 C<$order> are fields from the biblio, biblioitems, aqorders tables of the Koha database.
1123 =cut
1125 sub GetOrder {
1126 my ($ordernumber) = @_;
1127 return unless $ordernumber;
1129 my $dbh = C4::Context->dbh;
1130 my $query = qq{SELECT
1131 aqorders.*,
1132 biblio.title,
1133 biblio.author,
1134 aqbasket.basketname,
1135 borrowers.branchcode,
1136 biblioitems.publicationyear,
1137 biblio.copyrightdate,
1138 biblioitems.editionstatement,
1139 biblioitems.isbn,
1140 biblioitems.ean,
1141 biblio.seriestitle,
1142 biblioitems.publishercode,
1143 aqorders.rrp AS unitpricesupplier,
1144 aqorders.ecost AS unitpricelib,
1145 aqorders.claims_count AS claims_count,
1146 aqorders.claimed_date AS claimed_date,
1147 aqbudgets.budget_name AS budget,
1148 aqbooksellers.name AS supplier,
1149 aqbooksellers.id AS supplierid,
1150 biblioitems.publishercode AS publisher,
1151 ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
1152 DATE(aqbasket.closedate) AS orderdate,
1153 aqorders.quantity - COALESCE(aqorders.quantityreceived,0) AS quantity_to_receive,
1154 (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
1155 DATEDIFF(CURDATE( ),closedate) AS latesince
1156 FROM aqorders LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1157 LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
1158 LEFT JOIN aqbudgets ON aqorders.budget_id = aqbudgets.budget_id,
1159 aqbasket LEFT JOIN borrowers ON aqbasket.authorisedby = borrowers.borrowernumber
1160 LEFT JOIN aqbooksellers ON aqbasket.booksellerid = aqbooksellers.id
1161 WHERE aqorders.basketno = aqbasket.basketno
1162 AND ordernumber=?};
1163 my $result_set =
1164 $dbh->selectall_arrayref( $query, { Slice => {} }, $ordernumber );
1166 # result_set assumed to contain 1 match
1167 return $result_set->[0];
1170 =head3 GetLastOrderNotReceivedFromSubscriptionid
1172 $order = &GetLastOrderNotReceivedFromSubscriptionid($subscriptionid);
1174 Returns a reference-to-hash describing the last order not received for a subscription.
1176 =cut
1178 sub GetLastOrderNotReceivedFromSubscriptionid {
1179 my ( $subscriptionid ) = @_;
1180 my $dbh = C4::Context->dbh;
1181 my $query = qq|
1182 SELECT * FROM aqorders
1183 LEFT JOIN subscription
1184 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1185 WHERE aqorders.subscriptionid = ?
1186 AND aqorders.datereceived IS NULL
1187 LIMIT 1
1189 my $result_set =
1190 $dbh->selectall_arrayref( $query, { Slice => {} }, $subscriptionid );
1192 # result_set assumed to contain 1 match
1193 return $result_set->[0];
1196 =head3 GetLastOrderReceivedFromSubscriptionid
1198 $order = &GetLastOrderReceivedFromSubscriptionid($subscriptionid);
1200 Returns a reference-to-hash describing the last order received for a subscription.
1202 =cut
1204 sub GetLastOrderReceivedFromSubscriptionid {
1205 my ( $subscriptionid ) = @_;
1206 my $dbh = C4::Context->dbh;
1207 my $query = qq|
1208 SELECT * FROM aqorders
1209 LEFT JOIN subscription
1210 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1211 WHERE aqorders.subscriptionid = ?
1212 AND aqorders.datereceived =
1214 SELECT MAX( aqorders.datereceived )
1215 FROM aqorders
1216 LEFT JOIN subscription
1217 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1218 WHERE aqorders.subscriptionid = ?
1219 AND aqorders.datereceived IS NOT NULL
1221 ORDER BY ordernumber DESC
1222 LIMIT 1
1224 my $result_set =
1225 $dbh->selectall_arrayref( $query, { Slice => {} }, $subscriptionid, $subscriptionid );
1227 # result_set assumed to contain 1 match
1228 return $result_set->[0];
1232 #------------------------------------------------------------#
1234 =head3 ModOrder
1236 &ModOrder(\%hashref);
1238 Modifies an existing order. Updates the order with order number
1239 $hashref->{'ordernumber'} and biblionumber $hashref->{'biblionumber'}. All
1240 other keys of the hash update the fields with the same name in the aqorders
1241 table of the Koha database.
1243 =cut
1245 sub ModOrder {
1246 my $orderinfo = shift;
1248 die "Ordernumber is required" if $orderinfo->{'ordernumber'} eq '' ;
1249 die "Biblionumber is required" if $orderinfo->{'biblionumber'} eq '';
1251 my $dbh = C4::Context->dbh;
1252 my @params;
1254 # update uncertainprice to an integer, just in case (under FF, checked boxes have the value "ON" by default)
1255 $orderinfo->{uncertainprice}=1 if $orderinfo->{uncertainprice};
1257 # delete($orderinfo->{'branchcode'});
1258 # the hash contains a lot of entries not in aqorders, so get the columns ...
1259 my $sth = $dbh->prepare("SELECT * FROM aqorders LIMIT 1;");
1260 $sth->execute;
1261 my $colnames = $sth->{NAME};
1262 #FIXME Be careful. If aqorders would have columns with diacritics,
1263 #you should need to decode what you get back from NAME.
1264 #See report 10110 and guided_reports.pl
1265 my $query = "UPDATE aqorders SET ";
1267 foreach my $orderinfokey (grep(!/ordernumber/, keys %$orderinfo)){
1268 # ... and skip hash entries that are not in the aqorders table
1269 # FIXME : probably not the best way to do it (would be better to have a correct hash)
1270 next unless grep(/^$orderinfokey$/, @$colnames);
1271 $query .= "$orderinfokey=?, ";
1272 push(@params, $orderinfo->{$orderinfokey});
1275 $query .= "timestamp=NOW() WHERE ordernumber=?";
1276 push(@params, $orderinfo->{'ordernumber'} );
1277 $sth = $dbh->prepare($query);
1278 $sth->execute(@params);
1279 return;
1282 #------------------------------------------------------------#
1284 =head3 ModItemOrder
1286 ModItemOrder($itemnumber, $ordernumber);
1288 Modifies the ordernumber of an item in aqorders_items.
1290 =cut
1292 sub ModItemOrder {
1293 my ($itemnumber, $ordernumber) = @_;
1295 return unless ($itemnumber and $ordernumber);
1297 my $dbh = C4::Context->dbh;
1298 my $query = qq{
1299 UPDATE aqorders_items
1300 SET ordernumber = ?
1301 WHERE itemnumber = ?
1303 my $sth = $dbh->prepare($query);
1304 return $sth->execute($ordernumber, $itemnumber);
1307 #------------------------------------------------------------#
1309 =head3 GetCancelledOrders
1311 my @orders = GetCancelledOrders($basketno, $orderby);
1313 Returns cancelled orders for a basket
1315 =cut
1317 sub GetCancelledOrders {
1318 my ( $basketno, $orderby ) = @_;
1320 return () unless $basketno;
1322 my $dbh = C4::Context->dbh;
1323 my $query = "
1324 SELECT
1325 biblio.*,
1326 biblioitems.*,
1327 aqorders.*,
1328 aqbudgets.*,
1329 aqorders_transfers.ordernumber_to AS transferred_to,
1330 aqorders_transfers.timestamp AS transferred_to_timestamp
1331 FROM aqorders
1332 LEFT JOIN aqbudgets ON aqbudgets.budget_id = aqorders.budget_id
1333 LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1334 LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
1335 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_from = aqorders.ordernumber
1336 WHERE basketno = ?
1337 AND (datecancellationprinted IS NOT NULL
1338 AND datecancellationprinted <> '0000-00-00')
1341 $orderby = "aqorders.datecancellationprinted desc, aqorders.timestamp desc"
1342 unless $orderby;
1343 $query .= " ORDER BY $orderby";
1344 my $sth = $dbh->prepare($query);
1345 $sth->execute($basketno);
1346 my $results = $sth->fetchall_arrayref( {} );
1348 return @$results;
1352 #------------------------------------------------------------#
1354 =head3 ModReceiveOrder
1356 &ModReceiveOrder({
1357 biblionumber => $biblionumber,
1358 ordernumber => $ordernumber,
1359 quantityreceived => $quantityreceived,
1360 user => $user,
1361 cost => $cost,
1362 ecost => $ecost,
1363 invoiceid => $invoiceid,
1364 rrp => $rrp,
1365 budget_id => $budget_id,
1366 datereceived => $datereceived,
1367 received_itemnumbers => \@received_itemnumbers,
1368 order_internalnote => $order_internalnote,
1369 order_vendornote => $order_vendornote,
1372 Updates an order, to reflect the fact that it was received, at least
1373 in part. All arguments not mentioned below update the fields with the
1374 same name in the aqorders table of the Koha database.
1376 If a partial order is received, splits the order into two.
1378 Updates the order with bibilionumber C<$biblionumber> and ordernumber
1379 C<$ordernumber>.
1381 =cut
1384 sub ModReceiveOrder {
1385 my ( $params ) = @_;
1386 my $biblionumber = $params->{biblionumber};
1387 my $ordernumber = $params->{ordernumber};
1388 my $quantrec = $params->{quantityreceived};
1389 my $user = $params->{user};
1390 my $cost = $params->{cost};
1391 my $ecost = $params->{ecost};
1392 my $invoiceid = $params->{invoiceid};
1393 my $rrp = $params->{rrp};
1394 my $budget_id = $params->{budget_id};
1395 my $datereceived = $params->{datereceived};
1396 my $received_items = $params->{received_items};
1397 my $order_internalnote = $params->{order_internalnote};
1398 my $order_vendornote = $params->{order_vendornote};
1400 my $dbh = C4::Context->dbh;
1401 $datereceived = C4::Dates->output('iso') unless $datereceived;
1402 my $suggestionid = GetSuggestionFromBiblionumber( $biblionumber );
1403 if ($suggestionid) {
1404 ModSuggestion( {suggestionid=>$suggestionid,
1405 STATUS=>'AVAILABLE',
1406 biblionumber=> $biblionumber}
1410 my $result_set = $dbh->selectall_arrayref(
1411 q{SELECT * FROM aqorders WHERE biblionumber=? AND aqorders.ordernumber=?},
1412 { Slice => {} }, $biblionumber, $ordernumber
1415 # we assume we have a unique order
1416 my $order = $result_set->[0];
1418 my $new_ordernumber = $ordernumber;
1419 if ( $order->{quantity} > $quantrec ) {
1420 # Split order line in two parts: the first is the original order line
1421 # without received items (the quantity is decreased),
1422 # the second part is a new order line with quantity=quantityrec
1423 # (entirely received)
1424 my $query = q|
1425 UPDATE aqorders
1426 SET quantity = ?,
1427 orderstatus = 'partial'|;
1428 $query .= q|, order_internalnote = ?| if defined $order_internalnote;
1429 $query .= q|, order_vendornote = ?| if defined $order_vendornote;
1430 $query .= q| WHERE ordernumber = ?|;
1431 my $sth = $dbh->prepare($query);
1433 $sth->execute(
1434 $order->{quantity} - $quantrec,
1435 ( defined $order_internalnote ? $order_internalnote : () ),
1436 ( defined $order_vendornote ? $order_vendornote : () ),
1437 $ordernumber
1440 delete $order->{'ordernumber'};
1441 $order->{'budget_id'} = ( $budget_id || $order->{'budget_id'} );
1442 $order->{'quantity'} = $quantrec;
1443 $order->{'quantityreceived'} = $quantrec;
1444 $order->{'datereceived'} = $datereceived;
1445 $order->{'invoiceid'} = $invoiceid;
1446 $order->{'unitprice'} = $cost;
1447 $order->{'rrp'} = $rrp;
1448 $order->{ecost} = $ecost;
1449 $order->{'orderstatus'} = 'complete';
1450 $new_ordernumber = Koha::Acquisition::Order->new($order)->insert->{ordernumber};
1452 if ($received_items) {
1453 foreach my $itemnumber (@$received_items) {
1454 ModItemOrder($itemnumber, $new_ordernumber);
1457 } else {
1458 my $query = q|
1459 update aqorders
1460 set quantityreceived=?,datereceived=?,invoiceid=?,
1461 unitprice=?,rrp=?,ecost=?,budget_id=?,orderstatus='complete'|;
1462 $query .= q|, order_internalnote = ?| if defined $order_internalnote;
1463 $query .= q|, order_vendornote = ?| if defined $order_vendornote;
1464 $query .= q| where biblionumber=? and ordernumber=?|;
1465 my $sth = $dbh->prepare( $query );
1466 $sth->execute(
1467 $quantrec,
1468 $datereceived,
1469 $invoiceid,
1470 $cost,
1471 $rrp,
1472 $ecost,
1473 $budget_id,
1474 ( defined $order_internalnote ? $order_internalnote : () ),
1475 ( defined $order_vendornote ? $order_vendornote : () ),
1476 $biblionumber,
1477 $ordernumber
1480 return ($datereceived, $new_ordernumber);
1483 =head3 CancelReceipt
1485 my $parent_ordernumber = CancelReceipt($ordernumber);
1487 Cancel an order line receipt and update the parent order line, as if no
1488 receipt was made.
1489 If items are created at receipt (AcqCreateItem = receiving) then delete
1490 these items.
1492 =cut
1494 sub CancelReceipt {
1495 my $ordernumber = shift;
1497 return unless $ordernumber;
1499 my $dbh = C4::Context->dbh;
1500 my $query = qq{
1501 SELECT datereceived, parent_ordernumber, quantity
1502 FROM aqorders
1503 WHERE ordernumber = ?
1505 my $sth = $dbh->prepare($query);
1506 $sth->execute($ordernumber);
1507 my $order = $sth->fetchrow_hashref;
1508 unless($order) {
1509 warn "CancelReceipt: order $ordernumber does not exist";
1510 return;
1512 unless($order->{'datereceived'}) {
1513 warn "CancelReceipt: order $ordernumber is not received";
1514 return;
1517 my $parent_ordernumber = $order->{'parent_ordernumber'};
1519 my @itemnumbers = GetItemnumbersFromOrder( $ordernumber );
1521 if($parent_ordernumber == $ordernumber || not $parent_ordernumber) {
1522 # The order line has no parent, just mark it as not received
1523 $query = qq{
1524 UPDATE aqorders
1525 SET quantityreceived = ?,
1526 datereceived = ?,
1527 invoiceid = ?,
1528 orderstatus = 'ordered'
1529 WHERE ordernumber = ?
1531 $sth = $dbh->prepare($query);
1532 $sth->execute(0, undef, undef, $ordernumber);
1533 _cancel_items_receipt( $ordernumber );
1534 } else {
1535 # The order line has a parent, increase parent quantity and delete
1536 # the order line.
1537 $query = qq{
1538 SELECT quantity, datereceived
1539 FROM aqorders
1540 WHERE ordernumber = ?
1542 $sth = $dbh->prepare($query);
1543 $sth->execute($parent_ordernumber);
1544 my $parent_order = $sth->fetchrow_hashref;
1545 unless($parent_order) {
1546 warn "Parent order $parent_ordernumber does not exist.";
1547 return;
1549 if($parent_order->{'datereceived'}) {
1550 warn "CancelReceipt: parent order is received.".
1551 " Can't cancel receipt.";
1552 return;
1554 $query = qq{
1555 UPDATE aqorders
1556 SET quantity = ?,
1557 orderstatus = 'ordered'
1558 WHERE ordernumber = ?
1560 $sth = $dbh->prepare($query);
1561 my $rv = $sth->execute(
1562 $order->{'quantity'} + $parent_order->{'quantity'},
1563 $parent_ordernumber
1565 unless($rv) {
1566 warn "Cannot update parent order line, so do not cancel".
1567 " receipt";
1568 return;
1570 _cancel_items_receipt( $ordernumber, $parent_ordernumber );
1571 # Delete order line
1572 $query = qq{
1573 DELETE FROM aqorders
1574 WHERE ordernumber = ?
1576 $sth = $dbh->prepare($query);
1577 $sth->execute($ordernumber);
1581 if(C4::Context->preference('AcqCreateItem') eq 'ordering') {
1582 my @affects = split q{\|}, C4::Context->preference("AcqItemSetSubfieldsWhenReceiptIsCancelled");
1583 if ( @affects ) {
1584 for my $in ( @itemnumbers ) {
1585 my $biblionumber = C4::Biblio::GetBiblionumberFromItemnumber( $in );
1586 my $frameworkcode = GetFrameworkCode($biblionumber);
1587 my ( $itemfield ) = GetMarcFromKohaField( 'items.itemnumber', $frameworkcode );
1588 my $item = C4::Items::GetMarcItem( $biblionumber, $in );
1589 for my $affect ( @affects ) {
1590 my ( $sf, $v ) = split q{=}, $affect, 2;
1591 foreach ( $item->field($itemfield) ) {
1592 $_->update( $sf => $v );
1595 C4::Items::ModItemFromMarc( $item, $biblionumber, $in );
1600 return $parent_ordernumber;
1603 sub _cancel_items_receipt {
1604 my ( $ordernumber, $parent_ordernumber ) = @_;
1605 $parent_ordernumber ||= $ordernumber;
1607 my @itemnumbers = GetItemnumbersFromOrder($ordernumber);
1608 if(C4::Context->preference('AcqCreateItem') eq 'receiving') {
1609 # Remove items that were created at receipt
1610 my $query = qq{
1611 DELETE FROM items, aqorders_items
1612 USING items, aqorders_items
1613 WHERE items.itemnumber = ? AND aqorders_items.itemnumber = ?
1615 my $dbh = C4::Context->dbh;
1616 my $sth = $dbh->prepare($query);
1617 foreach my $itemnumber (@itemnumbers) {
1618 $sth->execute($itemnumber, $itemnumber);
1620 } else {
1621 # Update items
1622 foreach my $itemnumber (@itemnumbers) {
1623 ModItemOrder($itemnumber, $parent_ordernumber);
1628 #------------------------------------------------------------#
1630 =head3 SearchOrders
1632 @results = &SearchOrders({
1633 ordernumber => $ordernumber,
1634 search => $search,
1635 biblionumber => $biblionumber,
1636 ean => $ean,
1637 booksellerid => $booksellerid,
1638 basketno => $basketno,
1639 owner => $owner,
1640 pending => $pending
1641 ordered => $ordered
1644 Searches for orders.
1646 C<$owner> Finds order for the logged in user.
1647 C<$pending> Finds pending orders. Ignores completed and cancelled orders.
1648 C<$ordered> Finds orders to receive only (status 'ordered' or 'partial').
1651 C<@results> is an array of references-to-hash with the keys are fields
1652 from aqorders, biblio, biblioitems and aqbasket tables.
1654 =cut
1656 sub SearchOrders {
1657 my ( $params ) = @_;
1658 my $ordernumber = $params->{ordernumber};
1659 my $search = $params->{search};
1660 my $ean = $params->{ean};
1661 my $booksellerid = $params->{booksellerid};
1662 my $basketno = $params->{basketno};
1663 my $basketname = $params->{basketname};
1664 my $basketgroupname = $params->{basketgroupname};
1665 my $owner = $params->{owner};
1666 my $pending = $params->{pending};
1667 my $ordered = $params->{ordered};
1668 my $biblionumber = $params->{biblionumber};
1669 my $budget_id = $params->{budget_id};
1671 my $dbh = C4::Context->dbh;
1672 my @args = ();
1673 my $query = q{
1674 SELECT aqbasket.basketno,
1675 borrowers.surname,
1676 borrowers.firstname,
1677 biblio.*,
1678 biblioitems.isbn,
1679 biblioitems.biblioitemnumber,
1680 aqbasket.authorisedby,
1681 aqbasket.booksellerid,
1682 aqbasket.closedate,
1683 aqbasket.creationdate,
1684 aqbasket.basketname,
1685 aqbasketgroups.id as basketgroupid,
1686 aqbasketgroups.name as basketgroupname,
1687 aqorders.*
1688 FROM aqorders
1689 LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
1690 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
1691 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
1692 LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
1693 LEFT JOIN biblioitems ON biblioitems.biblionumber=biblio.biblionumber
1696 # If we search on ordernumber, we retrieve the transfered order if a transfer has been done.
1697 $query .= q{
1698 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1699 } if $ordernumber;
1701 $query .= q{
1702 WHERE (datecancellationprinted is NULL)
1705 if ( $pending or $ordered ) {
1706 $query .= q{ AND (quantity > quantityreceived OR quantityreceived is NULL)};
1708 if ( $ordered ) {
1709 $query .= q{ AND aqorders.orderstatus IN ( "ordered", "partial" )};
1712 my $userenv = C4::Context->userenv;
1713 if ( C4::Context->preference("IndependentBranches") ) {
1714 unless ( C4::Context->IsSuperLibrarian() ) {
1715 $query .= q{
1716 AND (
1717 borrowers.branchcode = ?
1718 OR borrowers.branchcode = ''
1721 push @args, $userenv->{branch};
1725 if ( $ordernumber ) {
1726 $query .= ' AND ( aqorders.ordernumber = ? OR aqorders_transfers.ordernumber_from = ? ) ';
1727 push @args, ( $ordernumber, $ordernumber );
1729 if ( $biblionumber ) {
1730 $query .= 'AND aqorders.biblionumber = ?';
1731 push @args, $biblionumber;
1733 if( $search ) {
1734 $query .= ' AND (biblio.title LIKE ? OR biblio.author LIKE ? OR biblioitems.isbn LIKE ?)';
1735 push @args, ("%$search%","%$search%","%$search%");
1737 if ( $ean ) {
1738 $query .= ' AND biblioitems.ean = ?';
1739 push @args, $ean;
1741 if ( $booksellerid ) {
1742 $query .= 'AND aqbasket.booksellerid = ?';
1743 push @args, $booksellerid;
1745 if( $basketno ) {
1746 $query .= 'AND aqbasket.basketno = ?';
1747 push @args, $basketno;
1749 if( $basketname ) {
1750 $query .= 'AND aqbasket.basketname LIKE ?';
1751 push @args, "%$basketname%";
1753 if( $basketgroupname ) {
1754 $query .= ' AND aqbasketgroups.name LIKE ?';
1755 push @args, "%$basketgroupname%";
1758 if ( $owner ) {
1759 $query .= ' AND aqbasket.authorisedby=? ';
1760 push @args, $userenv->{'number'};
1763 if ( $budget_id ) {
1764 $query .= ' AND aqorders.budget_id = ?';
1765 push @args, $budget_id;
1768 $query .= ' ORDER BY aqbasket.basketno';
1770 my $sth = $dbh->prepare($query);
1771 $sth->execute(@args);
1772 return $sth->fetchall_arrayref({});
1775 #------------------------------------------------------------#
1777 =head3 DelOrder
1779 &DelOrder($biblionumber, $ordernumber);
1781 Cancel the order with the given order and biblio numbers. It does not
1782 delete any entries in the aqorders table, it merely marks them as
1783 cancelled.
1785 =cut
1787 sub DelOrder {
1788 my ( $bibnum, $ordernumber, $delete_biblio, $reason ) = @_;
1790 my $error;
1791 my $dbh = C4::Context->dbh;
1792 my $query = "
1793 UPDATE aqorders
1794 SET datecancellationprinted=now(), orderstatus='cancelled'
1796 if($reason) {
1797 $query .= ", cancellationreason = ? ";
1799 $query .= "
1800 WHERE biblionumber=? AND ordernumber=?
1802 my $sth = $dbh->prepare($query);
1803 if($reason) {
1804 $sth->execute($reason, $bibnum, $ordernumber);
1805 } else {
1806 $sth->execute( $bibnum, $ordernumber );
1808 $sth->finish;
1810 my @itemnumbers = GetItemnumbersFromOrder( $ordernumber );
1811 foreach my $itemnumber (@itemnumbers){
1812 my $delcheck = C4::Items::DelItemCheck( $dbh, $bibnum, $itemnumber );
1814 if($delcheck != 1) {
1815 $error->{'delitem'} = 1;
1819 if($delete_biblio) {
1820 # We get the number of remaining items
1821 my $itemcount = C4::Items::GetItemsCount($bibnum);
1823 # If there are no items left,
1824 if ( $itemcount == 0 ) {
1825 # We delete the record
1826 my $delcheck = DelBiblio($bibnum);
1828 if($delcheck) {
1829 $error->{'delbiblio'} = 1;
1834 return $error;
1837 =head3 TransferOrder
1839 my $newordernumber = TransferOrder($ordernumber, $basketno);
1841 Transfer an order line to a basket.
1842 Mark $ordernumber as cancelled with an internal note 'Cancelled and transfered
1843 to BOOKSELLER on DATE' and create new order with internal note
1844 'Transfered from BOOKSELLER on DATE'.
1845 Move all attached items to the new order.
1846 Received orders cannot be transfered.
1847 Return the ordernumber of created order.
1849 =cut
1851 sub TransferOrder {
1852 my ($ordernumber, $basketno) = @_;
1854 return unless ($ordernumber and $basketno);
1856 my $order = GetOrder( $ordernumber );
1857 return if $order->{datereceived};
1858 my $basket = GetBasket($basketno);
1859 return unless $basket;
1861 my $dbh = C4::Context->dbh;
1862 my ($query, $sth, $rv);
1864 $query = q{
1865 UPDATE aqorders
1866 SET datecancellationprinted = CAST(NOW() AS date)
1867 WHERE ordernumber = ?
1869 $sth = $dbh->prepare($query);
1870 $rv = $sth->execute($ordernumber);
1872 delete $order->{'ordernumber'};
1873 delete $order->{parent_ordernumber};
1874 $order->{'basketno'} = $basketno;
1876 my $newordernumber = Koha::Acquisition::Order->new($order)->insert->{ordernumber};
1878 $query = q{
1879 UPDATE aqorders_items
1880 SET ordernumber = ?
1881 WHERE ordernumber = ?
1883 $sth = $dbh->prepare($query);
1884 $sth->execute($newordernumber, $ordernumber);
1886 $query = q{
1887 INSERT INTO aqorders_transfers (ordernumber_from, ordernumber_to)
1888 VALUES (?, ?)
1890 $sth = $dbh->prepare($query);
1891 $sth->execute($ordernumber, $newordernumber);
1893 return $newordernumber;
1896 =head2 FUNCTIONS ABOUT PARCELS
1898 =cut
1900 #------------------------------------------------------------#
1902 =head3 GetParcel
1904 @results = &GetParcel($booksellerid, $code, $date);
1906 Looks up all of the received items from the supplier with the given
1907 bookseller ID at the given date, for the given code (bookseller Invoice number). Ignores cancelled and completed orders.
1909 C<@results> is an array of references-to-hash. The keys of each element are fields from
1910 the aqorders, biblio, and biblioitems tables of the Koha database.
1912 C<@results> is sorted alphabetically by book title.
1914 =cut
1916 sub GetParcel {
1917 #gets all orders from a certain supplier, orders them alphabetically
1918 my ( $supplierid, $code, $datereceived ) = @_;
1919 my $dbh = C4::Context->dbh;
1920 my @results = ();
1921 $code .= '%'
1922 if $code; # add % if we search on a given code (otherwise, let him empty)
1923 my $strsth ="
1924 SELECT authorisedby,
1925 creationdate,
1926 aqbasket.basketno,
1927 closedate,surname,
1928 firstname,
1929 aqorders.biblionumber,
1930 aqorders.ordernumber,
1931 aqorders.parent_ordernumber,
1932 aqorders.quantity,
1933 aqorders.quantityreceived,
1934 aqorders.unitprice,
1935 aqorders.listprice,
1936 aqorders.rrp,
1937 aqorders.ecost,
1938 aqorders.gstrate,
1939 biblio.title
1940 FROM aqorders
1941 LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
1942 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
1943 LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
1944 LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
1945 WHERE
1946 aqbasket.booksellerid = ?
1947 AND aqinvoices.invoicenumber LIKE ?
1948 AND aqorders.datereceived = ? ";
1950 my @query_params = ( $supplierid, $code, $datereceived );
1951 if ( C4::Context->preference("IndependentBranches") ) {
1952 unless ( C4::Context->IsSuperLibrarian() ) {
1953 $strsth .= " and (borrowers.branchcode = ?
1954 or borrowers.branchcode = '')";
1955 push @query_params, C4::Context->userenv->{branch};
1958 $strsth .= " ORDER BY aqbasket.basketno";
1959 my $result_set = $dbh->selectall_arrayref(
1960 $strsth,
1961 { Slice => {} },
1962 @query_params);
1964 return @{$result_set};
1967 #------------------------------------------------------------#
1969 =head3 GetParcels
1971 $results = &GetParcels($bookseller, $order, $code, $datefrom, $dateto);
1973 get a lists of parcels.
1975 * Input arg :
1977 =over
1979 =item $bookseller
1980 is the bookseller this function has to get parcels.
1982 =item $order
1983 To know on what criteria the results list has to be ordered.
1985 =item $code
1986 is the booksellerinvoicenumber.
1988 =item $datefrom & $dateto
1989 to know on what date this function has to filter its search.
1991 =back
1993 * return:
1994 a pointer on a hash list containing parcel informations as such :
1996 =over
1998 =item Creation date
2000 =item Last operation
2002 =item Number of biblio
2004 =item Number of items
2006 =back
2008 =cut
2010 sub GetParcels {
2011 my ($bookseller,$order, $code, $datefrom, $dateto) = @_;
2012 my $dbh = C4::Context->dbh;
2013 my @query_params = ();
2014 my $strsth ="
2015 SELECT aqinvoices.invoicenumber,
2016 datereceived,purchaseordernumber,
2017 count(DISTINCT biblionumber) AS biblio,
2018 sum(quantity) AS itemsexpected,
2019 sum(quantityreceived) AS itemsreceived
2020 FROM aqorders LEFT JOIN aqbasket ON aqbasket.basketno = aqorders.basketno
2021 LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
2022 WHERE aqbasket.booksellerid = ? and datereceived IS NOT NULL
2024 push @query_params, $bookseller;
2026 if ( defined $code ) {
2027 $strsth .= ' and aqinvoices.invoicenumber like ? ';
2028 # add a % to the end of the code to allow stemming.
2029 push @query_params, "$code%";
2032 if ( defined $datefrom ) {
2033 $strsth .= ' and datereceived >= ? ';
2034 push @query_params, $datefrom;
2037 if ( defined $dateto ) {
2038 $strsth .= 'and datereceived <= ? ';
2039 push @query_params, $dateto;
2042 $strsth .= "group by aqinvoices.invoicenumber,datereceived ";
2044 # can't use a placeholder to place this column name.
2045 # but, we could probably be checking to make sure it is a column that will be fetched.
2046 $strsth .= "order by $order " if ($order);
2048 my $sth = $dbh->prepare($strsth);
2050 $sth->execute( @query_params );
2051 my $results = $sth->fetchall_arrayref({});
2052 return @{$results};
2055 #------------------------------------------------------------#
2057 =head3 GetLateOrders
2059 @results = &GetLateOrders;
2061 Searches for bookseller with late orders.
2063 return:
2064 the table of supplier with late issues. This table is full of hashref.
2066 =cut
2068 sub GetLateOrders {
2069 my $delay = shift;
2070 my $supplierid = shift;
2071 my $branch = shift;
2072 my $estimateddeliverydatefrom = shift;
2073 my $estimateddeliverydateto = shift;
2075 my $dbh = C4::Context->dbh;
2077 #BEWARE, order of parenthesis and LEFT JOIN is important for speed
2078 my $dbdriver = C4::Context->config("db_scheme") || "mysql";
2080 my @query_params = ();
2081 my $select = "
2082 SELECT aqbasket.basketno,
2083 aqorders.ordernumber,
2084 DATE(aqbasket.closedate) AS orderdate,
2085 aqbasket.basketname AS basketname,
2086 aqbasket.basketgroupid AS basketgroupid,
2087 aqbasketgroups.name AS basketgroupname,
2088 aqorders.rrp AS unitpricesupplier,
2089 aqorders.ecost AS unitpricelib,
2090 aqorders.claims_count AS claims_count,
2091 aqorders.claimed_date AS claimed_date,
2092 aqbudgets.budget_name AS budget,
2093 borrowers.branchcode AS branch,
2094 aqbooksellers.name AS supplier,
2095 aqbooksellers.id AS supplierid,
2096 biblio.author, biblio.title,
2097 biblioitems.publishercode AS publisher,
2098 biblioitems.publicationyear,
2099 ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
2101 my $from = "
2102 FROM
2103 aqorders LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
2104 LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
2105 LEFT JOIN aqbudgets ON aqorders.budget_id = aqbudgets.budget_id,
2106 aqbasket LEFT JOIN borrowers ON aqbasket.authorisedby = borrowers.borrowernumber
2107 LEFT JOIN aqbooksellers ON aqbasket.booksellerid = aqbooksellers.id
2108 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
2109 WHERE aqorders.basketno = aqbasket.basketno
2110 AND ( datereceived = ''
2111 OR datereceived IS NULL
2112 OR aqorders.quantityreceived < aqorders.quantity
2114 AND aqbasket.closedate IS NOT NULL
2115 AND (aqorders.datecancellationprinted IS NULL OR aqorders.datecancellationprinted='0000-00-00')
2117 my $having = "";
2118 if ($dbdriver eq "mysql") {
2119 $select .= "
2120 aqorders.quantity - COALESCE(aqorders.quantityreceived,0) AS quantity,
2121 (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
2122 DATEDIFF(CAST(now() AS date),closedate) AS latesince
2124 if ( defined $delay ) {
2125 $from .= " AND (closedate <= DATE_SUB(CAST(now() AS date),INTERVAL ? DAY)) " ;
2126 push @query_params, $delay;
2128 $having = "
2129 HAVING quantity <> 0
2130 AND unitpricesupplier <> 0
2131 AND unitpricelib <> 0
2133 } else {
2134 # FIXME: account for IFNULL as above
2135 $select .= "
2136 aqorders.quantity AS quantity,
2137 aqorders.quantity * aqorders.rrp AS subtotal,
2138 (CAST(now() AS date) - closedate) AS latesince
2140 if ( defined $delay ) {
2141 $from .= " AND (closedate <= (CAST(now() AS date) -(INTERVAL ? DAY)) ";
2142 push @query_params, $delay;
2145 if (defined $supplierid) {
2146 $from .= ' AND aqbasket.booksellerid = ? ';
2147 push @query_params, $supplierid;
2149 if (defined $branch) {
2150 $from .= ' AND borrowers.branchcode LIKE ? ';
2151 push @query_params, $branch;
2154 if ( defined $estimateddeliverydatefrom or defined $estimateddeliverydateto ) {
2155 $from .= ' AND aqbooksellers.deliverytime IS NOT NULL ';
2157 if ( defined $estimateddeliverydatefrom ) {
2158 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) >= ?';
2159 push @query_params, $estimateddeliverydatefrom;
2161 if ( defined $estimateddeliverydateto ) {
2162 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) <= ?';
2163 push @query_params, $estimateddeliverydateto;
2165 if ( defined $estimateddeliverydatefrom and not defined $estimateddeliverydateto ) {
2166 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) <= CAST(now() AS date)';
2168 if (C4::Context->preference("IndependentBranches")
2169 && !C4::Context->IsSuperLibrarian() ) {
2170 $from .= ' AND borrowers.branchcode LIKE ? ';
2171 push @query_params, C4::Context->userenv->{branch};
2173 $from .= " AND orderstatus <> 'cancelled' ";
2174 my $query = "$select $from $having\nORDER BY latesince, basketno, borrowers.branchcode, supplier";
2175 $debug and print STDERR "GetLateOrders query: $query\nGetLateOrders args: " . join(" ",@query_params);
2176 my $sth = $dbh->prepare($query);
2177 $sth->execute(@query_params);
2178 my @results;
2179 while (my $data = $sth->fetchrow_hashref) {
2180 push @results, $data;
2182 return @results;
2185 #------------------------------------------------------------#
2187 =head3 GetHistory
2189 (\@order_loop, $total_qty, $total_price, $total_qtyreceived) = GetHistory( %params );
2191 Retreives some acquisition history information
2193 params:
2194 title
2195 author
2196 name
2197 isbn
2199 from_placed_on
2200 to_placed_on
2201 basket - search both basket name and number
2202 booksellerinvoicenumber
2203 basketgroupname
2204 budget
2205 orderstatus (note that orderstatus '' will retrieve orders
2206 of any status except cancelled)
2207 biblionumber
2208 get_canceled_order (if set to a true value, cancelled orders will
2209 be included)
2211 returns:
2212 $order_loop is a list of hashrefs that each look like this:
2214 'author' => 'Twain, Mark',
2215 'basketno' => '1',
2216 'biblionumber' => '215',
2217 'count' => 1,
2218 'creationdate' => 'MM/DD/YYYY',
2219 'datereceived' => undef,
2220 'ecost' => '1.00',
2221 'id' => '1',
2222 'invoicenumber' => undef,
2223 'name' => '',
2224 'ordernumber' => '1',
2225 'quantity' => 1,
2226 'quantityreceived' => undef,
2227 'title' => 'The Adventures of Huckleberry Finn'
2229 $total_qty is the sum of all of the quantities in $order_loop
2230 $total_price is the cost of each in $order_loop times the quantity
2231 $total_qtyreceived is the sum of all of the quantityreceived entries in $order_loop
2233 =cut
2235 sub GetHistory {
2236 # don't run the query if there are no parameters (list would be too long for sure !)
2237 croak "No search params" unless @_;
2238 my %params = @_;
2239 my $title = $params{title};
2240 my $author = $params{author};
2241 my $isbn = $params{isbn};
2242 my $ean = $params{ean};
2243 my $name = $params{name};
2244 my $from_placed_on = $params{from_placed_on};
2245 my $to_placed_on = $params{to_placed_on};
2246 my $basket = $params{basket};
2247 my $booksellerinvoicenumber = $params{booksellerinvoicenumber};
2248 my $basketgroupname = $params{basketgroupname};
2249 my $budget = $params{budget};
2250 my $orderstatus = $params{orderstatus};
2251 my $biblionumber = $params{biblionumber};
2252 my $get_canceled_order = $params{get_canceled_order} || 0;
2253 my $ordernumber = $params{ordernumber};
2254 my $search_children_too = $params{search_children_too} || 0;
2256 my @order_loop;
2257 my $total_qty = 0;
2258 my $total_qtyreceived = 0;
2259 my $total_price = 0;
2261 my $dbh = C4::Context->dbh;
2262 my $query ="
2263 SELECT
2264 COALESCE(biblio.title, deletedbiblio.title) AS title,
2265 COALESCE(biblio.author, deletedbiblio.author) AS author,
2266 COALESCE(biblioitems.isbn, deletedbiblioitems.isbn) AS isbn,
2267 COALESCE(biblioitems.ean, deletedbiblioitems.ean) AS ean,
2268 aqorders.basketno,
2269 aqbasket.basketname,
2270 aqbasket.basketgroupid,
2271 aqbasketgroups.name as groupname,
2272 aqbooksellers.name,
2273 aqbasket.creationdate,
2274 aqorders.datereceived,
2275 aqorders.quantity,
2276 aqorders.quantityreceived,
2277 aqorders.ecost,
2278 aqorders.ordernumber,
2279 aqorders.invoiceid,
2280 aqinvoices.invoicenumber,
2281 aqbooksellers.id as id,
2282 aqorders.biblionumber,
2283 aqorders.orderstatus,
2284 aqorders.parent_ordernumber,
2285 aqbudgets.budget_name
2287 $query .= ", aqbudgets.budget_id AS budget" if defined $budget;
2288 $query .= "
2289 FROM aqorders
2290 LEFT JOIN aqbasket ON aqorders.basketno=aqbasket.basketno
2291 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid=aqbasketgroups.id
2292 LEFT JOIN aqbooksellers ON aqbasket.booksellerid=aqbooksellers.id
2293 LEFT JOIN biblioitems ON biblioitems.biblionumber=aqorders.biblionumber
2294 LEFT JOIN biblio ON biblio.biblionumber=aqorders.biblionumber
2295 LEFT JOIN aqbudgets ON aqorders.budget_id=aqbudgets.budget_id
2296 LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
2297 LEFT JOIN deletedbiblio ON deletedbiblio.biblionumber=aqorders.biblionumber
2298 LEFT JOIN deletedbiblioitems ON deletedbiblioitems.biblionumber=aqorders.biblionumber
2301 if ( C4::Context->preference("IndependentBranches") ) {
2302 $query .= " LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber";
2305 $query .= " WHERE 1 ";
2307 unless ($get_canceled_order or (defined $orderstatus and $orderstatus eq 'cancelled')) {
2308 $query .= " AND (datecancellationprinted is NULL or datecancellationprinted='0000-00-00') ";
2311 my @query_params = ();
2313 if ( $biblionumber ) {
2314 $query .= " AND biblio.biblionumber = ?";
2315 push @query_params, $biblionumber;
2318 if ( $title ) {
2319 $query .= " AND biblio.title LIKE ? ";
2320 $title =~ s/\s+/%/g;
2321 push @query_params, "%$title%";
2324 if ( $author ) {
2325 $query .= " AND biblio.author LIKE ? ";
2326 push @query_params, "%$author%";
2329 if ( $isbn ) {
2330 $query .= " AND biblioitems.isbn LIKE ? ";
2331 push @query_params, "%$isbn%";
2333 if ( $ean ) {
2334 $query .= " AND biblioitems.ean = ? ";
2335 push @query_params, "$ean";
2337 if ( $name ) {
2338 $query .= " AND aqbooksellers.name LIKE ? ";
2339 push @query_params, "%$name%";
2342 if ( $budget ) {
2343 $query .= " AND aqbudgets.budget_id = ? ";
2344 push @query_params, "$budget";
2347 if ( $from_placed_on ) {
2348 $query .= " AND creationdate >= ? ";
2349 push @query_params, $from_placed_on;
2352 if ( $to_placed_on ) {
2353 $query .= " AND creationdate <= ? ";
2354 push @query_params, $to_placed_on;
2357 if ( defined $orderstatus and $orderstatus ne '') {
2358 $query .= " AND aqorders.orderstatus = ? ";
2359 push @query_params, "$orderstatus";
2362 if ($basket) {
2363 if ($basket =~ m/^\d+$/) {
2364 $query .= " AND aqorders.basketno = ? ";
2365 push @query_params, $basket;
2366 } else {
2367 $query .= " AND aqbasket.basketname LIKE ? ";
2368 push @query_params, "%$basket%";
2372 if ($booksellerinvoicenumber) {
2373 $query .= " AND aqinvoices.invoicenumber LIKE ? ";
2374 push @query_params, "%$booksellerinvoicenumber%";
2377 if ($basketgroupname) {
2378 $query .= " AND aqbasketgroups.name LIKE ? ";
2379 push @query_params, "%$basketgroupname%";
2382 if ($ordernumber) {
2383 $query .= " AND (aqorders.ordernumber = ? ";
2384 push @query_params, $ordernumber;
2385 if ($search_children_too) {
2386 $query .= " OR aqorders.parent_ordernumber = ? ";
2387 push @query_params, $ordernumber;
2389 $query .= ") ";
2393 if ( C4::Context->preference("IndependentBranches") ) {
2394 unless ( C4::Context->IsSuperLibrarian() ) {
2395 $query .= " AND (borrowers.branchcode = ? OR borrowers.branchcode ='' ) ";
2396 push @query_params, C4::Context->userenv->{branch};
2399 $query .= " ORDER BY id";
2400 my $sth = $dbh->prepare($query);
2401 $sth->execute( @query_params );
2402 my $cnt = 1;
2403 while ( my $line = $sth->fetchrow_hashref ) {
2404 $line->{count} = $cnt++;
2405 $line->{toggle} = 1 if $cnt % 2;
2406 push @order_loop, $line;
2407 $total_qty += ( $line->{quantity} ) ? $line->{quantity} : 0;
2408 $total_qtyreceived += ( $line->{quantityreceived} ) ? $line->{quantityreceived} : 0;
2409 $total_price += ( $line->{quantity} and $line->{ecost} ) ? $line->{quantity} * $line->{ecost} : 0;
2411 return \@order_loop, $total_qty, $total_price, $total_qtyreceived;
2414 =head2 GetRecentAcqui
2416 $results = GetRecentAcqui($days);
2418 C<$results> is a ref to a table which containts hashref
2420 =cut
2422 sub GetRecentAcqui {
2423 my $limit = shift;
2424 my $dbh = C4::Context->dbh;
2425 my $query = "
2426 SELECT *
2427 FROM biblio
2428 ORDER BY timestamp DESC
2429 LIMIT 0,".$limit;
2431 my $sth = $dbh->prepare($query);
2432 $sth->execute;
2433 my $results = $sth->fetchall_arrayref({});
2434 return $results;
2437 #------------------------------------------------------------#
2439 =head3 AddClaim
2441 =over
2443 &AddClaim($ordernumber);
2445 Add a claim for an order
2447 =back
2449 =cut
2451 sub AddClaim {
2452 my ($ordernumber) = @_;
2453 my $dbh = C4::Context->dbh;
2454 my $query = "
2455 UPDATE aqorders SET
2456 claims_count = claims_count + 1,
2457 claimed_date = CURDATE()
2458 WHERE ordernumber = ?
2460 my $sth = $dbh->prepare($query);
2461 $sth->execute($ordernumber);
2464 =head3 GetInvoices
2466 my @invoices = GetInvoices(
2467 invoicenumber => $invoicenumber,
2468 supplierid => $supplierid,
2469 suppliername => $suppliername,
2470 shipmentdatefrom => $shipmentdatefrom, # ISO format
2471 shipmentdateto => $shipmentdateto, # ISO format
2472 billingdatefrom => $billingdatefrom, # ISO format
2473 billingdateto => $billingdateto, # ISO format
2474 isbneanissn => $isbn_or_ean_or_issn,
2475 title => $title,
2476 author => $author,
2477 publisher => $publisher,
2478 publicationyear => $publicationyear,
2479 branchcode => $branchcode,
2480 order_by => $order_by
2483 Return a list of invoices that match all given criteria.
2485 $order_by is "column_name (asc|desc)", where column_name is any of
2486 'invoicenumber', 'booksellerid', 'shipmentdate', 'billingdate', 'closedate',
2487 'shipmentcost', 'shipmentcost_budgetid'.
2489 asc is the default if omitted
2491 =cut
2493 sub GetInvoices {
2494 my %args = @_;
2496 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2497 closedate shipmentcost shipmentcost_budgetid);
2499 my $dbh = C4::Context->dbh;
2500 my $query = qq{
2501 SELECT aqinvoices.*, aqbooksellers.name AS suppliername,
2502 COUNT(
2503 DISTINCT IF(
2504 aqorders.datereceived IS NOT NULL,
2505 aqorders.biblionumber,
2506 NULL
2508 ) AS receivedbiblios,
2509 COUNT(
2510 DISTINCT IF(
2511 aqorders.subscriptionid IS NOT NULL,
2512 aqorders.subscriptionid,
2513 NULL
2515 ) AS is_linked_to_subscriptions,
2516 SUM(aqorders.quantityreceived) AS receiveditems
2517 FROM aqinvoices
2518 LEFT JOIN aqbooksellers ON aqbooksellers.id = aqinvoices.booksellerid
2519 LEFT JOIN aqorders ON aqorders.invoiceid = aqinvoices.invoiceid
2520 LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
2521 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
2522 LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2523 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
2524 LEFT JOIN subscription ON biblio.biblionumber = subscription.biblionumber
2527 my @bind_args;
2528 my @bind_strs;
2529 if($args{supplierid}) {
2530 push @bind_strs, " aqinvoices.booksellerid = ? ";
2531 push @bind_args, $args{supplierid};
2533 if($args{invoicenumber}) {
2534 push @bind_strs, " aqinvoices.invoicenumber LIKE ? ";
2535 push @bind_args, "%$args{invoicenumber}%";
2537 if($args{suppliername}) {
2538 push @bind_strs, " aqbooksellers.name LIKE ? ";
2539 push @bind_args, "%$args{suppliername}%";
2541 if($args{shipmentdatefrom}) {
2542 push @bind_strs, " aqinvoices.shipmentdate >= ? ";
2543 push @bind_args, $args{shipmentdatefrom};
2545 if($args{shipmentdateto}) {
2546 push @bind_strs, " aqinvoices.shipmentdate <= ? ";
2547 push @bind_args, $args{shipmentdateto};
2549 if($args{billingdatefrom}) {
2550 push @bind_strs, " aqinvoices.billingdate >= ? ";
2551 push @bind_args, $args{billingdatefrom};
2553 if($args{billingdateto}) {
2554 push @bind_strs, " aqinvoices.billingdate <= ? ";
2555 push @bind_args, $args{billingdateto};
2557 if($args{isbneanissn}) {
2558 push @bind_strs, " (biblioitems.isbn LIKE CONCAT('%', ?, '%') OR biblioitems.ean LIKE CONCAT('%', ?, '%') OR biblioitems.issn LIKE CONCAT('%', ?, '%') ) ";
2559 push @bind_args, $args{isbneanissn}, $args{isbneanissn}, $args{isbneanissn};
2561 if($args{title}) {
2562 push @bind_strs, " biblio.title LIKE CONCAT('%', ?, '%') ";
2563 push @bind_args, $args{title};
2565 if($args{author}) {
2566 push @bind_strs, " biblio.author LIKE CONCAT('%', ?, '%') ";
2567 push @bind_args, $args{author};
2569 if($args{publisher}) {
2570 push @bind_strs, " biblioitems.publishercode LIKE CONCAT('%', ?, '%') ";
2571 push @bind_args, $args{publisher};
2573 if($args{publicationyear}) {
2574 push @bind_strs, " ((biblioitems.publicationyear LIKE CONCAT('%', ?, '%')) OR (biblio.copyrightdate LIKE CONCAT('%', ?, '%'))) ";
2575 push @bind_args, $args{publicationyear}, $args{publicationyear};
2577 if($args{branchcode}) {
2578 push @bind_strs, " borrowers.branchcode = ? ";
2579 push @bind_args, $args{branchcode};
2582 $query .= " WHERE " . join(" AND ", @bind_strs) if @bind_strs;
2583 $query .= " GROUP BY aqinvoices.invoiceid ";
2585 if($args{order_by}) {
2586 my ($column, $direction) = split / /, $args{order_by};
2587 if(grep /^$column$/, @columns) {
2588 $direction ||= 'ASC';
2589 $query .= " ORDER BY $column $direction";
2593 my $sth = $dbh->prepare($query);
2594 $sth->execute(@bind_args);
2596 my $results = $sth->fetchall_arrayref({});
2597 return @$results;
2600 =head3 GetInvoice
2602 my $invoice = GetInvoice($invoiceid);
2604 Get informations about invoice with given $invoiceid
2606 Return a hash filled with aqinvoices.* fields
2608 =cut
2610 sub GetInvoice {
2611 my ($invoiceid) = @_;
2612 my $invoice;
2614 return unless $invoiceid;
2616 my $dbh = C4::Context->dbh;
2617 my $query = qq{
2618 SELECT *
2619 FROM aqinvoices
2620 WHERE invoiceid = ?
2622 my $sth = $dbh->prepare($query);
2623 $sth->execute($invoiceid);
2625 $invoice = $sth->fetchrow_hashref;
2626 return $invoice;
2629 =head3 GetInvoiceDetails
2631 my $invoice = GetInvoiceDetails($invoiceid)
2633 Return informations about an invoice + the list of related order lines
2635 Orders informations are in $invoice->{orders} (array ref)
2637 =cut
2639 sub GetInvoiceDetails {
2640 my ($invoiceid) = @_;
2642 if ( !defined $invoiceid ) {
2643 carp 'GetInvoiceDetails called without an invoiceid';
2644 return;
2647 my $dbh = C4::Context->dbh;
2648 my $query = q{
2649 SELECT aqinvoices.*, aqbooksellers.name AS suppliername
2650 FROM aqinvoices
2651 LEFT JOIN aqbooksellers ON aqinvoices.booksellerid = aqbooksellers.id
2652 WHERE invoiceid = ?
2654 my $sth = $dbh->prepare($query);
2655 $sth->execute($invoiceid);
2657 my $invoice = $sth->fetchrow_hashref;
2659 $query = q{
2660 SELECT aqorders.*, biblio.*, aqbasket.basketname
2661 FROM aqorders
2662 LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
2663 LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2664 WHERE invoiceid = ?
2666 $sth = $dbh->prepare($query);
2667 $sth->execute($invoiceid);
2668 $invoice->{orders} = $sth->fetchall_arrayref({});
2669 $invoice->{orders} ||= []; # force an empty arrayref if fetchall_arrayref fails
2671 return $invoice;
2674 =head3 AddInvoice
2676 my $invoiceid = AddInvoice(
2677 invoicenumber => $invoicenumber,
2678 booksellerid => $booksellerid,
2679 shipmentdate => $shipmentdate,
2680 billingdate => $billingdate,
2681 closedate => $closedate,
2682 shipmentcost => $shipmentcost,
2683 shipmentcost_budgetid => $shipmentcost_budgetid
2686 Create a new invoice and return its id or undef if it fails.
2688 =cut
2690 sub AddInvoice {
2691 my %invoice = @_;
2693 return unless(%invoice and $invoice{invoicenumber});
2695 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2696 closedate shipmentcost shipmentcost_budgetid);
2698 my @set_strs;
2699 my @set_args;
2700 foreach my $key (keys %invoice) {
2701 if(0 < grep(/^$key$/, @columns)) {
2702 push @set_strs, "$key = ?";
2703 push @set_args, ($invoice{$key} || undef);
2707 my $rv;
2708 if(@set_args > 0) {
2709 my $dbh = C4::Context->dbh;
2710 my $query = "INSERT INTO aqinvoices SET ";
2711 $query .= join (",", @set_strs);
2712 my $sth = $dbh->prepare($query);
2713 $rv = $sth->execute(@set_args);
2714 if($rv) {
2715 $rv = $dbh->last_insert_id(undef, undef, 'aqinvoices', undef);
2718 return $rv;
2721 =head3 ModInvoice
2723 ModInvoice(
2724 invoiceid => $invoiceid, # Mandatory
2725 invoicenumber => $invoicenumber,
2726 booksellerid => $booksellerid,
2727 shipmentdate => $shipmentdate,
2728 billingdate => $billingdate,
2729 closedate => $closedate,
2730 shipmentcost => $shipmentcost,
2731 shipmentcost_budgetid => $shipmentcost_budgetid
2734 Modify an invoice, invoiceid is mandatory.
2736 Return undef if it fails.
2738 =cut
2740 sub ModInvoice {
2741 my %invoice = @_;
2743 return unless(%invoice and $invoice{invoiceid});
2745 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2746 closedate shipmentcost shipmentcost_budgetid);
2748 my @set_strs;
2749 my @set_args;
2750 foreach my $key (keys %invoice) {
2751 if(0 < grep(/^$key$/, @columns)) {
2752 push @set_strs, "$key = ?";
2753 push @set_args, ($invoice{$key} || undef);
2757 my $dbh = C4::Context->dbh;
2758 my $query = "UPDATE aqinvoices SET ";
2759 $query .= join(",", @set_strs);
2760 $query .= " WHERE invoiceid = ?";
2762 my $sth = $dbh->prepare($query);
2763 $sth->execute(@set_args, $invoice{invoiceid});
2766 =head3 CloseInvoice
2768 CloseInvoice($invoiceid);
2770 Close an invoice.
2772 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => undef);
2774 =cut
2776 sub CloseInvoice {
2777 my ($invoiceid) = @_;
2779 return unless $invoiceid;
2781 my $dbh = C4::Context->dbh;
2782 my $query = qq{
2783 UPDATE aqinvoices
2784 SET closedate = CAST(NOW() AS DATE)
2785 WHERE invoiceid = ?
2787 my $sth = $dbh->prepare($query);
2788 $sth->execute($invoiceid);
2791 =head3 ReopenInvoice
2793 ReopenInvoice($invoiceid);
2795 Reopen an invoice
2797 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => C4::Dates->new()->output('iso'))
2799 =cut
2801 sub ReopenInvoice {
2802 my ($invoiceid) = @_;
2804 return unless $invoiceid;
2806 my $dbh = C4::Context->dbh;
2807 my $query = qq{
2808 UPDATE aqinvoices
2809 SET closedate = NULL
2810 WHERE invoiceid = ?
2812 my $sth = $dbh->prepare($query);
2813 $sth->execute($invoiceid);
2816 =head3 DelInvoice
2818 DelInvoice($invoiceid);
2820 Delete an invoice if there are no items attached to it.
2822 =cut
2824 sub DelInvoice {
2825 my ($invoiceid) = @_;
2827 return unless $invoiceid;
2829 my $dbh = C4::Context->dbh;
2830 my $query = qq{
2831 SELECT COUNT(*)
2832 FROM aqorders
2833 WHERE invoiceid = ?
2835 my $sth = $dbh->prepare($query);
2836 $sth->execute($invoiceid);
2837 my $res = $sth->fetchrow_arrayref;
2838 if ( $res && $res->[0] == 0 ) {
2839 $query = qq{
2840 DELETE FROM aqinvoices
2841 WHERE invoiceid = ?
2843 my $sth = $dbh->prepare($query);
2844 return ( $sth->execute($invoiceid) > 0 );
2846 return;
2849 =head3 MergeInvoices
2851 MergeInvoices($invoiceid, \@sourceids);
2853 Merge the invoices identified by the IDs in \@sourceids into
2854 the invoice identified by $invoiceid.
2856 =cut
2858 sub MergeInvoices {
2859 my ($invoiceid, $sourceids) = @_;
2861 return unless $invoiceid;
2862 foreach my $sourceid (@$sourceids) {
2863 next if $sourceid == $invoiceid;
2864 my $source = GetInvoiceDetails($sourceid);
2865 foreach my $order (@{$source->{'orders'}}) {
2866 $order->{'invoiceid'} = $invoiceid;
2867 ModOrder($order);
2869 DelInvoice($source->{'invoiceid'});
2871 return;
2874 =head3 GetBiblioCountByBasketno
2876 $biblio_count = &GetBiblioCountByBasketno($basketno);
2878 Looks up the biblio's count that has basketno value $basketno
2880 Returns a quantity
2882 =cut
2884 sub GetBiblioCountByBasketno {
2885 my ($basketno) = @_;
2886 my $dbh = C4::Context->dbh;
2887 my $query = "
2888 SELECT COUNT( DISTINCT( biblionumber ) )
2889 FROM aqorders
2890 WHERE basketno = ?
2891 AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')
2894 my $sth = $dbh->prepare($query);
2895 $sth->execute($basketno);
2896 return $sth->fetchrow;
2900 __END__
2902 =head1 AUTHOR
2904 Koha Development Team <http://koha-community.org/>
2906 =cut