Bug 15395: Allow correct handling of plural translation
[koha.git] / C4 / Acquisition.pm
blob8646677360f51c68992d17af75608ec8579c4fc5
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
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.
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.
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>.
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::Booksellers;
32 use Koha::Acquisition::Orders;
33 use Koha::Biblios;
34 use Koha::Exceptions;
35 use Koha::Items;
36 use Koha::Number::Price;
37 use Koha::Libraries;
38 use Koha::CsvProfiles;
39 use Koha::Patrons;
41 use C4::Koha;
43 use MARC::Field;
44 use MARC::Record;
46 use Time::localtime;
48 use vars qw(@ISA @EXPORT);
50 BEGIN {
51 require Exporter;
52 @ISA = qw(Exporter);
53 @EXPORT = qw(
54 &GetBasket &NewBasket &CloseBasket &ReopenBasket &DelBasket &ModBasket
55 &GetBasketAsCSV &GetBasketGroupAsCSV
56 &GetBasketsByBookseller &GetBasketsByBasketgroup
57 &GetBasketsInfosByBookseller
59 &GetBasketUsers &ModBasketUsers
60 &CanUserManageBasket
62 &ModBasketHeader
64 &ModBasketgroup &NewBasketgroup &DelBasketgroup &GetBasketgroup &CloseBasketgroup
65 &GetBasketgroups &ReOpenBasketgroup
67 &DelOrder &ModOrder &GetOrder &GetOrders &GetOrdersByBiblionumber
68 &GetLateOrders &GetOrderFromItemnumber
69 &SearchOrders &GetHistory &GetRecentAcqui
70 &ModReceiveOrder &CancelReceipt
71 &TransferOrder
72 &GetLastOrderNotReceivedFromSubscriptionid &GetLastOrderReceivedFromSubscriptionid
73 &ModItemOrder
75 &GetParcels
77 &GetInvoices
78 &GetInvoice
79 &GetInvoiceDetails
80 &AddInvoice
81 &ModInvoice
82 &CloseInvoice
83 &ReopenInvoice
84 &DelInvoice
85 &MergeInvoices
87 &GetItemnumbersFromOrder
89 &AddClaim
90 &GetBiblioCountByBasketno
92 &GetOrderUsers
93 &ModOrderUsers
94 &NotifyOrderUsers
96 &FillWithDefaultValues
104 sub GetOrderFromItemnumber {
105 my ($itemnumber) = @_;
106 my $dbh = C4::Context->dbh;
107 my $query = qq|
109 SELECT * from aqorders LEFT JOIN aqorders_items
110 ON ( aqorders.ordernumber = aqorders_items.ordernumber )
111 WHERE itemnumber = ? |;
113 my $sth = $dbh->prepare($query);
115 # $sth->trace(3);
117 $sth->execute($itemnumber);
119 my $order = $sth->fetchrow_hashref;
120 return ( $order );
124 # Returns the itemnumber(s) associated with the ordernumber given in parameter
125 sub GetItemnumbersFromOrder {
126 my ($ordernumber) = @_;
127 my $dbh = C4::Context->dbh;
128 my $query = "SELECT itemnumber FROM aqorders_items WHERE ordernumber=?";
129 my $sth = $dbh->prepare($query);
130 $sth->execute($ordernumber);
131 my @tab;
133 while (my $order = $sth->fetchrow_hashref) {
134 push @tab, $order->{'itemnumber'};
137 return @tab;
146 =head1 NAME
148 C4::Acquisition - Koha functions for dealing with orders and acquisitions
150 =head1 SYNOPSIS
152 use C4::Acquisition;
154 =head1 DESCRIPTION
156 The functions in this module deal with acquisitions, managing book
157 orders, basket and parcels.
159 =head1 FUNCTIONS
161 =head2 FUNCTIONS ABOUT BASKETS
163 =head3 GetBasket
165 $aqbasket = &GetBasket($basketnumber);
167 get all basket informations in aqbasket for a given basket
169 B<returns:> informations for a given basket returned as a hashref.
171 =cut
173 sub GetBasket {
174 my ($basketno) = @_;
175 my $dbh = C4::Context->dbh;
176 my $query = "
177 SELECT aqbasket.*,
178 concat( b.firstname,' ',b.surname) AS authorisedbyname
179 FROM aqbasket
180 LEFT JOIN borrowers b ON aqbasket.authorisedby=b.borrowernumber
181 WHERE basketno=?
183 my $sth=$dbh->prepare($query);
184 $sth->execute($basketno);
185 my $basket = $sth->fetchrow_hashref;
186 return ( $basket );
189 #------------------------------------------------------------#
191 =head3 NewBasket
193 $basket = &NewBasket( $booksellerid, $authorizedby, $basketname,
194 $basketnote, $basketbooksellernote, $basketcontractnumber, $deliveryplace, $billingplace, $is_standing, $create_items );
196 Create a new basket in aqbasket table
198 =over
200 =item C<$booksellerid> is a foreign key in the aqbasket table
202 =item C<$authorizedby> is the username of who created the basket
204 =back
206 The other parameters are optional, see ModBasketHeader for more info on them.
208 =cut
210 sub NewBasket {
211 my ( $booksellerid, $authorisedby, $basketname, $basketnote,
212 $basketbooksellernote, $basketcontractnumber, $deliveryplace,
213 $billingplace, $is_standing, $create_items ) = @_;
214 my $dbh = C4::Context->dbh;
215 my $query =
216 'INSERT INTO aqbasket (creationdate,booksellerid,authorisedby) '
217 . 'VALUES (now(),?,?)';
218 $dbh->do( $query, {}, $booksellerid, $authorisedby );
220 my $basket = $dbh->{mysql_insertid};
221 $basketname ||= q{}; # default to empty strings
222 $basketnote ||= q{};
223 $basketbooksellernote ||= q{};
224 ModBasketHeader( $basket, $basketname, $basketnote, $basketbooksellernote,
225 $basketcontractnumber, $booksellerid, $deliveryplace, $billingplace, $is_standing, $create_items );
226 return $basket;
229 #------------------------------------------------------------#
231 =head3 CloseBasket
233 &CloseBasket($basketno);
235 close a basket (becomes unmodifiable, except for receives)
237 =cut
239 sub CloseBasket {
240 my ($basketno) = @_;
241 my $dbh = C4::Context->dbh;
242 $dbh->do('UPDATE aqbasket SET closedate=now() WHERE basketno=?', {}, $basketno );
244 $dbh->do(
245 q{UPDATE aqorders SET orderstatus = 'ordered' WHERE basketno = ? AND orderstatus NOT IN ( 'complete', 'cancelled')},
246 {}, $basketno
248 return;
251 =head3 ReopenBasket
253 &ReopenBasket($basketno);
255 reopen a basket
257 =cut
259 sub ReopenBasket {
260 my ($basketno) = @_;
261 my $dbh = C4::Context->dbh;
262 $dbh->do( q{UPDATE aqbasket SET closedate=NULL WHERE basketno=?}, {}, $basketno );
264 $dbh->do( q{
265 UPDATE aqorders
266 SET orderstatus = 'new'
267 WHERE basketno = ?
268 AND orderstatus NOT IN ( 'complete', 'cancelled' )
269 }, {}, $basketno);
270 return;
273 #------------------------------------------------------------#
275 =head3 GetBasketAsCSV
277 &GetBasketAsCSV($basketno);
279 Export a basket as CSV
281 $cgi parameter is needed for column name translation
283 =cut
285 sub GetBasketAsCSV {
286 my ($basketno, $cgi, $csv_profile_id) = @_;
287 my $basket = GetBasket($basketno);
288 my @orders = GetOrders($basketno);
289 my $contract = GetContract({
290 contractnumber => $basket->{'contractnumber'}
293 my $template = C4::Templates::gettemplate("acqui/csv/basket.tt", "intranet", $cgi);
294 my @rows;
295 if ($csv_profile_id) {
296 my $csv_profile = Koha::CsvProfiles->find( $csv_profile_id );
297 Koha::Exceptions::ObjectNotFound->throw( 'There is no valid csv profile given') unless $csv_profile;
299 my $csv = Text::CSV_XS->new({'quote_char'=>'"','escape_char'=>'"','sep_char'=>$csv_profile->csv_separator,'binary'=>1});
300 my $csv_profile_content = $csv_profile->content;
301 my ( @headers, @fields );
302 while ( $csv_profile_content =~ /
303 ([^=\|]+) # header
305 ([^\|]*) # fieldname (table.row or row)
306 \|? /gxms
308 my $header = $1;
309 my $field = ($2 eq '') ? $1 : $2;
311 $header =~ s/^\s+|\s+$//g; # Trim whitespaces
312 push @headers, $header;
314 $field =~ s/[^\.]*\.{1}//; # Remove the table name if exists.
315 $field =~ s/^\s+|\s+$//g; # Trim whitespaces
316 push @fields, $field;
318 for my $order (@orders) {
319 my @row;
320 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
321 my $biblioitem = $biblio->biblioitem;
322 $order = { %$order, %{ $biblioitem->unblessed } };
323 if ($contract) {
324 $order = {%$order, %$contract};
326 $order = {%$order, %$basket, %{ $biblio->unblessed }};
327 for my $field (@fields) {
328 push @row, $order->{$field};
330 push @rows, \@row;
332 my $content = join( $csv_profile->csv_separator, @headers ) . "\n";
333 for my $row ( @rows ) {
334 $csv->combine(@$row);
335 my $string = $csv->string;
336 $content .= $string . "\n";
338 return $content;
340 else {
341 foreach my $order (@orders) {
342 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
343 my $biblioitem = $biblio->biblioitem;
344 my $row = {
345 contractname => $contract->{'contractname'},
346 ordernumber => $order->{'ordernumber'},
347 entrydate => $order->{'entrydate'},
348 isbn => $order->{'isbn'},
349 author => $biblio->author,
350 title => $biblio->title,
351 publicationyear => $biblioitem->publicationyear,
352 publishercode => $biblioitem->publishercode,
353 collectiontitle => $biblioitem->collectiontitle,
354 notes => $order->{'order_vendornote'},
355 quantity => $order->{'quantity'},
356 rrp => $order->{'rrp'},
358 for my $place ( qw( deliveryplace billingplace ) ) {
359 if ( my $library = Koha::Libraries->find( $row->{deliveryplace} ) ) {
360 $row->{$place} = $library->branchname
363 foreach(qw(
364 contractname author title publishercode collectiontitle notes
365 deliveryplace billingplace
366 ) ) {
367 # Double the quotes to not be interpreted as a field end
368 $row->{$_} =~ s/"/""/g if $row->{$_};
370 push @rows, $row;
373 @rows = sort {
374 if(defined $a->{publishercode} and defined $b->{publishercode}) {
375 $a->{publishercode} cmp $b->{publishercode};
377 } @rows;
379 $template->param(rows => \@rows);
381 return $template->output;
386 =head3 GetBasketGroupAsCSV
388 &GetBasketGroupAsCSV($basketgroupid);
390 Export a basket group as CSV
392 $cgi parameter is needed for column name translation
394 =cut
396 sub GetBasketGroupAsCSV {
397 my ($basketgroupid, $cgi) = @_;
398 my $baskets = GetBasketsByBasketgroup($basketgroupid);
400 my $template = C4::Templates::gettemplate('acqui/csv/basketgroup.tt', 'intranet', $cgi);
402 my @rows;
403 for my $basket (@$baskets) {
404 my @orders = GetOrders( $basket->{basketno} );
405 my $contract = GetContract({
406 contractnumber => $basket->{contractnumber}
408 my $bookseller = Koha::Acquisition::Booksellers->find( $basket->{booksellerid} );
409 my $basketgroup = GetBasketgroup( $$basket{basketgroupid} );
411 foreach my $order (@orders) {
412 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
413 my $biblioitem = $biblio->biblioitem;
414 my $row = {
415 clientnumber => $bookseller->accountnumber,
416 basketname => $basket->{basketname},
417 ordernumber => $order->{ordernumber},
418 author => $biblio->author,
419 title => $biblio->title,
420 publishercode => $biblioitem->publishercode,
421 publicationyear => $biblioitem->publicationyear,
422 collectiontitle => $biblioitem->collectiontitle,
423 isbn => $order->{isbn},
424 quantity => $order->{quantity},
425 rrp_tax_included => $order->{rrp_tax_included},
426 rrp_tax_excluded => $order->{rrp_tax_excluded},
427 discount => $bookseller->discount,
428 ecost_tax_included => $order->{ecost_tax_included},
429 ecost_tax_excluded => $order->{ecost_tax_excluded},
430 notes => $order->{order_vendornote},
431 entrydate => $order->{entrydate},
432 booksellername => $bookseller->name,
433 bookselleraddress => $bookseller->address1,
434 booksellerpostal => $bookseller->postal,
435 contractnumber => $contract->{contractnumber},
436 contractname => $contract->{contractname},
438 my $temp = {
439 basketgroupdeliveryplace => $basketgroup->{deliveryplace},
440 basketgroupbillingplace => $basketgroup->{billingplace},
441 basketdeliveryplace => $basket->{deliveryplace},
442 basketbillingplace => $basket->{billingplace},
444 for my $place (qw( basketgroupdeliveryplace basketgroupbillingplace basketdeliveryplace basketbillingplace )) {
445 if ( my $library = Koha::Libraries->find( $temp->{$place} ) ) {
446 $row->{$place} = $library->branchname;
449 foreach(qw(
450 basketname author title publishercode collectiontitle notes
451 booksellername bookselleraddress booksellerpostal contractname
452 basketgroupdeliveryplace basketgroupbillingplace
453 basketdeliveryplace basketbillingplace
454 ) ) {
455 # Double the quotes to not be interpreted as a field end
456 $row->{$_} =~ s/"/""/g if $row->{$_};
458 push @rows, $row;
461 $template->param(rows => \@rows);
463 return $template->output;
467 =head3 CloseBasketgroup
469 &CloseBasketgroup($basketgroupno);
471 close a basketgroup
473 =cut
475 sub CloseBasketgroup {
476 my ($basketgroupno) = @_;
477 my $dbh = C4::Context->dbh;
478 my $sth = $dbh->prepare("
479 UPDATE aqbasketgroups
480 SET closed=1
481 WHERE id=?
483 $sth->execute($basketgroupno);
486 #------------------------------------------------------------#
488 =head3 ReOpenBaskergroup($basketgroupno)
490 &ReOpenBaskergroup($basketgroupno);
492 reopen a basketgroup
494 =cut
496 sub ReOpenBasketgroup {
497 my ($basketgroupno) = @_;
498 my $dbh = C4::Context->dbh;
499 my $sth = $dbh->prepare("
500 UPDATE aqbasketgroups
501 SET closed=0
502 WHERE id=?
504 $sth->execute($basketgroupno);
507 #------------------------------------------------------------#
510 =head3 DelBasket
512 &DelBasket($basketno);
514 Deletes the basket that has basketno field $basketno in the aqbasket table.
516 =over
518 =item C<$basketno> is the primary key of the basket in the aqbasket table.
520 =back
522 =cut
524 sub DelBasket {
525 my ( $basketno ) = @_;
526 my $query = "DELETE FROM aqbasket WHERE basketno=?";
527 my $dbh = C4::Context->dbh;
528 my $sth = $dbh->prepare($query);
529 $sth->execute($basketno);
530 return;
533 #------------------------------------------------------------#
535 =head3 ModBasket
537 &ModBasket($basketinfo);
539 Modifies a basket, using a hashref $basketinfo for the relevant information, only $basketinfo->{'basketno'} is required.
541 =over
543 =item C<$basketno> is the primary key of the basket in the aqbasket table.
545 =back
547 =cut
549 sub ModBasket {
550 my $basketinfo = shift;
551 my $query = "UPDATE aqbasket SET ";
552 my @params;
553 foreach my $key (keys %$basketinfo){
554 if ($key ne 'basketno'){
555 $query .= "$key=?, ";
556 push(@params, $basketinfo->{$key} || undef );
559 # get rid of the "," at the end of $query
560 if (substr($query, length($query)-2) eq ', '){
561 chop($query);
562 chop($query);
563 $query .= ' ';
565 $query .= "WHERE basketno=?";
566 push(@params, $basketinfo->{'basketno'});
567 my $dbh = C4::Context->dbh;
568 my $sth = $dbh->prepare($query);
569 $sth->execute(@params);
571 return;
574 #------------------------------------------------------------#
576 =head3 ModBasketHeader
578 &ModBasketHeader($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid);
580 Modifies a basket's header.
582 =over
584 =item C<$basketno> is the "basketno" field in the "aqbasket" table;
586 =item C<$basketname> is the "basketname" field in the "aqbasket" table;
588 =item C<$note> is the "note" field in the "aqbasket" table;
590 =item C<$booksellernote> is the "booksellernote" field in the "aqbasket" table;
592 =item C<$contractnumber> is the "contractnumber" (foreign) key in the "aqbasket" table.
594 =item C<$booksellerid> is the id (foreign) key in the "aqbooksellers" table for the vendor.
596 =item C<$deliveryplace> is the "deliveryplace" field in the aqbasket table.
598 =item C<$billingplace> is the "billingplace" field in the aqbasket table.
600 =item C<$is_standing> is the "is_standing" field in the aqbasket table.
602 =item C<$create_items> should be set to 'ordering', 'receiving' or 'cataloguing' (or undef, in which
603 case the AcqCreateItem syspref takes precedence).
605 =back
607 =cut
609 sub ModBasketHeader {
610 my ($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid, $deliveryplace, $billingplace, $is_standing, $create_items) = @_;
612 $is_standing ||= 0;
613 my $query = qq{
614 UPDATE aqbasket
615 SET basketname=?, note=?, booksellernote=?, booksellerid=?, deliveryplace=?, billingplace=?, is_standing=?, create_items=?
616 WHERE basketno=?
619 my $dbh = C4::Context->dbh;
620 my $sth = $dbh->prepare($query);
621 $sth->execute($basketname, $note, $booksellernote, $booksellerid, $deliveryplace, $billingplace, $is_standing, $create_items || undef, $basketno);
623 if ( $contractnumber ) {
624 my $query2 ="UPDATE aqbasket SET contractnumber=? WHERE basketno=?";
625 my $sth2 = $dbh->prepare($query2);
626 $sth2->execute($contractnumber,$basketno);
628 return;
631 #------------------------------------------------------------#
633 =head3 GetBasketsByBookseller
635 @results = &GetBasketsByBookseller($booksellerid, $extra);
637 Returns a list of hashes of all the baskets that belong to bookseller 'booksellerid'.
639 =over
641 =item C<$booksellerid> is the 'id' field of the bookseller in the aqbooksellers table
643 =item C<$extra> is the extra sql parameters, can be
645 $extra->{groupby}: group baskets by column
646 ex. $extra->{groupby} = aqbasket.basketgroupid
647 $extra->{orderby}: order baskets by column
648 $extra->{limit}: limit number of results (can be helpful for pagination)
650 =back
652 =cut
654 sub GetBasketsByBookseller {
655 my ($booksellerid, $extra) = @_;
656 my $query = "SELECT * FROM aqbasket WHERE booksellerid=?";
657 if ($extra){
658 if ($extra->{groupby}) {
659 $query .= " GROUP by $extra->{groupby}";
661 if ($extra->{orderby}){
662 $query .= " ORDER by $extra->{orderby}";
664 if ($extra->{limit}){
665 $query .= " LIMIT $extra->{limit}";
668 my $dbh = C4::Context->dbh;
669 my $sth = $dbh->prepare($query);
670 $sth->execute($booksellerid);
671 return $sth->fetchall_arrayref({});
674 =head3 GetBasketsInfosByBookseller
676 my $baskets = GetBasketsInfosByBookseller($supplierid, $allbaskets);
678 The optional second parameter allbaskets is a boolean allowing you to
679 select all baskets from the supplier; by default only active baskets (open or
680 closed but still something to receive) are returned.
682 Returns in a arrayref of hashref all about booksellers baskets, plus:
683 total_biblios: Number of distinct biblios in basket
684 total_items: Number of items in basket
685 expected_items: Number of non-received items in basket
687 =cut
689 sub GetBasketsInfosByBookseller {
690 my ($supplierid, $allbaskets) = @_;
692 return unless $supplierid;
694 my $dbh = C4::Context->dbh;
695 my $query = q{
696 SELECT aqbasket.basketno, aqbasket.basketname, aqbasket.note, aqbasket.booksellernote, aqbasket.contractnumber, aqbasket.creationdate, aqbasket.closedate, aqbasket.booksellerid, aqbasket.authorisedby, aqbasket.booksellerinvoicenumber, aqbasket.basketgroupid, aqbasket.deliveryplace, aqbasket.billingplace, aqbasket.branch, aqbasket.is_standing, aqbasket.create_items,
697 SUM(aqorders.quantity) AS total_items,
698 SUM(
699 IF ( aqorders.orderstatus = 'cancelled', aqorders.quantity, 0 )
700 ) AS total_items_cancelled,
701 COUNT(DISTINCT aqorders.biblionumber) AS total_biblios,
702 SUM(
703 IF(aqorders.datereceived IS NULL
704 AND (aqorders.datecancellationprinted IS NULL OR aqorders.datecancellationprinted='0000-00-00')
705 , aqorders.quantity
706 , 0)
707 ) AS expected_items
708 FROM aqbasket
709 LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
710 WHERE booksellerid = ?};
712 $query.=" GROUP BY aqbasket.basketno, aqbasket.basketname, aqbasket.note, aqbasket.booksellernote, aqbasket.contractnumber, aqbasket.creationdate, aqbasket.closedate, aqbasket.booksellerid, aqbasket.authorisedby, aqbasket.booksellerinvoicenumber, aqbasket.basketgroupid, aqbasket.deliveryplace, aqbasket.billingplace, aqbasket.branch, aqbasket.is_standing, aqbasket.create_items";
714 unless ( $allbaskets ) {
715 # Don't show the basket if it's NOT CLOSED or is FULLY RECEIVED
716 $query.=" HAVING (closedate IS NULL OR (
717 SUM(
718 IF(aqorders.datereceived IS NULL
719 AND (aqorders.datecancellationprinted IS NULL OR aqorders.datecancellationprinted='0000-00-00')
720 , aqorders.quantity
721 , 0)
722 ) > 0))"
725 my $sth = $dbh->prepare($query);
726 $sth->execute($supplierid);
727 my $baskets = $sth->fetchall_arrayref({});
729 # Retrieve the number of biblios cancelled
730 my $cancelled_biblios = $dbh->selectall_hashref( q|
731 SELECT COUNT(DISTINCT(biblionumber)) AS total_biblios_cancelled, aqbasket.basketno
732 FROM aqbasket
733 LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
734 WHERE booksellerid = ?
735 AND aqorders.orderstatus = 'cancelled'
736 GROUP BY aqbasket.basketno
737 |, 'basketno', {}, $supplierid );
738 map {
739 $_->{total_biblios_cancelled} = $cancelled_biblios->{$_->{basketno}}{total_biblios_cancelled} || 0
740 } @$baskets;
742 return $baskets;
745 =head3 GetBasketUsers
747 $basketusers_ids = &GetBasketUsers($basketno);
749 Returns a list of all borrowernumbers that are in basket users list
751 =cut
753 sub GetBasketUsers {
754 my $basketno = shift;
756 return unless $basketno;
758 my $query = qq{
759 SELECT borrowernumber
760 FROM aqbasketusers
761 WHERE basketno = ?
763 my $dbh = C4::Context->dbh;
764 my $sth = $dbh->prepare($query);
765 $sth->execute($basketno);
766 my $results = $sth->fetchall_arrayref( {} );
768 my @borrowernumbers;
769 foreach (@$results) {
770 push @borrowernumbers, $_->{'borrowernumber'};
773 return @borrowernumbers;
776 =head3 ModBasketUsers
778 my @basketusers_ids = (1, 2, 3);
779 &ModBasketUsers($basketno, @basketusers_ids);
781 Delete all users from basket users list, and add users in C<@basketusers_ids>
782 to this users list.
784 =cut
786 sub ModBasketUsers {
787 my ($basketno, @basketusers_ids) = @_;
789 return unless $basketno;
791 my $dbh = C4::Context->dbh;
792 my $query = qq{
793 DELETE FROM aqbasketusers
794 WHERE basketno = ?
796 my $sth = $dbh->prepare($query);
797 $sth->execute($basketno);
799 $query = qq{
800 INSERT INTO aqbasketusers (basketno, borrowernumber)
801 VALUES (?, ?)
803 $sth = $dbh->prepare($query);
804 foreach my $basketuser_id (@basketusers_ids) {
805 $sth->execute($basketno, $basketuser_id);
807 return;
810 =head3 CanUserManageBasket
812 my $bool = CanUserManageBasket($borrower, $basket[, $userflags]);
813 my $bool = CanUserManageBasket($borrowernumber, $basketno[, $userflags]);
815 Check if a borrower can manage a basket, according to system preference
816 AcqViewBaskets, user permissions and basket properties (creator, users list,
817 branch).
819 First parameter can be either a borrowernumber or a hashref as returned by
820 Koha::Patron->unblessed
822 Second parameter can be either a basketno or a hashref as returned by
823 C4::Acquisition::GetBasket.
825 The third parameter is optional. If given, it should be a hashref as returned
826 by C4::Auth::getuserflags. If not, getuserflags is called.
828 If user is authorised to manage basket, returns 1.
829 Otherwise returns 0.
831 =cut
833 sub CanUserManageBasket {
834 my ($borrower, $basket, $userflags) = @_;
836 if (!ref $borrower) {
837 # FIXME This needs to be replaced
838 # We should not accept both scalar and array
839 # Tests need to be updated
840 $borrower = Koha::Patrons->find( $borrower )->unblessed;
842 if (!ref $basket) {
843 $basket = GetBasket($basket);
846 return 0 unless ($basket and $borrower);
848 my $borrowernumber = $borrower->{borrowernumber};
849 my $basketno = $basket->{basketno};
851 my $AcqViewBaskets = C4::Context->preference('AcqViewBaskets');
853 if (!defined $userflags) {
854 my $dbh = C4::Context->dbh;
855 my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE borrowernumber = ?");
856 $sth->execute($borrowernumber);
857 my ($flags) = $sth->fetchrow_array;
858 $sth->finish;
860 $userflags = C4::Auth::getuserflags($flags, $borrower->{userid}, $dbh);
863 unless ($userflags->{superlibrarian}
864 || (ref $userflags->{acquisition} && $userflags->{acquisition}->{order_manage_all})
865 || (!ref $userflags->{acquisition} && $userflags->{acquisition}))
867 if (not exists $userflags->{acquisition}) {
868 return 0;
871 if ( (ref $userflags->{acquisition} && !$userflags->{acquisition}->{order_manage})
872 || (!ref $userflags->{acquisition} && !$userflags->{acquisition}) ) {
873 return 0;
876 if ($AcqViewBaskets eq 'user'
877 && $basket->{authorisedby} != $borrowernumber
878 && ! grep { $borrowernumber eq $_ } GetBasketUsers($basketno)) {
879 return 0;
882 if ($AcqViewBaskets eq 'branch' && defined $basket->{branch}
883 && $basket->{branch} ne $borrower->{branchcode}) {
884 return 0;
888 return 1;
891 #------------------------------------------------------------#
893 =head3 GetBasketsByBasketgroup
895 $baskets = &GetBasketsByBasketgroup($basketgroupid);
897 Returns a reference to all baskets that belong to basketgroup $basketgroupid.
899 =cut
901 sub GetBasketsByBasketgroup {
902 my $basketgroupid = shift;
903 my $query = qq{
904 SELECT *, aqbasket.booksellerid as booksellerid
905 FROM aqbasket
906 LEFT JOIN aqcontract USING(contractnumber) WHERE basketgroupid=?
908 my $dbh = C4::Context->dbh;
909 my $sth = $dbh->prepare($query);
910 $sth->execute($basketgroupid);
911 return $sth->fetchall_arrayref({});
914 #------------------------------------------------------------#
916 =head3 NewBasketgroup
918 $basketgroupid = NewBasketgroup(\%hashref);
920 Adds a basketgroup to the aqbasketgroups table, and add the initial baskets to it.
922 $hashref->{'booksellerid'} is the 'id' field of the bookseller in the aqbooksellers table,
924 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
926 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
928 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
930 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
932 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
934 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
936 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
938 =cut
940 sub NewBasketgroup {
941 my $basketgroupinfo = shift;
942 die "booksellerid is required to create a basketgroup" unless $basketgroupinfo->{'booksellerid'};
943 my $query = "INSERT INTO aqbasketgroups (";
944 my @params;
945 foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
946 if ( defined $basketgroupinfo->{$field} ) {
947 $query .= "$field, ";
948 push(@params, $basketgroupinfo->{$field});
951 $query .= "booksellerid) VALUES (";
952 foreach (@params) {
953 $query .= "?, ";
955 $query .= "?)";
956 push(@params, $basketgroupinfo->{'booksellerid'});
957 my $dbh = C4::Context->dbh;
958 my $sth = $dbh->prepare($query);
959 $sth->execute(@params);
960 my $basketgroupid = $dbh->{'mysql_insertid'};
961 if( $basketgroupinfo->{'basketlist'} ) {
962 foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
963 my $query2 = "UPDATE aqbasket SET basketgroupid=? WHERE basketno=?";
964 my $sth2 = $dbh->prepare($query2);
965 $sth2->execute($basketgroupid, $basketno);
968 return $basketgroupid;
971 #------------------------------------------------------------#
973 =head3 ModBasketgroup
975 ModBasketgroup(\%hashref);
977 Modifies a basketgroup in the aqbasketgroups table, and add the baskets to it.
979 $hashref->{'id'} is the 'id' field of the basketgroup in the aqbasketgroup table, this parameter is mandatory,
981 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
983 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
985 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
987 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
989 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
991 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
993 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
995 =cut
997 sub ModBasketgroup {
998 my $basketgroupinfo = shift;
999 die "basketgroup id is required to edit a basketgroup" unless $basketgroupinfo->{'id'};
1000 my $dbh = C4::Context->dbh;
1001 my $query = "UPDATE aqbasketgroups SET ";
1002 my @params;
1003 foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
1004 if ( defined $basketgroupinfo->{$field} ) {
1005 $query .= "$field=?, ";
1006 push(@params, $basketgroupinfo->{$field});
1009 chop($query);
1010 chop($query);
1011 $query .= " WHERE id=?";
1012 push(@params, $basketgroupinfo->{'id'});
1013 my $sth = $dbh->prepare($query);
1014 $sth->execute(@params);
1016 $sth = $dbh->prepare('UPDATE aqbasket SET basketgroupid = NULL WHERE basketgroupid = ?');
1017 $sth->execute($basketgroupinfo->{'id'});
1019 if($basketgroupinfo->{'basketlist'} && @{$basketgroupinfo->{'basketlist'}}){
1020 $sth = $dbh->prepare("UPDATE aqbasket SET basketgroupid=? WHERE basketno=?");
1021 foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
1022 $sth->execute($basketgroupinfo->{'id'}, $basketno);
1025 return;
1028 #------------------------------------------------------------#
1030 =head3 DelBasketgroup
1032 DelBasketgroup($basketgroupid);
1034 Deletes a basketgroup in the aqbasketgroups table, and removes the reference to it from the baskets,
1036 =over
1038 =item C<$basketgroupid> is the 'id' field of the basket in the aqbasketgroup table
1040 =back
1042 =cut
1044 sub DelBasketgroup {
1045 my $basketgroupid = shift;
1046 die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
1047 my $query = "DELETE FROM aqbasketgroups WHERE id=?";
1048 my $dbh = C4::Context->dbh;
1049 my $sth = $dbh->prepare($query);
1050 $sth->execute($basketgroupid);
1051 return;
1054 #------------------------------------------------------------#
1057 =head2 FUNCTIONS ABOUT ORDERS
1059 =head3 GetBasketgroup
1061 $basketgroup = &GetBasketgroup($basketgroupid);
1063 Returns a reference to the hash containing all information about the basketgroup.
1065 =cut
1067 sub GetBasketgroup {
1068 my $basketgroupid = shift;
1069 die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
1070 my $dbh = C4::Context->dbh;
1071 my $result_set = $dbh->selectall_arrayref(
1072 'SELECT * FROM aqbasketgroups WHERE id=?',
1073 { Slice => {} },
1074 $basketgroupid
1076 return $result_set->[0]; # id is unique
1079 #------------------------------------------------------------#
1081 =head3 GetBasketgroups
1083 $basketgroups = &GetBasketgroups($booksellerid);
1085 Returns a reference to the array of all the basketgroups of bookseller $booksellerid.
1087 =cut
1089 sub GetBasketgroups {
1090 my $booksellerid = shift;
1091 die 'bookseller id is required to edit a basketgroup' unless $booksellerid;
1092 my $query = 'SELECT * FROM aqbasketgroups WHERE booksellerid=? ORDER BY id DESC';
1093 my $dbh = C4::Context->dbh;
1094 my $sth = $dbh->prepare($query);
1095 $sth->execute($booksellerid);
1096 return $sth->fetchall_arrayref({});
1099 #------------------------------------------------------------#
1101 =head2 FUNCTIONS ABOUT ORDERS
1103 =head3 GetOrders
1105 @orders = &GetOrders( $basketno, { orderby => 'biblio.title', cancelled => 0|1 } );
1107 Looks up the pending (non-cancelled) orders with the given basket
1108 number.
1110 If cancelled is set, only cancelled orders will be returned.
1112 =cut
1114 sub GetOrders {
1115 my ( $basketno, $params ) = @_;
1117 return () unless $basketno;
1119 my $orderby = $params->{orderby};
1120 my $cancelled = $params->{cancelled} || 0;
1122 my $dbh = C4::Context->dbh;
1123 my $query = q|
1124 SELECT biblio.*,biblioitems.*,
1125 aqorders.*,
1126 aqbudgets.*,
1128 $query .= $cancelled
1129 ? q|
1130 aqorders_transfers.ordernumber_to AS transferred_to,
1131 aqorders_transfers.timestamp AS transferred_to_timestamp
1133 : q|
1134 aqorders_transfers.ordernumber_from AS transferred_from,
1135 aqorders_transfers.timestamp AS transferred_from_timestamp
1137 $query .= q|
1138 FROM aqorders
1139 LEFT JOIN aqbudgets ON aqbudgets.budget_id = aqorders.budget_id
1140 LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1141 LEFT JOIN biblioitems ON biblioitems.biblionumber =biblio.biblionumber
1143 $query .= $cancelled
1144 ? q|
1145 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_from = aqorders.ordernumber
1147 : q|
1148 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1151 $query .= q|
1152 WHERE basketno=?
1155 if ($cancelled) {
1156 $orderby ||= q|biblioitems.publishercode, biblio.title|;
1157 $query .= q|
1158 AND (datecancellationprinted IS NOT NULL
1159 AND datecancellationprinted <> '0000-00-00')
1162 else {
1163 $orderby ||=
1164 q|aqorders.datecancellationprinted desc, aqorders.timestamp desc|;
1165 $query .= q|
1166 AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')
1170 $query .= " ORDER BY $orderby";
1171 my $orders =
1172 $dbh->selectall_arrayref( $query, { Slice => {} }, $basketno );
1173 return @{$orders};
1177 #------------------------------------------------------------#
1179 =head3 GetOrdersByBiblionumber
1181 @orders = &GetOrdersByBiblionumber($biblionumber);
1183 Looks up the orders with linked to a specific $biblionumber, including
1184 cancelled orders and received orders.
1186 return :
1187 C<@orders> is an array of references-to-hash, whose keys are the
1188 fields from the aqorders, biblio, and biblioitems tables in the Koha database.
1190 =cut
1192 sub GetOrdersByBiblionumber {
1193 my $biblionumber = shift;
1194 return unless $biblionumber;
1195 my $dbh = C4::Context->dbh;
1196 my $query ="
1197 SELECT biblio.*,biblioitems.*,
1198 aqorders.*,
1199 aqbudgets.*
1200 FROM aqorders
1201 LEFT JOIN aqbudgets ON aqbudgets.budget_id = aqorders.budget_id
1202 LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1203 LEFT JOIN biblioitems ON biblioitems.biblionumber =biblio.biblionumber
1204 WHERE aqorders.biblionumber=?
1206 my $result_set =
1207 $dbh->selectall_arrayref( $query, { Slice => {} }, $biblionumber );
1208 return @{$result_set};
1212 #------------------------------------------------------------#
1214 =head3 GetOrder
1216 $order = &GetOrder($ordernumber);
1218 Looks up an order by order number.
1220 Returns a reference-to-hash describing the order. The keys of
1221 C<$order> are fields from the biblio, biblioitems, aqorders tables of the Koha database.
1223 =cut
1225 sub GetOrder {
1226 my ($ordernumber) = @_;
1227 return unless $ordernumber;
1229 my $dbh = C4::Context->dbh;
1230 my $query = qq{SELECT
1231 aqorders.*,
1232 biblio.title,
1233 biblio.author,
1234 aqbasket.basketname,
1235 borrowers.branchcode,
1236 biblioitems.publicationyear,
1237 biblio.copyrightdate,
1238 biblioitems.editionstatement,
1239 biblioitems.isbn,
1240 biblioitems.ean,
1241 biblio.seriestitle,
1242 biblioitems.publishercode,
1243 aqorders.rrp AS unitpricesupplier,
1244 aqorders.ecost AS unitpricelib,
1245 aqorders.claims_count AS claims_count,
1246 aqorders.claimed_date AS claimed_date,
1247 aqbudgets.budget_name AS budget,
1248 aqbooksellers.name AS supplier,
1249 aqbooksellers.id AS supplierid,
1250 biblioitems.publishercode AS publisher,
1251 ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
1252 DATE(aqbasket.closedate) AS orderdate,
1253 aqorders.quantity - COALESCE(aqorders.quantityreceived,0) AS quantity_to_receive,
1254 (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
1255 DATEDIFF(CURDATE( ),closedate) AS latesince
1256 FROM aqorders LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1257 LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
1258 LEFT JOIN aqbudgets ON aqorders.budget_id = aqbudgets.budget_id,
1259 aqbasket LEFT JOIN borrowers ON aqbasket.authorisedby = borrowers.borrowernumber
1260 LEFT JOIN aqbooksellers ON aqbasket.booksellerid = aqbooksellers.id
1261 WHERE aqorders.basketno = aqbasket.basketno
1262 AND ordernumber=?};
1263 my $result_set =
1264 $dbh->selectall_arrayref( $query, { Slice => {} }, $ordernumber );
1266 # result_set assumed to contain 1 match
1267 return $result_set->[0];
1270 =head3 GetLastOrderNotReceivedFromSubscriptionid
1272 $order = &GetLastOrderNotReceivedFromSubscriptionid($subscriptionid);
1274 Returns a reference-to-hash describing the last order not received for a subscription.
1276 =cut
1278 sub GetLastOrderNotReceivedFromSubscriptionid {
1279 my ( $subscriptionid ) = @_;
1280 my $dbh = C4::Context->dbh;
1281 my $query = qq|
1282 SELECT * FROM aqorders
1283 LEFT JOIN subscription
1284 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1285 WHERE aqorders.subscriptionid = ?
1286 AND aqorders.datereceived IS NULL
1287 LIMIT 1
1289 my $result_set =
1290 $dbh->selectall_arrayref( $query, { Slice => {} }, $subscriptionid );
1292 # result_set assumed to contain 1 match
1293 return $result_set->[0];
1296 =head3 GetLastOrderReceivedFromSubscriptionid
1298 $order = &GetLastOrderReceivedFromSubscriptionid($subscriptionid);
1300 Returns a reference-to-hash describing the last order received for a subscription.
1302 =cut
1304 sub GetLastOrderReceivedFromSubscriptionid {
1305 my ( $subscriptionid ) = @_;
1306 my $dbh = C4::Context->dbh;
1307 my $query = qq|
1308 SELECT * FROM aqorders
1309 LEFT JOIN subscription
1310 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1311 WHERE aqorders.subscriptionid = ?
1312 AND aqorders.datereceived =
1314 SELECT MAX( aqorders.datereceived )
1315 FROM aqorders
1316 LEFT JOIN subscription
1317 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1318 WHERE aqorders.subscriptionid = ?
1319 AND aqorders.datereceived IS NOT NULL
1321 ORDER BY ordernumber DESC
1322 LIMIT 1
1324 my $result_set =
1325 $dbh->selectall_arrayref( $query, { Slice => {} }, $subscriptionid, $subscriptionid );
1327 # result_set assumed to contain 1 match
1328 return $result_set->[0];
1332 #------------------------------------------------------------#
1334 =head3 ModOrder
1336 &ModOrder(\%hashref);
1338 Modifies an existing order. Updates the order with order number
1339 $hashref->{'ordernumber'} and biblionumber $hashref->{'biblionumber'}. All
1340 other keys of the hash update the fields with the same name in the aqorders
1341 table of the Koha database.
1343 =cut
1345 sub ModOrder {
1346 my $orderinfo = shift;
1348 die "Ordernumber is required" if $orderinfo->{'ordernumber'} eq '';
1350 my $dbh = C4::Context->dbh;
1351 my @params;
1353 # update uncertainprice to an integer, just in case (under FF, checked boxes have the value "ON" by default)
1354 $orderinfo->{uncertainprice}=1 if $orderinfo->{uncertainprice};
1356 # delete($orderinfo->{'branchcode'});
1357 # the hash contains a lot of entries not in aqorders, so get the columns ...
1358 my $sth = $dbh->prepare("SELECT * FROM aqorders LIMIT 1;");
1359 $sth->execute;
1360 my $colnames = $sth->{NAME};
1361 #FIXME Be careful. If aqorders would have columns with diacritics,
1362 #you should need to decode what you get back from NAME.
1363 #See report 10110 and guided_reports.pl
1364 my $query = "UPDATE aqorders SET ";
1366 foreach my $orderinfokey (grep(!/ordernumber/, keys %$orderinfo)){
1367 # ... and skip hash entries that are not in the aqorders table
1368 # FIXME : probably not the best way to do it (would be better to have a correct hash)
1369 next unless grep(/^$orderinfokey$/, @$colnames);
1370 $query .= "$orderinfokey=?, ";
1371 push(@params, $orderinfo->{$orderinfokey});
1374 $query .= "timestamp=NOW() WHERE ordernumber=?";
1375 push(@params, $orderinfo->{'ordernumber'} );
1376 $sth = $dbh->prepare($query);
1377 $sth->execute(@params);
1378 return;
1381 #------------------------------------------------------------#
1383 =head3 ModItemOrder
1385 ModItemOrder($itemnumber, $ordernumber);
1387 Modifies the ordernumber of an item in aqorders_items.
1389 =cut
1391 sub ModItemOrder {
1392 my ($itemnumber, $ordernumber) = @_;
1394 return unless ($itemnumber and $ordernumber);
1396 my $dbh = C4::Context->dbh;
1397 my $query = qq{
1398 UPDATE aqorders_items
1399 SET ordernumber = ?
1400 WHERE itemnumber = ?
1402 my $sth = $dbh->prepare($query);
1403 return $sth->execute($ordernumber, $itemnumber);
1406 #------------------------------------------------------------#
1408 =head3 ModReceiveOrder
1410 my ( $date_received, $new_ordernumber ) = ModReceiveOrder(
1412 biblionumber => $biblionumber,
1413 order => $order,
1414 quantityreceived => $quantityreceived,
1415 user => $user,
1416 invoice => $invoice,
1417 budget_id => $budget_id,
1418 received_itemnumbers => \@received_itemnumbers,
1419 order_internalnote => $order_internalnote,
1423 Updates an order, to reflect the fact that it was received, at least
1424 in part.
1426 If a partial order is received, splits the order into two.
1428 Updates the order with biblionumber C<$biblionumber> and ordernumber
1429 C<$order->{ordernumber}>.
1431 =cut
1434 sub ModReceiveOrder {
1435 my ($params) = @_;
1436 my $biblionumber = $params->{biblionumber};
1437 my $order = { %{ $params->{order} } }; # Copy the order, we don't want to modify it
1438 my $invoice = $params->{invoice};
1439 my $quantrec = $params->{quantityreceived};
1440 my $user = $params->{user};
1441 my $budget_id = $params->{budget_id};
1442 my $received_items = $params->{received_items};
1444 my $dbh = C4::Context->dbh;
1445 my $datereceived = ( $invoice and $invoice->{datereceived} ) ? $invoice->{datereceived} : dt_from_string;
1446 my $suggestionid = GetSuggestionFromBiblionumber( $biblionumber );
1447 if ($suggestionid) {
1448 ModSuggestion( {suggestionid=>$suggestionid,
1449 STATUS=>'AVAILABLE',
1450 biblionumber=> $biblionumber}
1454 my $result_set = $dbh->selectrow_arrayref(
1455 q{SELECT aqbasket.is_standing
1456 FROM aqbasket
1457 WHERE basketno=?},{ Slice => {} }, $order->{basketno});
1458 my $is_standing = $result_set->[0]; # we assume we have a unique basket
1460 my $new_ordernumber = $order->{ordernumber};
1461 if ( $is_standing || $order->{quantity} > $quantrec ) {
1462 # Split order line in two parts: the first is the original order line
1463 # without received items (the quantity is decreased),
1464 # the second part is a new order line with quantity=quantityrec
1465 # (entirely received)
1466 my $query = q|
1467 UPDATE aqorders
1468 SET quantity = ?,
1469 orderstatus = 'partial'|;
1470 $query .= q|, order_internalnote = ?| if defined $order->{order_internalnote};
1471 $query .= q| WHERE ordernumber = ?|;
1472 my $sth = $dbh->prepare($query);
1474 $sth->execute(
1475 ( $is_standing ? 1 : ($order->{quantity} - $quantrec) ),
1476 ( defined $order->{order_internalnote} ? $order->{order_internalnote} : () ),
1477 $order->{ordernumber}
1480 # Recalculate tax_value
1481 $dbh->do(q|
1482 UPDATE aqorders
1484 tax_value_on_ordering = quantity * ecost_tax_excluded * tax_rate_on_ordering,
1485 tax_value_on_receiving = quantity * unitprice_tax_excluded * tax_rate_on_receiving
1486 WHERE ordernumber = ?
1487 |, undef, $order->{ordernumber});
1489 delete $order->{ordernumber};
1490 $order->{budget_id} = ( $budget_id || $order->{budget_id} );
1491 $order->{quantity} = $quantrec;
1492 $order->{quantityreceived} = $quantrec;
1493 $order->{ecost_tax_excluded} //= 0;
1494 $order->{tax_rate_on_ordering} //= 0;
1495 $order->{unitprice_tax_excluded} //= 0;
1496 $order->{tax_rate_on_receiving} //= 0;
1497 $order->{tax_value_on_ordering} = $order->{quantity} * $order->{ecost_tax_excluded} * $order->{tax_rate_on_ordering};
1498 $order->{tax_value_on_receiving} = $order->{quantity} * $order->{unitprice_tax_excluded} * $order->{tax_rate_on_receiving};
1499 $order->{datereceived} = $datereceived;
1500 $order->{invoiceid} = $invoice->{invoiceid};
1501 $order->{orderstatus} = 'complete';
1502 $new_ordernumber = Koha::Acquisition::Order->new($order)->store->ordernumber; # TODO What if the store fails?
1504 if ($received_items) {
1505 foreach my $itemnumber (@$received_items) {
1506 ModItemOrder($itemnumber, $new_ordernumber);
1509 } else {
1510 my $query = q|
1511 UPDATE aqorders
1512 SET quantityreceived = ?,
1513 datereceived = ?,
1514 invoiceid = ?,
1515 budget_id = ?,
1516 orderstatus = 'complete'
1519 $query .= q|
1520 , replacementprice = ?
1521 | if defined $order->{replacementprice};
1523 $query .= q|
1524 , unitprice = ?, unitprice_tax_included = ?, unitprice_tax_excluded = ?
1525 | if defined $order->{unitprice};
1527 $query .= q|
1528 ,tax_value_on_receiving = ?
1529 | if defined $order->{tax_value_on_receiving};
1531 $query .= q|
1532 ,tax_rate_on_receiving = ?
1533 | if defined $order->{tax_rate_on_receiving};
1535 $query .= q|
1536 , order_internalnote = ?
1537 | if defined $order->{order_internalnote};
1539 $query .= q| where biblionumber=? and ordernumber=?|;
1541 my $sth = $dbh->prepare( $query );
1542 my @params = ( $quantrec, $datereceived, $invoice->{invoiceid}, ( $budget_id ? $budget_id : $order->{budget_id} ) );
1544 if ( defined $order->{replacementprice} ) {
1545 push @params, $order->{replacementprice};
1548 if ( defined $order->{unitprice} ) {
1549 push @params, $order->{unitprice}, $order->{unitprice_tax_included}, $order->{unitprice_tax_excluded};
1552 if ( defined $order->{tax_value_on_receiving} ) {
1553 push @params, $order->{tax_value_on_receiving};
1556 if ( defined $order->{tax_rate_on_receiving} ) {
1557 push @params, $order->{tax_rate_on_receiving};
1560 if ( defined $order->{order_internalnote} ) {
1561 push @params, $order->{order_internalnote};
1564 push @params, ( $biblionumber, $order->{ordernumber} );
1566 $sth->execute( @params );
1568 # All items have been received, sent a notification to users
1569 NotifyOrderUsers( $order->{ordernumber} );
1572 return ($datereceived, $new_ordernumber);
1575 =head3 CancelReceipt
1577 my $parent_ordernumber = CancelReceipt($ordernumber);
1579 Cancel an order line receipt and update the parent order line, as if no
1580 receipt was made.
1581 If items are created at receipt (AcqCreateItem = receiving) then delete
1582 these items.
1584 =cut
1586 sub CancelReceipt {
1587 my $ordernumber = shift;
1589 return unless $ordernumber;
1591 my $dbh = C4::Context->dbh;
1592 my $query = qq{
1593 SELECT datereceived, parent_ordernumber, quantity
1594 FROM aqorders
1595 WHERE ordernumber = ?
1597 my $sth = $dbh->prepare($query);
1598 $sth->execute($ordernumber);
1599 my $order = $sth->fetchrow_hashref;
1600 unless($order) {
1601 warn "CancelReceipt: order $ordernumber does not exist";
1602 return;
1604 unless($order->{'datereceived'}) {
1605 warn "CancelReceipt: order $ordernumber is not received";
1606 return;
1609 my $parent_ordernumber = $order->{'parent_ordernumber'};
1611 my @itemnumbers = GetItemnumbersFromOrder( $ordernumber );
1612 my $order_obj = Koha::Acquisition::Orders->find( $ordernumber ); # FIXME rewrite all this subroutine using this object
1614 if($parent_ordernumber == $ordernumber || not $parent_ordernumber) {
1615 # The order line has no parent, just mark it as not received
1616 $query = qq{
1617 UPDATE aqorders
1618 SET quantityreceived = ?,
1619 datereceived = ?,
1620 invoiceid = ?,
1621 orderstatus = 'ordered'
1622 WHERE ordernumber = ?
1624 $sth = $dbh->prepare($query);
1625 $sth->execute(0, undef, undef, $ordernumber);
1626 _cancel_items_receipt( $order_obj );
1627 } else {
1628 # The order line has a parent, increase parent quantity and delete
1629 # the order line.
1630 $query = qq{
1631 SELECT quantity, datereceived
1632 FROM aqorders
1633 WHERE ordernumber = ?
1635 $sth = $dbh->prepare($query);
1636 $sth->execute($parent_ordernumber);
1637 my $parent_order = $sth->fetchrow_hashref;
1638 unless($parent_order) {
1639 warn "Parent order $parent_ordernumber does not exist.";
1640 return;
1642 if($parent_order->{'datereceived'}) {
1643 warn "CancelReceipt: parent order is received.".
1644 " Can't cancel receipt.";
1645 return;
1647 $query = qq{
1648 UPDATE aqorders
1649 SET quantity = ?,
1650 orderstatus = 'ordered'
1651 WHERE ordernumber = ?
1653 $sth = $dbh->prepare($query);
1654 my $rv = $sth->execute(
1655 $order->{'quantity'} + $parent_order->{'quantity'},
1656 $parent_ordernumber
1658 unless($rv) {
1659 warn "Cannot update parent order line, so do not cancel".
1660 " receipt";
1661 return;
1664 # Recalculate tax_value
1665 $dbh->do(q|
1666 UPDATE aqorders
1668 tax_value_on_ordering = quantity * ecost_tax_excluded * tax_rate_on_ordering,
1669 tax_value_on_receiving = quantity * unitprice_tax_excluded * tax_rate_on_receiving
1670 WHERE ordernumber = ?
1671 |, undef, $parent_ordernumber);
1673 _cancel_items_receipt( $order_obj, $parent_ordernumber );
1674 # Delete order line
1675 $query = qq{
1676 DELETE FROM aqorders
1677 WHERE ordernumber = ?
1679 $sth = $dbh->prepare($query);
1680 $sth->execute($ordernumber);
1684 if( $order_obj->basket->effective_create_items eq 'ordering' ) {
1685 my @affects = split q{\|}, C4::Context->preference("AcqItemSetSubfieldsWhenReceiptIsCancelled");
1686 if ( @affects ) {
1687 for my $in ( @itemnumbers ) {
1688 my $item = Koha::Items->find( $in );
1689 my $biblio = $item->biblio;
1690 my ( $itemfield ) = GetMarcFromKohaField( 'items.itemnumber', $biblio->frameworkcode );
1691 my $item_marc = C4::Items::GetMarcItem( $biblio->biblionumber, $in );
1692 for my $affect ( @affects ) {
1693 my ( $sf, $v ) = split q{=}, $affect, 2;
1694 foreach ( $item_marc->field($itemfield) ) {
1695 $_->update( $sf => $v );
1698 C4::Items::ModItemFromMarc( $item_marc, $biblio->biblionumber, $in );
1703 return $parent_ordernumber;
1706 sub _cancel_items_receipt {
1707 my ( $order, $parent_ordernumber ) = @_;
1708 $parent_ordernumber ||= $order->ordernumber;
1710 my @itemnumbers = GetItemnumbersFromOrder($order->ordernumber); # FIXME Must be $order->items
1711 if ( $order->basket->effective_create_items eq 'receiving' ) {
1712 # Remove items that were created at receipt
1713 my $query = qq{
1714 DELETE FROM items, aqorders_items
1715 USING items, aqorders_items
1716 WHERE items.itemnumber = ? AND aqorders_items.itemnumber = ?
1718 my $dbh = C4::Context->dbh;
1719 my $sth = $dbh->prepare($query);
1720 foreach my $itemnumber (@itemnumbers) {
1721 $sth->execute($itemnumber, $itemnumber);
1723 } else {
1724 # Update items
1725 foreach my $itemnumber (@itemnumbers) {
1726 ModItemOrder($itemnumber, $parent_ordernumber);
1731 #------------------------------------------------------------#
1733 =head3 SearchOrders
1735 @results = &SearchOrders({
1736 ordernumber => $ordernumber,
1737 search => $search,
1738 ean => $ean,
1739 booksellerid => $booksellerid,
1740 basketno => $basketno,
1741 basketname => $basketname,
1742 basketgroupname => $basketgroupname,
1743 owner => $owner,
1744 pending => $pending
1745 ordered => $ordered
1746 biblionumber => $biblionumber,
1747 budget_id => $budget_id
1750 Searches for orders filtered by criteria.
1752 C<$ordernumber> Finds matching orders or transferred orders by ordernumber.
1753 C<$search> Finds orders matching %$search% in title, author, or isbn.
1754 C<$owner> Finds order for the logged in user.
1755 C<$pending> Finds pending orders. Ignores completed and cancelled orders.
1756 C<$ordered> Finds orders to receive only (status 'ordered' or 'partial').
1759 C<@results> is an array of references-to-hash with the keys are fields
1760 from aqorders, biblio, biblioitems and aqbasket tables.
1762 =cut
1764 sub SearchOrders {
1765 my ( $params ) = @_;
1766 my $ordernumber = $params->{ordernumber};
1767 my $search = $params->{search};
1768 my $ean = $params->{ean};
1769 my $booksellerid = $params->{booksellerid};
1770 my $basketno = $params->{basketno};
1771 my $basketname = $params->{basketname};
1772 my $basketgroupname = $params->{basketgroupname};
1773 my $owner = $params->{owner};
1774 my $pending = $params->{pending};
1775 my $ordered = $params->{ordered};
1776 my $biblionumber = $params->{biblionumber};
1777 my $budget_id = $params->{budget_id};
1779 my $dbh = C4::Context->dbh;
1780 my @args = ();
1781 my $query = q{
1782 SELECT aqbasket.basketno,
1783 borrowers.surname,
1784 borrowers.firstname,
1785 biblio.*,
1786 biblioitems.isbn,
1787 biblioitems.biblioitemnumber,
1788 biblioitems.publishercode,
1789 biblioitems.publicationyear,
1790 aqbasket.authorisedby,
1791 aqbasket.booksellerid,
1792 aqbasket.closedate,
1793 aqbasket.creationdate,
1794 aqbasket.basketname,
1795 aqbasketgroups.id as basketgroupid,
1796 aqbasketgroups.name as basketgroupname,
1797 aqorders.*
1798 FROM aqorders
1799 LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
1800 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
1801 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
1802 LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
1803 LEFT JOIN biblioitems ON biblioitems.biblionumber=biblio.biblionumber
1806 # If we search on ordernumber, we retrieve the transferred order if a transfer has been done.
1807 $query .= q{
1808 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1809 } if $ordernumber;
1811 $query .= q{
1812 WHERE (datecancellationprinted is NULL)
1815 if ( $pending or $ordered ) {
1816 $query .= q{
1817 AND (
1818 ( aqbasket.is_standing AND aqorders.orderstatus IN ( "new", "ordered", "partial" ) )
1819 OR (
1820 ( quantity > quantityreceived OR quantityreceived is NULL )
1823 if ( $ordered ) {
1824 $query .= q{ AND aqorders.orderstatus IN ( "ordered", "partial" )};
1826 $query .= q{
1832 my $userenv = C4::Context->userenv;
1833 if ( C4::Context->preference("IndependentBranches") ) {
1834 unless ( C4::Context->IsSuperLibrarian() ) {
1835 $query .= q{
1836 AND (
1837 borrowers.branchcode = ?
1838 OR borrowers.branchcode = ''
1841 push @args, $userenv->{branch};
1845 if ( $ordernumber ) {
1846 $query .= ' AND ( aqorders.ordernumber = ? OR aqorders_transfers.ordernumber_from = ? ) ';
1847 push @args, ( $ordernumber, $ordernumber );
1849 if ( $biblionumber ) {
1850 $query .= 'AND aqorders.biblionumber = ?';
1851 push @args, $biblionumber;
1853 if( $search ) {
1854 $query .= ' AND (biblio.title LIKE ? OR biblio.author LIKE ? OR biblioitems.isbn LIKE ?)';
1855 push @args, ("%$search%","%$search%","%$search%");
1857 if ( $ean ) {
1858 $query .= ' AND biblioitems.ean = ?';
1859 push @args, $ean;
1861 if ( $booksellerid ) {
1862 $query .= 'AND aqbasket.booksellerid = ?';
1863 push @args, $booksellerid;
1865 if( $basketno ) {
1866 $query .= 'AND aqbasket.basketno = ?';
1867 push @args, $basketno;
1869 if( $basketname ) {
1870 $query .= 'AND aqbasket.basketname LIKE ?';
1871 push @args, "%$basketname%";
1873 if( $basketgroupname ) {
1874 $query .= ' AND aqbasketgroups.name LIKE ?';
1875 push @args, "%$basketgroupname%";
1878 if ( $owner ) {
1879 $query .= ' AND aqbasket.authorisedby=? ';
1880 push @args, $userenv->{'number'};
1883 if ( $budget_id ) {
1884 $query .= ' AND aqorders.budget_id = ?';
1885 push @args, $budget_id;
1888 $query .= ' ORDER BY aqbasket.basketno';
1890 my $sth = $dbh->prepare($query);
1891 $sth->execute(@args);
1892 return $sth->fetchall_arrayref({});
1895 #------------------------------------------------------------#
1897 =head3 DelOrder
1899 &DelOrder($biblionumber, $ordernumber);
1901 Cancel the order with the given order and biblio numbers. It does not
1902 delete any entries in the aqorders table, it merely marks them as
1903 cancelled.
1905 =cut
1907 sub DelOrder {
1908 my ( $bibnum, $ordernumber, $delete_biblio, $reason ) = @_;
1910 my $error;
1911 my $dbh = C4::Context->dbh;
1912 my $query = "
1913 UPDATE aqorders
1914 SET datecancellationprinted=now(), orderstatus='cancelled'
1916 if($reason) {
1917 $query .= ", cancellationreason = ? ";
1919 $query .= "
1920 WHERE biblionumber=? AND ordernumber=?
1922 my $sth = $dbh->prepare($query);
1923 if($reason) {
1924 $sth->execute($reason, $bibnum, $ordernumber);
1925 } else {
1926 $sth->execute( $bibnum, $ordernumber );
1928 $sth->finish;
1930 my @itemnumbers = GetItemnumbersFromOrder( $ordernumber );
1931 foreach my $itemnumber (@itemnumbers){
1932 my $delcheck = C4::Items::DelItemCheck( $bibnum, $itemnumber );
1934 if($delcheck != 1) {
1935 $error->{'delitem'} = 1;
1939 if($delete_biblio) {
1940 # We get the number of remaining items
1941 my $biblio = Koha::Biblios->find( $bibnum );
1942 my $itemcount = $biblio->items->count;
1944 # If there are no items left,
1945 if ( $itemcount == 0 ) {
1946 # We delete the record
1947 my $delcheck = DelBiblio($bibnum);
1949 if($delcheck) {
1950 $error->{'delbiblio'} = 1;
1955 return $error;
1958 =head3 TransferOrder
1960 my $newordernumber = TransferOrder($ordernumber, $basketno);
1962 Transfer an order line to a basket.
1963 Mark $ordernumber as cancelled with an internal note 'Cancelled and transferred
1964 to BOOKSELLER on DATE' and create new order with internal note
1965 'Transferred from BOOKSELLER on DATE'.
1966 Move all attached items to the new order.
1967 Received orders cannot be transferred.
1968 Return the ordernumber of created order.
1970 =cut
1972 sub TransferOrder {
1973 my ($ordernumber, $basketno) = @_;
1975 return unless ($ordernumber and $basketno);
1977 my $order = Koha::Acquisition::Orders->find( $ordernumber ) or return;
1978 return if $order->datereceived;
1980 $order = $order->unblessed;
1982 my $basket = GetBasket($basketno);
1983 return unless $basket;
1985 my $dbh = C4::Context->dbh;
1986 my ($query, $sth, $rv);
1988 $query = q{
1989 UPDATE aqorders
1990 SET datecancellationprinted = CAST(NOW() AS date), orderstatus = ?
1991 WHERE ordernumber = ?
1993 $sth = $dbh->prepare($query);
1994 $rv = $sth->execute('cancelled', $ordernumber);
1996 delete $order->{'ordernumber'};
1997 delete $order->{parent_ordernumber};
1998 $order->{'basketno'} = $basketno;
2000 my $newordernumber = Koha::Acquisition::Order->new($order)->store->ordernumber;
2002 $query = q{
2003 UPDATE aqorders_items
2004 SET ordernumber = ?
2005 WHERE ordernumber = ?
2007 $sth = $dbh->prepare($query);
2008 $sth->execute($newordernumber, $ordernumber);
2010 $query = q{
2011 INSERT INTO aqorders_transfers (ordernumber_from, ordernumber_to)
2012 VALUES (?, ?)
2014 $sth = $dbh->prepare($query);
2015 $sth->execute($ordernumber, $newordernumber);
2017 return $newordernumber;
2020 =head2 FUNCTIONS ABOUT PARCELS
2022 =head3 GetParcels
2024 $results = &GetParcels($bookseller, $order, $code, $datefrom, $dateto);
2026 get a lists of parcels.
2028 * Input arg :
2030 =over
2032 =item $bookseller
2033 is the bookseller this function has to get parcels.
2035 =item $order
2036 To know on what criteria the results list has to be ordered.
2038 =item $code
2039 is the booksellerinvoicenumber.
2041 =item $datefrom & $dateto
2042 to know on what date this function has to filter its search.
2044 =back
2046 * return:
2047 a pointer on a hash list containing parcel informations as such :
2049 =over
2051 =item Creation date
2053 =item Last operation
2055 =item Number of biblio
2057 =item Number of items
2059 =back
2061 =cut
2063 sub GetParcels {
2064 my ($bookseller,$order, $code, $datefrom, $dateto) = @_;
2065 my $dbh = C4::Context->dbh;
2066 my @query_params = ();
2067 my $strsth ="
2068 SELECT aqinvoices.invoicenumber,
2069 datereceived,purchaseordernumber,
2070 count(DISTINCT biblionumber) AS biblio,
2071 sum(quantity) AS itemsexpected,
2072 sum(quantityreceived) AS itemsreceived
2073 FROM aqorders LEFT JOIN aqbasket ON aqbasket.basketno = aqorders.basketno
2074 LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
2075 WHERE aqbasket.booksellerid = ? and datereceived IS NOT NULL
2077 push @query_params, $bookseller;
2079 if ( defined $code ) {
2080 $strsth .= ' and aqinvoices.invoicenumber like ? ';
2081 # add a % to the end of the code to allow stemming.
2082 push @query_params, "$code%";
2085 if ( defined $datefrom ) {
2086 $strsth .= ' and datereceived >= ? ';
2087 push @query_params, $datefrom;
2090 if ( defined $dateto ) {
2091 $strsth .= 'and datereceived <= ? ';
2092 push @query_params, $dateto;
2095 $strsth .= "group by aqinvoices.invoicenumber,datereceived ";
2097 # can't use a placeholder to place this column name.
2098 # but, we could probably be checking to make sure it is a column that will be fetched.
2099 $strsth .= "order by $order " if ($order);
2101 my $sth = $dbh->prepare($strsth);
2103 $sth->execute( @query_params );
2104 my $results = $sth->fetchall_arrayref({});
2105 return @{$results};
2108 #------------------------------------------------------------#
2110 =head3 GetLateOrders
2112 @results = &GetLateOrders;
2114 Searches for bookseller with late orders.
2116 return:
2117 the table of supplier with late issues. This table is full of hashref.
2119 =cut
2121 sub GetLateOrders {
2122 my $delay = shift;
2123 my $supplierid = shift;
2124 my $branch = shift;
2125 my $estimateddeliverydatefrom = shift;
2126 my $estimateddeliverydateto = shift;
2128 my $dbh = C4::Context->dbh;
2130 #BEWARE, order of parenthesis and LEFT JOIN is important for speed
2131 my $dbdriver = C4::Context->config("db_scheme") || "mysql";
2133 my @query_params = ();
2134 my $select = "
2135 SELECT aqbasket.basketno,
2136 aqorders.ordernumber,
2137 DATE(aqbasket.closedate) AS orderdate,
2138 aqbasket.basketname AS basketname,
2139 aqbasket.basketgroupid AS basketgroupid,
2140 aqbasketgroups.name AS basketgroupname,
2141 aqorders.rrp AS unitpricesupplier,
2142 aqorders.ecost AS unitpricelib,
2143 aqorders.claims_count AS claims_count,
2144 aqorders.claimed_date AS claimed_date,
2145 aqbudgets.budget_name AS budget,
2146 borrowers.branchcode AS branch,
2147 aqbooksellers.name AS supplier,
2148 aqbooksellers.id AS supplierid,
2149 biblio.author, biblio.title,
2150 biblioitems.publishercode AS publisher,
2151 biblioitems.publicationyear,
2152 ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
2154 my $from = "
2155 FROM
2156 aqorders LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
2157 LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
2158 LEFT JOIN aqbudgets ON aqorders.budget_id = aqbudgets.budget_id,
2159 aqbasket LEFT JOIN borrowers ON aqbasket.authorisedby = borrowers.borrowernumber
2160 LEFT JOIN aqbooksellers ON aqbasket.booksellerid = aqbooksellers.id
2161 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
2162 WHERE aqorders.basketno = aqbasket.basketno
2163 AND ( datereceived = ''
2164 OR datereceived IS NULL
2165 OR aqorders.quantityreceived < aqorders.quantity
2167 AND aqbasket.closedate IS NOT NULL
2168 AND (aqorders.datecancellationprinted IS NULL OR aqorders.datecancellationprinted='0000-00-00')
2170 if ($dbdriver eq "mysql") {
2171 $select .= "
2172 aqorders.quantity - COALESCE(aqorders.quantityreceived,0) AS quantity,
2173 (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
2174 DATEDIFF(CAST(now() AS date),closedate) AS latesince
2176 if ( defined $delay ) {
2177 $from .= " AND (closedate <= DATE_SUB(CAST(now() AS date),INTERVAL ? DAY)) " ;
2178 push @query_params, $delay;
2180 $from .= " AND aqorders.quantity - COALESCE(aqorders.quantityreceived,0) <> 0";
2181 } else {
2182 # FIXME: account for IFNULL as above
2183 $select .= "
2184 aqorders.quantity AS quantity,
2185 aqorders.quantity * aqorders.rrp AS subtotal,
2186 (CAST(now() AS date) - closedate) AS latesince
2188 if ( defined $delay ) {
2189 $from .= " AND (closedate <= (CAST(now() AS date) -(INTERVAL ? DAY)) ";
2190 push @query_params, $delay;
2192 $from .= " AND aqorders.quantity <> 0";
2194 if (defined $supplierid) {
2195 $from .= ' AND aqbasket.booksellerid = ? ';
2196 push @query_params, $supplierid;
2198 if (defined $branch) {
2199 $from .= ' AND borrowers.branchcode LIKE ? ';
2200 push @query_params, $branch;
2203 if ( defined $estimateddeliverydatefrom or defined $estimateddeliverydateto ) {
2204 $from .= ' AND aqbooksellers.deliverytime IS NOT NULL ';
2206 if ( defined $estimateddeliverydatefrom ) {
2207 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) >= ?';
2208 push @query_params, $estimateddeliverydatefrom;
2210 if ( defined $estimateddeliverydateto ) {
2211 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) <= ?';
2212 push @query_params, $estimateddeliverydateto;
2214 if ( defined $estimateddeliverydatefrom and not defined $estimateddeliverydateto ) {
2215 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) <= CAST(now() AS date)';
2217 if (C4::Context->preference("IndependentBranches")
2218 && !C4::Context->IsSuperLibrarian() ) {
2219 $from .= ' AND borrowers.branchcode LIKE ? ';
2220 push @query_params, C4::Context->userenv->{branch};
2222 $from .= " AND orderstatus <> 'cancelled' ";
2223 my $query = "$select $from \nORDER BY latesince, basketno, borrowers.branchcode, supplier";
2224 $debug and print STDERR "GetLateOrders query: $query\nGetLateOrders args: " . join(" ",@query_params);
2225 my $sth = $dbh->prepare($query);
2226 $sth->execute(@query_params);
2227 my @results;
2228 while (my $data = $sth->fetchrow_hashref) {
2229 push @results, $data;
2231 return @results;
2234 #------------------------------------------------------------#
2236 =head3 GetHistory
2238 \@order_loop = GetHistory( %params );
2240 Retreives some acquisition history information
2242 params:
2243 title
2244 author
2245 name
2246 isbn
2248 from_placed_on
2249 to_placed_on
2250 basket - search both basket name and number
2251 booksellerinvoicenumber
2252 basketgroupname
2253 budget
2254 orderstatus (note that orderstatus '' will retrieve orders
2255 of any status except cancelled)
2256 biblionumber
2257 get_canceled_order (if set to a true value, cancelled orders will
2258 be included)
2260 returns:
2261 $order_loop is a list of hashrefs that each look like this:
2263 'author' => 'Twain, Mark',
2264 'basketno' => '1',
2265 'biblionumber' => '215',
2266 'count' => 1,
2267 'creationdate' => 'MM/DD/YYYY',
2268 'datereceived' => undef,
2269 'ecost' => '1.00',
2270 'id' => '1',
2271 'invoicenumber' => undef,
2272 'name' => '',
2273 'ordernumber' => '1',
2274 'quantity' => 1,
2275 'quantityreceived' => undef,
2276 'title' => 'The Adventures of Huckleberry Finn'
2279 =cut
2281 sub GetHistory {
2282 # don't run the query if there are no parameters (list would be too long for sure !)
2283 croak "No search params" unless @_;
2284 my %params = @_;
2285 my $title = $params{title};
2286 my $author = $params{author};
2287 my $isbn = $params{isbn};
2288 my $ean = $params{ean};
2289 my $name = $params{name};
2290 my $from_placed_on = $params{from_placed_on};
2291 my $to_placed_on = $params{to_placed_on};
2292 my $basket = $params{basket};
2293 my $booksellerinvoicenumber = $params{booksellerinvoicenumber};
2294 my $basketgroupname = $params{basketgroupname};
2295 my $budget = $params{budget};
2296 my $orderstatus = $params{orderstatus};
2297 my $biblionumber = $params{biblionumber};
2298 my $get_canceled_order = $params{get_canceled_order} || 0;
2299 my $ordernumber = $params{ordernumber};
2300 my $search_children_too = $params{search_children_too} || 0;
2301 my $created_by = $params{created_by} || [];
2302 my $ordernumbers = $params{ordernumbers} || [];
2304 my @order_loop;
2305 my $total_qty = 0;
2306 my $total_qtyreceived = 0;
2307 my $total_price = 0;
2309 #get variation of isbn
2310 my @isbn_params;
2311 my @isbns;
2312 if ($isbn){
2313 if ( C4::Context->preference("SearchWithISBNVariations") ){
2314 @isbns = C4::Koha::GetVariationsOfISBN( $isbn );
2315 foreach my $isb (@isbns){
2316 push @isbn_params, '?';
2319 unless (@isbns){
2320 push @isbns, $isbn;
2321 push @isbn_params, '?';
2325 my $dbh = C4::Context->dbh;
2326 my $query ="
2327 SELECT
2328 COALESCE(biblio.title, deletedbiblio.title) AS title,
2329 COALESCE(biblio.author, deletedbiblio.author) AS author,
2330 COALESCE(biblioitems.isbn, deletedbiblioitems.isbn) AS isbn,
2331 COALESCE(biblioitems.ean, deletedbiblioitems.ean) AS ean,
2332 aqorders.basketno,
2333 aqbasket.basketname,
2334 aqbasket.basketgroupid,
2335 aqbasket.authorisedby,
2336 concat( borrowers.firstname,' ',borrowers.surname) AS authorisedbyname,
2337 aqbasketgroups.name as groupname,
2338 aqbooksellers.name,
2339 aqbasket.creationdate,
2340 aqorders.datereceived,
2341 aqorders.quantity,
2342 aqorders.quantityreceived,
2343 aqorders.ecost,
2344 aqorders.ordernumber,
2345 aqorders.invoiceid,
2346 aqinvoices.invoicenumber,
2347 aqbooksellers.id as id,
2348 aqorders.biblionumber,
2349 aqorders.orderstatus,
2350 aqorders.parent_ordernumber,
2351 aqbudgets.budget_name
2353 $query .= ", aqbudgets.budget_id AS budget" if defined $budget;
2354 $query .= "
2355 FROM aqorders
2356 LEFT JOIN aqbasket ON aqorders.basketno=aqbasket.basketno
2357 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid=aqbasketgroups.id
2358 LEFT JOIN aqbooksellers ON aqbasket.booksellerid=aqbooksellers.id
2359 LEFT JOIN biblioitems ON biblioitems.biblionumber=aqorders.biblionumber
2360 LEFT JOIN biblio ON biblio.biblionumber=aqorders.biblionumber
2361 LEFT JOIN aqbudgets ON aqorders.budget_id=aqbudgets.budget_id
2362 LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
2363 LEFT JOIN deletedbiblio ON deletedbiblio.biblionumber=aqorders.biblionumber
2364 LEFT JOIN deletedbiblioitems ON deletedbiblioitems.biblionumber=aqorders.biblionumber
2365 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
2368 $query .= " WHERE 1 ";
2370 unless ($get_canceled_order or (defined $orderstatus and $orderstatus eq 'cancelled')) {
2371 $query .= " AND (datecancellationprinted is NULL or datecancellationprinted='0000-00-00') ";
2374 my @query_params = ();
2376 if ( $biblionumber ) {
2377 $query .= " AND biblio.biblionumber = ?";
2378 push @query_params, $biblionumber;
2381 if ( $title ) {
2382 $query .= " AND biblio.title LIKE ? ";
2383 $title =~ s/\s+/%/g;
2384 push @query_params, "%$title%";
2387 if ( $author ) {
2388 $query .= " AND biblio.author LIKE ? ";
2389 push @query_params, "%$author%";
2392 if ( @isbns ) {
2393 $query .= " AND ( biblioitems.isbn LIKE " . join (" OR biblioitems.isbn LIKE ", @isbn_params ) . ")";
2394 foreach my $isb (@isbns){
2395 push @query_params, "%$isb%";
2399 if ( $ean ) {
2400 $query .= " AND biblioitems.ean = ? ";
2401 push @query_params, "$ean";
2403 if ( $name ) {
2404 $query .= " AND aqbooksellers.name LIKE ? ";
2405 push @query_params, "%$name%";
2408 if ( $budget ) {
2409 $query .= " AND aqbudgets.budget_id = ? ";
2410 push @query_params, "$budget";
2413 if ( $from_placed_on ) {
2414 $query .= " AND creationdate >= ? ";
2415 push @query_params, $from_placed_on;
2418 if ( $to_placed_on ) {
2419 $query .= " AND creationdate <= ? ";
2420 push @query_params, $to_placed_on;
2423 if ( defined $orderstatus and $orderstatus ne '') {
2424 $query .= " AND aqorders.orderstatus = ? ";
2425 push @query_params, "$orderstatus";
2428 if ($basket) {
2429 if ($basket =~ m/^\d+$/) {
2430 $query .= " AND aqorders.basketno = ? ";
2431 push @query_params, $basket;
2432 } else {
2433 $query .= " AND aqbasket.basketname LIKE ? ";
2434 push @query_params, "%$basket%";
2438 if ($booksellerinvoicenumber) {
2439 $query .= " AND aqinvoices.invoicenumber LIKE ? ";
2440 push @query_params, "%$booksellerinvoicenumber%";
2443 if ($basketgroupname) {
2444 $query .= " AND aqbasketgroups.name LIKE ? ";
2445 push @query_params, "%$basketgroupname%";
2448 if ($ordernumber) {
2449 $query .= " AND (aqorders.ordernumber = ? ";
2450 push @query_params, $ordernumber;
2451 if ($search_children_too) {
2452 $query .= " OR aqorders.parent_ordernumber = ? ";
2453 push @query_params, $ordernumber;
2455 $query .= ") ";
2458 if ( @$created_by ) {
2459 $query .= ' AND aqbasket.authorisedby IN ( ' . join( ',', ('?') x @$created_by ) . ')';
2460 push @query_params, @$created_by;
2463 if ( @$ordernumbers ) {
2464 $query .= ' AND (aqorders.ordernumber IN ( ' . join (',', ('?') x @$ordernumbers ) . '))';
2465 push @query_params, @$ordernumbers;
2468 if ( C4::Context->preference("IndependentBranches") ) {
2469 unless ( C4::Context->IsSuperLibrarian() ) {
2470 $query .= " AND (borrowers.branchcode = ? OR borrowers.branchcode ='' ) ";
2471 push @query_params, C4::Context->userenv->{branch};
2474 $query .= " ORDER BY id";
2476 return $dbh->selectall_arrayref( $query, { Slice => {} }, @query_params );
2479 =head2 GetRecentAcqui
2481 $results = GetRecentAcqui($days);
2483 C<$results> is a ref to a table which contains hashref
2485 =cut
2487 sub GetRecentAcqui {
2488 my $limit = shift;
2489 my $dbh = C4::Context->dbh;
2490 my $query = "
2491 SELECT *
2492 FROM biblio
2493 ORDER BY timestamp DESC
2494 LIMIT 0,".$limit;
2496 my $sth = $dbh->prepare($query);
2497 $sth->execute;
2498 my $results = $sth->fetchall_arrayref({});
2499 return $results;
2502 #------------------------------------------------------------#
2504 =head3 AddClaim
2506 &AddClaim($ordernumber);
2508 Add a claim for an order
2510 =cut
2512 sub AddClaim {
2513 my ($ordernumber) = @_;
2514 my $dbh = C4::Context->dbh;
2515 my $query = "
2516 UPDATE aqorders SET
2517 claims_count = claims_count + 1,
2518 claimed_date = CURDATE()
2519 WHERE ordernumber = ?
2521 my $sth = $dbh->prepare($query);
2522 $sth->execute($ordernumber);
2525 =head3 GetInvoices
2527 my @invoices = GetInvoices(
2528 invoicenumber => $invoicenumber,
2529 supplierid => $supplierid,
2530 suppliername => $suppliername,
2531 shipmentdatefrom => $shipmentdatefrom, # ISO format
2532 shipmentdateto => $shipmentdateto, # ISO format
2533 billingdatefrom => $billingdatefrom, # ISO format
2534 billingdateto => $billingdateto, # ISO format
2535 isbneanissn => $isbn_or_ean_or_issn,
2536 title => $title,
2537 author => $author,
2538 publisher => $publisher,
2539 publicationyear => $publicationyear,
2540 branchcode => $branchcode,
2541 order_by => $order_by
2544 Return a list of invoices that match all given criteria.
2546 $order_by is "column_name (asc|desc)", where column_name is any of
2547 'invoicenumber', 'booksellerid', 'shipmentdate', 'billingdate', 'closedate',
2548 'shipmentcost', 'shipmentcost_budgetid'.
2550 asc is the default if omitted
2552 =cut
2554 sub GetInvoices {
2555 my %args = @_;
2557 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2558 closedate shipmentcost shipmentcost_budgetid);
2560 my $dbh = C4::Context->dbh;
2561 my $query = qq{
2562 SELECT aqinvoices.invoiceid, aqinvoices.invoicenumber, aqinvoices.booksellerid, aqinvoices.shipmentdate, aqinvoices.billingdate, aqinvoices.closedate, aqinvoices.shipmentcost, aqinvoices.shipmentcost_budgetid, aqinvoices.message_id,
2563 aqbooksellers.name AS suppliername,
2564 COUNT(
2565 DISTINCT IF(
2566 aqorders.datereceived IS NOT NULL,
2567 aqorders.biblionumber,
2568 NULL
2570 ) AS receivedbiblios,
2571 COUNT(
2572 DISTINCT IF(
2573 aqorders.subscriptionid IS NOT NULL,
2574 aqorders.subscriptionid,
2575 NULL
2577 ) AS is_linked_to_subscriptions,
2578 SUM(aqorders.quantityreceived) AS receiveditems
2579 FROM aqinvoices
2580 LEFT JOIN aqbooksellers ON aqbooksellers.id = aqinvoices.booksellerid
2581 LEFT JOIN aqorders ON aqorders.invoiceid = aqinvoices.invoiceid
2582 LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
2583 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
2584 LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2585 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
2586 LEFT JOIN subscription ON biblio.biblionumber = subscription.biblionumber
2589 my @bind_args;
2590 my @bind_strs;
2591 if($args{supplierid}) {
2592 push @bind_strs, " aqinvoices.booksellerid = ? ";
2593 push @bind_args, $args{supplierid};
2595 if($args{invoicenumber}) {
2596 push @bind_strs, " aqinvoices.invoicenumber LIKE ? ";
2597 push @bind_args, "%$args{invoicenumber}%";
2599 if($args{suppliername}) {
2600 push @bind_strs, " aqbooksellers.name LIKE ? ";
2601 push @bind_args, "%$args{suppliername}%";
2603 if($args{shipmentdatefrom}) {
2604 push @bind_strs, " aqinvoices.shipmentdate >= ? ";
2605 push @bind_args, $args{shipmentdatefrom};
2607 if($args{shipmentdateto}) {
2608 push @bind_strs, " aqinvoices.shipmentdate <= ? ";
2609 push @bind_args, $args{shipmentdateto};
2611 if($args{billingdatefrom}) {
2612 push @bind_strs, " aqinvoices.billingdate >= ? ";
2613 push @bind_args, $args{billingdatefrom};
2615 if($args{billingdateto}) {
2616 push @bind_strs, " aqinvoices.billingdate <= ? ";
2617 push @bind_args, $args{billingdateto};
2619 if($args{isbneanissn}) {
2620 push @bind_strs, " (biblioitems.isbn LIKE CONCAT('%', ?, '%') OR biblioitems.ean LIKE CONCAT('%', ?, '%') OR biblioitems.issn LIKE CONCAT('%', ?, '%') ) ";
2621 push @bind_args, $args{isbneanissn}, $args{isbneanissn}, $args{isbneanissn};
2623 if($args{title}) {
2624 push @bind_strs, " biblio.title LIKE CONCAT('%', ?, '%') ";
2625 push @bind_args, $args{title};
2627 if($args{author}) {
2628 push @bind_strs, " biblio.author LIKE CONCAT('%', ?, '%') ";
2629 push @bind_args, $args{author};
2631 if($args{publisher}) {
2632 push @bind_strs, " biblioitems.publishercode LIKE CONCAT('%', ?, '%') ";
2633 push @bind_args, $args{publisher};
2635 if($args{publicationyear}) {
2636 push @bind_strs, " ((biblioitems.publicationyear LIKE CONCAT('%', ?, '%')) OR (biblio.copyrightdate LIKE CONCAT('%', ?, '%'))) ";
2637 push @bind_args, $args{publicationyear}, $args{publicationyear};
2639 if($args{branchcode}) {
2640 push @bind_strs, " borrowers.branchcode = ? ";
2641 push @bind_args, $args{branchcode};
2643 if($args{message_id}) {
2644 push @bind_strs, " aqinvoices.message_id = ? ";
2645 push @bind_args, $args{message_id};
2648 $query .= " WHERE " . join(" AND ", @bind_strs) if @bind_strs;
2649 $query .= " GROUP BY aqinvoices.invoiceid, aqinvoices.invoicenumber, aqinvoices.booksellerid, aqinvoices.shipmentdate, aqinvoices.billingdate, aqinvoices.closedate, aqinvoices.shipmentcost, aqinvoices.shipmentcost_budgetid, aqinvoices.message_id, aqbooksellers.name";
2651 if($args{order_by}) {
2652 my ($column, $direction) = split / /, $args{order_by};
2653 if(grep /^$column$/, @columns) {
2654 $direction ||= 'ASC';
2655 $query .= " ORDER BY $column $direction";
2659 my $sth = $dbh->prepare($query);
2660 $sth->execute(@bind_args);
2662 my $results = $sth->fetchall_arrayref({});
2663 return @$results;
2666 =head3 GetInvoice
2668 my $invoice = GetInvoice($invoiceid);
2670 Get informations about invoice with given $invoiceid
2672 Return a hash filled with aqinvoices.* fields
2674 =cut
2676 sub GetInvoice {
2677 my ($invoiceid) = @_;
2678 my $invoice;
2680 return unless $invoiceid;
2682 my $dbh = C4::Context->dbh;
2683 my $query = qq{
2684 SELECT *
2685 FROM aqinvoices
2686 WHERE invoiceid = ?
2688 my $sth = $dbh->prepare($query);
2689 $sth->execute($invoiceid);
2691 $invoice = $sth->fetchrow_hashref;
2692 return $invoice;
2695 =head3 GetInvoiceDetails
2697 my $invoice = GetInvoiceDetails($invoiceid)
2699 Return informations about an invoice + the list of related order lines
2701 Orders informations are in $invoice->{orders} (array ref)
2703 =cut
2705 sub GetInvoiceDetails {
2706 my ($invoiceid) = @_;
2708 if ( !defined $invoiceid ) {
2709 carp 'GetInvoiceDetails called without an invoiceid';
2710 return;
2713 my $dbh = C4::Context->dbh;
2714 my $query = q{
2715 SELECT aqinvoices.*, aqbooksellers.name AS suppliername
2716 FROM aqinvoices
2717 LEFT JOIN aqbooksellers ON aqinvoices.booksellerid = aqbooksellers.id
2718 WHERE invoiceid = ?
2720 my $sth = $dbh->prepare($query);
2721 $sth->execute($invoiceid);
2723 my $invoice = $sth->fetchrow_hashref;
2725 $query = q{
2726 SELECT aqorders.*,
2727 biblio.*,
2728 biblio.copyrightdate,
2729 biblioitems.isbn,
2730 biblioitems.publishercode,
2731 biblioitems.publicationyear,
2732 aqbasket.basketname,
2733 aqbasketgroups.id AS basketgroupid,
2734 aqbasketgroups.name AS basketgroupname
2735 FROM aqorders
2736 LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
2737 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
2738 LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2739 LEFT JOIN biblioitems ON aqorders.biblionumber = biblioitems.biblionumber
2740 WHERE invoiceid = ?
2742 $sth = $dbh->prepare($query);
2743 $sth->execute($invoiceid);
2744 $invoice->{orders} = $sth->fetchall_arrayref({});
2745 $invoice->{orders} ||= []; # force an empty arrayref if fetchall_arrayref fails
2747 return $invoice;
2750 =head3 AddInvoice
2752 my $invoiceid = AddInvoice(
2753 invoicenumber => $invoicenumber,
2754 booksellerid => $booksellerid,
2755 shipmentdate => $shipmentdate,
2756 billingdate => $billingdate,
2757 closedate => $closedate,
2758 shipmentcost => $shipmentcost,
2759 shipmentcost_budgetid => $shipmentcost_budgetid
2762 Create a new invoice and return its id or undef if it fails.
2764 =cut
2766 sub AddInvoice {
2767 my %invoice = @_;
2769 return unless(%invoice and $invoice{invoicenumber});
2771 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2772 closedate shipmentcost shipmentcost_budgetid message_id);
2774 my @set_strs;
2775 my @set_args;
2776 foreach my $key (keys %invoice) {
2777 if(0 < grep(/^$key$/, @columns)) {
2778 push @set_strs, "$key = ?";
2779 push @set_args, ($invoice{$key} || undef);
2783 my $rv;
2784 if(@set_args > 0) {
2785 my $dbh = C4::Context->dbh;
2786 my $query = "INSERT INTO aqinvoices SET ";
2787 $query .= join (",", @set_strs);
2788 my $sth = $dbh->prepare($query);
2789 $rv = $sth->execute(@set_args);
2790 if($rv) {
2791 $rv = $dbh->last_insert_id(undef, undef, 'aqinvoices', undef);
2794 return $rv;
2797 =head3 ModInvoice
2799 ModInvoice(
2800 invoiceid => $invoiceid, # Mandatory
2801 invoicenumber => $invoicenumber,
2802 booksellerid => $booksellerid,
2803 shipmentdate => $shipmentdate,
2804 billingdate => $billingdate,
2805 closedate => $closedate,
2806 shipmentcost => $shipmentcost,
2807 shipmentcost_budgetid => $shipmentcost_budgetid
2810 Modify an invoice, invoiceid is mandatory.
2812 Return undef if it fails.
2814 =cut
2816 sub ModInvoice {
2817 my %invoice = @_;
2819 return unless(%invoice and $invoice{invoiceid});
2821 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2822 closedate shipmentcost shipmentcost_budgetid);
2824 my @set_strs;
2825 my @set_args;
2826 foreach my $key (keys %invoice) {
2827 if(0 < grep(/^$key$/, @columns)) {
2828 push @set_strs, "$key = ?";
2829 push @set_args, ($invoice{$key} || undef);
2833 my $dbh = C4::Context->dbh;
2834 my $query = "UPDATE aqinvoices SET ";
2835 $query .= join(",", @set_strs);
2836 $query .= " WHERE invoiceid = ?";
2838 my $sth = $dbh->prepare($query);
2839 $sth->execute(@set_args, $invoice{invoiceid});
2842 =head3 CloseInvoice
2844 CloseInvoice($invoiceid);
2846 Close an invoice.
2848 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => undef);
2850 =cut
2852 sub CloseInvoice {
2853 my ($invoiceid) = @_;
2855 return unless $invoiceid;
2857 my $dbh = C4::Context->dbh;
2858 my $query = qq{
2859 UPDATE aqinvoices
2860 SET closedate = CAST(NOW() AS DATE)
2861 WHERE invoiceid = ?
2863 my $sth = $dbh->prepare($query);
2864 $sth->execute($invoiceid);
2867 =head3 ReopenInvoice
2869 ReopenInvoice($invoiceid);
2871 Reopen an invoice
2873 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => output_pref({ dt=>dt_from_string, dateonly=>1, otputpref=>'iso' }))
2875 =cut
2877 sub ReopenInvoice {
2878 my ($invoiceid) = @_;
2880 return unless $invoiceid;
2882 my $dbh = C4::Context->dbh;
2883 my $query = qq{
2884 UPDATE aqinvoices
2885 SET closedate = NULL
2886 WHERE invoiceid = ?
2888 my $sth = $dbh->prepare($query);
2889 $sth->execute($invoiceid);
2892 =head3 DelInvoice
2894 DelInvoice($invoiceid);
2896 Delete an invoice if there are no items attached to it.
2898 =cut
2900 sub DelInvoice {
2901 my ($invoiceid) = @_;
2903 return unless $invoiceid;
2905 my $dbh = C4::Context->dbh;
2906 my $query = qq{
2907 SELECT COUNT(*)
2908 FROM aqorders
2909 WHERE invoiceid = ?
2911 my $sth = $dbh->prepare($query);
2912 $sth->execute($invoiceid);
2913 my $res = $sth->fetchrow_arrayref;
2914 if ( $res && $res->[0] == 0 ) {
2915 $query = qq{
2916 DELETE FROM aqinvoices
2917 WHERE invoiceid = ?
2919 my $sth = $dbh->prepare($query);
2920 return ( $sth->execute($invoiceid) > 0 );
2922 return;
2925 =head3 MergeInvoices
2927 MergeInvoices($invoiceid, \@sourceids);
2929 Merge the invoices identified by the IDs in \@sourceids into
2930 the invoice identified by $invoiceid.
2932 =cut
2934 sub MergeInvoices {
2935 my ($invoiceid, $sourceids) = @_;
2937 return unless $invoiceid;
2938 foreach my $sourceid (@$sourceids) {
2939 next if $sourceid == $invoiceid;
2940 my $source = GetInvoiceDetails($sourceid);
2941 foreach my $order (@{$source->{'orders'}}) {
2942 $order->{'invoiceid'} = $invoiceid;
2943 ModOrder($order);
2945 DelInvoice($source->{'invoiceid'});
2947 return;
2950 =head3 GetBiblioCountByBasketno
2952 $biblio_count = &GetBiblioCountByBasketno($basketno);
2954 Looks up the biblio's count that has basketno value $basketno
2956 Returns a quantity
2958 =cut
2960 sub GetBiblioCountByBasketno {
2961 my ($basketno) = @_;
2962 my $dbh = C4::Context->dbh;
2963 my $query = "
2964 SELECT COUNT( DISTINCT( biblionumber ) )
2965 FROM aqorders
2966 WHERE basketno = ?
2967 AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')
2970 my $sth = $dbh->prepare($query);
2971 $sth->execute($basketno);
2972 return $sth->fetchrow;
2975 # Note this subroutine should be moved to Koha::Acquisition::Order
2976 # Will do when a DBIC decision will be taken.
2977 sub populate_order_with_prices {
2978 my ($params) = @_;
2980 my $order = $params->{order};
2981 my $booksellerid = $params->{booksellerid};
2982 return unless $booksellerid;
2984 my $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
2986 my $receiving = $params->{receiving};
2987 my $ordering = $params->{ordering};
2988 my $discount = $order->{discount};
2989 $discount /= 100 if $discount > 1;
2991 if ($ordering) {
2992 $order->{tax_rate_on_ordering} //= $order->{tax_rate};
2993 if ( $bookseller->listincgst ) {
2994 # The user entered the rrp tax included
2995 $order->{rrp_tax_included} = $order->{rrp};
2997 # rrp tax excluded = rrp tax included / ( 1 + tax rate )
2998 $order->{rrp_tax_excluded} = $order->{rrp_tax_included} / ( 1 + $order->{tax_rate_on_ordering} );
3000 # ecost tax excluded = rrp tax excluded * ( 1 - discount )
3001 $order->{ecost_tax_excluded} = $order->{rrp_tax_excluded} * ( 1 - $discount );
3003 # ecost tax included = rrp tax included ( 1 - discount )
3004 $order->{ecost_tax_included} = $order->{rrp_tax_included} * ( 1 - $discount );
3006 else {
3007 # The user entered the rrp tax excluded
3008 $order->{rrp_tax_excluded} = $order->{rrp};
3010 # rrp tax included = rrp tax excluded * ( 1 - tax rate )
3011 $order->{rrp_tax_included} = $order->{rrp_tax_excluded} * ( 1 + $order->{tax_rate_on_ordering} );
3013 # ecost tax excluded = rrp tax excluded * ( 1 - discount )
3014 $order->{ecost_tax_excluded} = $order->{rrp_tax_excluded} * ( 1 - $discount );
3016 # ecost tax included = rrp tax excluded * ( 1 + tax rate ) * ( 1 - discount )
3017 $order->{ecost_tax_included} =
3018 $order->{rrp_tax_excluded} *
3019 ( 1 + $order->{tax_rate_on_ordering} ) *
3020 ( 1 - $discount );
3023 # tax value = quantity * ecost tax excluded * tax rate
3024 $order->{tax_value_on_ordering} =
3025 $order->{quantity} * $order->{ecost_tax_excluded} * $order->{tax_rate_on_ordering};
3028 if ($receiving) {
3029 $order->{tax_rate_on_receiving} //= $order->{tax_rate};
3030 if ( $bookseller->invoiceincgst ) {
3031 # Trick for unitprice. If the unit price rounded value is the same as the ecost rounded value
3032 # we need to keep the exact ecost value
3033 if ( Koha::Number::Price->new( $order->{unitprice} )->round == Koha::Number::Price->new( $order->{ecost_tax_included} )->round ) {
3034 $order->{unitprice} = $order->{ecost_tax_included};
3037 # The user entered the unit price tax included
3038 $order->{unitprice_tax_included} = $order->{unitprice};
3040 # unit price tax excluded = unit price tax included / ( 1 + tax rate )
3041 $order->{unitprice_tax_excluded} = $order->{unitprice_tax_included} / ( 1 + $order->{tax_rate_on_receiving} );
3043 else {
3044 # Trick for unitprice. If the unit price rounded value is the same as the ecost rounded value
3045 # we need to keep the exact ecost value
3046 if ( Koha::Number::Price->new( $order->{unitprice} )->round == Koha::Number::Price->new( $order->{ecost_tax_excluded} )->round ) {
3047 $order->{unitprice} = $order->{ecost_tax_excluded};
3050 # The user entered the unit price tax excluded
3051 $order->{unitprice_tax_excluded} = $order->{unitprice};
3054 # unit price tax included = unit price tax included * ( 1 + tax rate )
3055 $order->{unitprice_tax_included} = $order->{unitprice_tax_excluded} * ( 1 + $order->{tax_rate_on_receiving} );
3058 # tax value = quantity * unit price tax excluded * tax rate
3059 $order->{tax_value_on_receiving} = $order->{quantity} * $order->{unitprice_tax_excluded} * $order->{tax_rate_on_receiving};
3062 return $order;
3065 =head3 GetOrderUsers
3067 $order_users_ids = &GetOrderUsers($ordernumber);
3069 Returns a list of all borrowernumbers that are in order users list
3071 =cut
3073 sub GetOrderUsers {
3074 my ($ordernumber) = @_;
3076 return unless $ordernumber;
3078 my $query = q|
3079 SELECT borrowernumber
3080 FROM aqorder_users
3081 WHERE ordernumber = ?
3083 my $dbh = C4::Context->dbh;
3084 my $sth = $dbh->prepare($query);
3085 $sth->execute($ordernumber);
3086 my $results = $sth->fetchall_arrayref( {} );
3088 my @borrowernumbers;
3089 foreach (@$results) {
3090 push @borrowernumbers, $_->{'borrowernumber'};
3093 return @borrowernumbers;
3096 =head3 ModOrderUsers
3098 my @order_users_ids = (1, 2, 3);
3099 &ModOrderUsers($ordernumber, @basketusers_ids);
3101 Delete all users from order users list, and add users in C<@order_users_ids>
3102 to this users list.
3104 =cut
3106 sub ModOrderUsers {
3107 my ( $ordernumber, @order_users_ids ) = @_;
3109 return unless $ordernumber;
3111 my $dbh = C4::Context->dbh;
3112 my $query = q|
3113 DELETE FROM aqorder_users
3114 WHERE ordernumber = ?
3116 my $sth = $dbh->prepare($query);
3117 $sth->execute($ordernumber);
3119 $query = q|
3120 INSERT INTO aqorder_users (ordernumber, borrowernumber)
3121 VALUES (?, ?)
3123 $sth = $dbh->prepare($query);
3124 foreach my $order_user_id (@order_users_ids) {
3125 $sth->execute( $ordernumber, $order_user_id );
3129 sub NotifyOrderUsers {
3130 my ($ordernumber) = @_;
3132 my @borrowernumbers = GetOrderUsers($ordernumber);
3133 return unless @borrowernumbers;
3135 my $order = GetOrder( $ordernumber );
3136 for my $borrowernumber (@borrowernumbers) {
3137 my $patron = Koha::Patrons->find( $borrowernumber );
3138 my $library = $patron->library->unblessed;
3139 my $biblio = Koha::Biblios->find( $order->{biblionumber} )->unblessed;
3140 my $letter = C4::Letters::GetPreparedLetter(
3141 module => 'acquisition',
3142 letter_code => 'ACQ_NOTIF_ON_RECEIV',
3143 branchcode => $library->{branchcode},
3144 lang => $patron->lang,
3145 tables => {
3146 'branches' => $library,
3147 'borrowers' => $patron->unblessed,
3148 'biblio' => $biblio,
3149 'aqorders' => $order,
3152 if ( $letter ) {
3153 C4::Letters::EnqueueLetter(
3155 letter => $letter,
3156 borrowernumber => $borrowernumber,
3157 LibraryName => C4::Context->preference("LibraryName"),
3158 message_transport_type => 'email',
3160 ) or warn "can't enqueue letter $letter";
3165 =head3 FillWithDefaultValues
3167 FillWithDefaultValues( $marc_record );
3169 This will update the record with default value defined in the ACQ framework.
3170 For all existing fields, if a default value exists and there are no subfield, it will be created.
3171 If the field does not exist, it will be created too.
3173 =cut
3175 sub FillWithDefaultValues {
3176 my ($record) = @_;
3177 my $tagslib = C4::Biblio::GetMarcStructure( 1, 'ACQ', { unsafe => 1 } );
3178 if ($tagslib) {
3179 my ($itemfield) =
3180 C4::Biblio::GetMarcFromKohaField( 'items.itemnumber', '' );
3181 for my $tag ( sort keys %$tagslib ) {
3182 next unless $tag;
3183 next if $tag == $itemfield;
3184 for my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
3185 next if IsMarcStructureInternal($tagslib->{$tag}{$subfield});
3186 my $defaultvalue = $tagslib->{$tag}{$subfield}{defaultvalue};
3187 if ( defined $defaultvalue and $defaultvalue ne '' ) {
3188 my @fields = $record->field($tag);
3189 if (@fields) {
3190 for my $field (@fields) {
3191 unless ( defined $field->subfield($subfield) ) {
3192 $field->add_subfields(
3193 $subfield => $defaultvalue );
3197 else {
3198 $record->insert_fields_ordered(
3199 MARC::Field->new(
3200 $tag, '', '', $subfield => $defaultvalue
3211 __END__
3213 =head1 AUTHOR
3215 Koha Development Team <http://koha-community.org/>
3217 =cut