Bug 15339: Remove extra 'my'
[koha.git] / C4 / Acquisition.pm
blobd0381ce4adfd9b781edc856257578c71bcaade15
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::Order;
32 use Koha::Acquisition::Booksellers;
33 use Koha::Biblios;
34 use Koha::Items;
35 use Koha::Number::Price;
36 use Koha::Libraries;
37 use Koha::CsvProfiles;
38 use Koha::Patrons;
40 use C4::Koha;
42 use MARC::Field;
43 use MARC::Record;
45 use Time::localtime;
47 use vars qw(@ISA @EXPORT);
49 BEGIN {
50 require Exporter;
51 @ISA = qw(Exporter);
52 @EXPORT = qw(
53 &GetBasket &NewBasket &CloseBasket &ReopenBasket &DelBasket &ModBasket
54 &GetBasketAsCSV &GetBasketGroupAsCSV
55 &GetBasketsByBookseller &GetBasketsByBasketgroup
56 &GetBasketsInfosByBookseller
58 &GetBasketUsers &ModBasketUsers
59 &CanUserManageBasket
61 &ModBasketHeader
63 &ModBasketgroup &NewBasketgroup &DelBasketgroup &GetBasketgroup &CloseBasketgroup
64 &GetBasketgroups &ReOpenBasketgroup
66 &DelOrder &ModOrder &GetOrder &GetOrders &GetOrdersByBiblionumber
67 &GetLateOrders &GetOrderFromItemnumber
68 &SearchOrders &GetHistory &GetRecentAcqui
69 &ModReceiveOrder &CancelReceipt
70 &TransferOrder
71 &GetLastOrderNotReceivedFromSubscriptionid &GetLastOrderReceivedFromSubscriptionid
72 &ModItemOrder
74 &GetParcels
76 &GetInvoices
77 &GetInvoice
78 &GetInvoiceDetails
79 &AddInvoice
80 &ModInvoice
81 &CloseInvoice
82 &ReopenInvoice
83 &DelInvoice
84 &MergeInvoices
86 &GetItemnumbersFromOrder
88 &AddClaim
89 &GetBiblioCountByBasketno
91 &GetOrderUsers
92 &ModOrderUsers
93 &NotifyOrderUsers
95 &FillWithDefaultValues
103 sub GetOrderFromItemnumber {
104 my ($itemnumber) = @_;
105 my $dbh = C4::Context->dbh;
106 my $query = qq|
108 SELECT * from aqorders LEFT JOIN aqorders_items
109 ON ( aqorders.ordernumber = aqorders_items.ordernumber )
110 WHERE itemnumber = ? |;
112 my $sth = $dbh->prepare($query);
114 # $sth->trace(3);
116 $sth->execute($itemnumber);
118 my $order = $sth->fetchrow_hashref;
119 return ( $order );
123 # Returns the itemnumber(s) associated with the ordernumber given in parameter
124 sub GetItemnumbersFromOrder {
125 my ($ordernumber) = @_;
126 my $dbh = C4::Context->dbh;
127 my $query = "SELECT itemnumber FROM aqorders_items WHERE ordernumber=?";
128 my $sth = $dbh->prepare($query);
129 $sth->execute($ordernumber);
130 my @tab;
132 while (my $order = $sth->fetchrow_hashref) {
133 push @tab, $order->{'itemnumber'};
136 return @tab;
145 =head1 NAME
147 C4::Acquisition - Koha functions for dealing with orders and acquisitions
149 =head1 SYNOPSIS
151 use C4::Acquisition;
153 =head1 DESCRIPTION
155 The functions in this module deal with acquisitions, managing book
156 orders, basket and parcels.
158 =head1 FUNCTIONS
160 =head2 FUNCTIONS ABOUT BASKETS
162 =head3 GetBasket
164 $aqbasket = &GetBasket($basketnumber);
166 get all basket informations in aqbasket for a given basket
168 B<returns:> informations for a given basket returned as a hashref.
170 =cut
172 sub GetBasket {
173 my ($basketno) = @_;
174 my $dbh = C4::Context->dbh;
175 my $query = "
176 SELECT aqbasket.*,
177 concat( b.firstname,' ',b.surname) AS authorisedbyname
178 FROM aqbasket
179 LEFT JOIN borrowers b ON aqbasket.authorisedby=b.borrowernumber
180 WHERE basketno=?
182 my $sth=$dbh->prepare($query);
183 $sth->execute($basketno);
184 my $basket = $sth->fetchrow_hashref;
185 return ( $basket );
188 #------------------------------------------------------------#
190 =head3 NewBasket
192 $basket = &NewBasket( $booksellerid, $authorizedby, $basketname,
193 $basketnote, $basketbooksellernote, $basketcontractnumber, $deliveryplace, $billingplace, $is_standing );
195 Create a new basket in aqbasket table
197 =over
199 =item C<$booksellerid> is a foreign key in the aqbasket table
201 =item C<$authorizedby> is the username of who created the basket
203 =back
205 The other parameters are optional, see ModBasketHeader for more info on them.
207 =cut
209 sub NewBasket {
210 my ( $booksellerid, $authorisedby, $basketname, $basketnote,
211 $basketbooksellernote, $basketcontractnumber, $deliveryplace,
212 $billingplace, $is_standing ) = @_;
213 my $dbh = C4::Context->dbh;
214 my $query =
215 'INSERT INTO aqbasket (creationdate,booksellerid,authorisedby) '
216 . 'VALUES (now(),?,?)';
217 $dbh->do( $query, {}, $booksellerid, $authorisedby );
219 my $basket = $dbh->{mysql_insertid};
220 $basketname ||= q{}; # default to empty strings
221 $basketnote ||= q{};
222 $basketbooksellernote ||= q{};
223 ModBasketHeader( $basket, $basketname, $basketnote, $basketbooksellernote,
224 $basketcontractnumber, $booksellerid, $deliveryplace, $billingplace, $is_standing );
225 return $basket;
228 #------------------------------------------------------------#
230 =head3 CloseBasket
232 &CloseBasket($basketno);
234 close a basket (becomes unmodifiable, except for receives)
236 =cut
238 sub CloseBasket {
239 my ($basketno) = @_;
240 my $dbh = C4::Context->dbh;
241 $dbh->do('UPDATE aqbasket SET closedate=now() WHERE basketno=?', {}, $basketno );
243 $dbh->do(
244 q{UPDATE aqorders SET orderstatus = 'ordered' WHERE basketno = ? AND orderstatus NOT IN ( 'complete', 'cancelled')},
245 {}, $basketno
247 return;
250 =head3 ReopenBasket
252 &ReopenBasket($basketno);
254 reopen a basket
256 =cut
258 sub ReopenBasket {
259 my ($basketno) = @_;
260 my $dbh = C4::Context->dbh;
261 $dbh->do( q{UPDATE aqbasket SET closedate=NULL WHERE basketno=?}, {}, $basketno );
263 $dbh->do( q{
264 UPDATE aqorders
265 SET orderstatus = 'new'
266 WHERE basketno = ?
267 AND orderstatus != 'complete'
268 }, {}, $basketno);
269 return;
272 #------------------------------------------------------------#
274 =head3 GetBasketAsCSV
276 &GetBasketAsCSV($basketno);
278 Export a basket as CSV
280 $cgi parameter is needed for column name translation
282 =cut
284 sub GetBasketAsCSV {
285 my ($basketno, $cgi, $csv_profile_id) = @_;
286 my $basket = GetBasket($basketno);
287 my @orders = GetOrders($basketno);
288 my $contract = GetContract({
289 contractnumber => $basket->{'contractnumber'}
292 my $template = C4::Templates::gettemplate("acqui/csv/basket.tt", "intranet", $cgi);
293 my @rows;
294 if ($csv_profile_id) {
295 my $csv_profile = Koha::CsvProfiles->find( $csv_profile_id );
296 die "There is no valid csv profile given" unless $csv_profile;
298 my $csv = Text::CSV_XS->new({'quote_char'=>'"','escape_char'=>'"','sep_char'=>$csv_profile->csv_separator,'binary'=>1});
299 my $csv_profile_content = $csv_profile->content;
300 my ( @headers, @fields );
301 while ( $csv_profile_content =~ /
302 ([^=\|]+) # header
304 ([^\|]*) # fieldname (table.row or row)
305 \|? /gxms
307 my $header = $1;
308 my $field = ($2 eq '') ? $1 : $2;
310 $header =~ s/^\s+|\s+$//g; # Trim whitespaces
311 push @headers, $header;
313 $field =~ s/[^\.]*\.{1}//; # Remove the table name if exists.
314 $field =~ s/^\s+|\s+$//g; # Trim whitespaces
315 push @fields, $field;
317 for my $order (@orders) {
318 my @row;
319 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
320 my $biblioitem = $biblio->biblioitem;
321 $order = { %$order, %{ $biblioitem->unblessed } };
322 if ($contract) {
323 $order = {%$order, %$contract};
325 $order = {%$order, %$basket, %{ $biblio->unblessed }};
326 for my $field (@fields) {
327 push @row, $order->{$field};
329 push @rows, \@row;
331 my $content = join( $csv_profile->csv_separator, @headers ) . "\n";
332 for my $row ( @rows ) {
333 $csv->combine(@$row);
334 my $string = $csv->string;
335 $content .= $string . "\n";
337 return $content;
339 else {
340 foreach my $order (@orders) {
341 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
342 my $biblioitem = $biblio->biblioitem;
343 my $row = {
344 contractname => $contract->{'contractname'},
345 ordernumber => $order->{'ordernumber'},
346 entrydate => $order->{'entrydate'},
347 isbn => $order->{'isbn'},
348 author => $biblio->author,
349 title => $biblio->title,
350 publicationyear => $biblioitem->publicationyear,
351 publishercode => $biblioitem->publishercode,
352 collectiontitle => $biblioitem->collectiontitle,
353 notes => $order->{'order_vendornote'},
354 quantity => $order->{'quantity'},
355 rrp => $order->{'rrp'},
357 for my $place ( qw( deliveryplace billingplace ) ) {
358 if ( my $library = Koha::Libraries->find( $row->{deliveryplace} ) ) {
359 $row->{$place} = $library->branchname
362 foreach(qw(
363 contractname author title publishercode collectiontitle notes
364 deliveryplace billingplace
365 ) ) {
366 # Double the quotes to not be interpreted as a field end
367 $row->{$_} =~ s/"/""/g if $row->{$_};
369 push @rows, $row;
372 @rows = sort {
373 if(defined $a->{publishercode} and defined $b->{publishercode}) {
374 $a->{publishercode} cmp $b->{publishercode};
376 } @rows;
378 $template->param(rows => \@rows);
380 return $template->output;
385 =head3 GetBasketGroupAsCSV
387 &GetBasketGroupAsCSV($basketgroupid);
389 Export a basket group as CSV
391 $cgi parameter is needed for column name translation
393 =cut
395 sub GetBasketGroupAsCSV {
396 my ($basketgroupid, $cgi) = @_;
397 my $baskets = GetBasketsByBasketgroup($basketgroupid);
399 my $template = C4::Templates::gettemplate('acqui/csv/basketgroup.tt', 'intranet', $cgi);
401 my @rows;
402 for my $basket (@$baskets) {
403 my @orders = GetOrders( $basket->{basketno} );
404 my $contract = GetContract({
405 contractnumber => $basket->{contractnumber}
407 my $bookseller = Koha::Acquisition::Booksellers->find( $basket->{booksellerid} );
408 my $basketgroup = GetBasketgroup( $$basket{basketgroupid} );
410 foreach my $order (@orders) {
411 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
412 my $biblioitem = $biblio->biblioitem;
413 my $row = {
414 clientnumber => $bookseller->accountnumber,
415 basketname => $basket->{basketname},
416 ordernumber => $order->{ordernumber},
417 author => $biblio->author,
418 title => $biblio->title,
419 publishercode => $biblioitem->publishercode,
420 publicationyear => $biblioitem->publicationyear,
421 collectiontitle => $biblioitem->collectiontitle,
422 isbn => $order->{isbn},
423 quantity => $order->{quantity},
424 rrp_tax_included => $order->{rrp_tax_included},
425 rrp_tax_excluded => $order->{rrp_tax_excluded},
426 discount => $bookseller->discount,
427 ecost_tax_included => $order->{ecost_tax_included},
428 ecost_tax_excluded => $order->{ecost_tax_excluded},
429 notes => $order->{order_vendornote},
430 entrydate => $order->{entrydate},
431 booksellername => $bookseller->name,
432 bookselleraddress => $bookseller->address1,
433 booksellerpostal => $bookseller->postal,
434 contractnumber => $contract->{contractnumber},
435 contractname => $contract->{contractname},
437 my $temp = {
438 basketgroupdeliveryplace => $basketgroup->{deliveryplace},
439 basketgroupbillingplace => $basketgroup->{billingplace},
440 basketdeliveryplace => $basket->{deliveryplace},
441 basketbillingplace => $basket->{billingplace},
443 for my $place (qw( basketgroupdeliveryplace basketgroupbillingplace basketdeliveryplace basketbillingplace )) {
444 if ( my $library = Koha::Libraries->find( $temp->{$place} ) ) {
445 $row->{$place} = $library->branchname;
448 foreach(qw(
449 basketname author title publishercode collectiontitle notes
450 booksellername bookselleraddress booksellerpostal contractname
451 basketgroupdeliveryplace basketgroupbillingplace
452 basketdeliveryplace basketbillingplace
453 ) ) {
454 # Double the quotes to not be interpreted as a field end
455 $row->{$_} =~ s/"/""/g if $row->{$_};
457 push @rows, $row;
460 $template->param(rows => \@rows);
462 return $template->output;
466 =head3 CloseBasketgroup
468 &CloseBasketgroup($basketgroupno);
470 close a basketgroup
472 =cut
474 sub CloseBasketgroup {
475 my ($basketgroupno) = @_;
476 my $dbh = C4::Context->dbh;
477 my $sth = $dbh->prepare("
478 UPDATE aqbasketgroups
479 SET closed=1
480 WHERE id=?
482 $sth->execute($basketgroupno);
485 #------------------------------------------------------------#
487 =head3 ReOpenBaskergroup($basketgroupno)
489 &ReOpenBaskergroup($basketgroupno);
491 reopen a basketgroup
493 =cut
495 sub ReOpenBasketgroup {
496 my ($basketgroupno) = @_;
497 my $dbh = C4::Context->dbh;
498 my $sth = $dbh->prepare("
499 UPDATE aqbasketgroups
500 SET closed=0
501 WHERE id=?
503 $sth->execute($basketgroupno);
506 #------------------------------------------------------------#
509 =head3 DelBasket
511 &DelBasket($basketno);
513 Deletes the basket that has basketno field $basketno in the aqbasket table.
515 =over
517 =item C<$basketno> is the primary key of the basket in the aqbasket table.
519 =back
521 =cut
523 sub DelBasket {
524 my ( $basketno ) = @_;
525 my $query = "DELETE FROM aqbasket WHERE basketno=?";
526 my $dbh = C4::Context->dbh;
527 my $sth = $dbh->prepare($query);
528 $sth->execute($basketno);
529 return;
532 #------------------------------------------------------------#
534 =head3 ModBasket
536 &ModBasket($basketinfo);
538 Modifies a basket, using a hashref $basketinfo for the relevant information, only $basketinfo->{'basketno'} is required.
540 =over
542 =item C<$basketno> is the primary key of the basket in the aqbasket table.
544 =back
546 =cut
548 sub ModBasket {
549 my $basketinfo = shift;
550 my $query = "UPDATE aqbasket SET ";
551 my @params;
552 foreach my $key (keys %$basketinfo){
553 if ($key ne 'basketno'){
554 $query .= "$key=?, ";
555 push(@params, $basketinfo->{$key} || undef );
558 # get rid of the "," at the end of $query
559 if (substr($query, length($query)-2) eq ', '){
560 chop($query);
561 chop($query);
562 $query .= ' ';
564 $query .= "WHERE basketno=?";
565 push(@params, $basketinfo->{'basketno'});
566 my $dbh = C4::Context->dbh;
567 my $sth = $dbh->prepare($query);
568 $sth->execute(@params);
570 return;
573 #------------------------------------------------------------#
575 =head3 ModBasketHeader
577 &ModBasketHeader($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid);
579 Modifies a basket's header.
581 =over
583 =item C<$basketno> is the "basketno" field in the "aqbasket" table;
585 =item C<$basketname> is the "basketname" field in the "aqbasket" table;
587 =item C<$note> is the "note" field in the "aqbasket" table;
589 =item C<$booksellernote> is the "booksellernote" field in the "aqbasket" table;
591 =item C<$contractnumber> is the "contractnumber" (foreign) key in the "aqbasket" table.
593 =item C<$booksellerid> is the id (foreign) key in the "aqbooksellers" table for the vendor.
595 =item C<$deliveryplace> is the "deliveryplace" field in the aqbasket table.
597 =item C<$billingplace> is the "billingplace" field in the aqbasket table.
599 =item C<$is_standing> is the "is_standing" field in the aqbasket table.
601 =back
603 =cut
605 sub ModBasketHeader {
606 my ($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid, $deliveryplace, $billingplace, $is_standing) = @_;
607 my $query = qq{
608 UPDATE aqbasket
609 SET basketname=?, note=?, booksellernote=?, booksellerid=?, deliveryplace=?, billingplace=?, is_standing=?
610 WHERE basketno=?
613 my $dbh = C4::Context->dbh;
614 my $sth = $dbh->prepare($query);
615 $sth->execute($basketname, $note, $booksellernote, $booksellerid, $deliveryplace, $billingplace, $is_standing, $basketno);
617 if ( $contractnumber ) {
618 my $query2 ="UPDATE aqbasket SET contractnumber=? WHERE basketno=?";
619 my $sth2 = $dbh->prepare($query2);
620 $sth2->execute($contractnumber,$basketno);
622 return;
625 #------------------------------------------------------------#
627 =head3 GetBasketsByBookseller
629 @results = &GetBasketsByBookseller($booksellerid, $extra);
631 Returns a list of hashes of all the baskets that belong to bookseller 'booksellerid'.
633 =over
635 =item C<$booksellerid> is the 'id' field of the bookseller in the aqbooksellers table
637 =item C<$extra> is the extra sql parameters, can be
639 $extra->{groupby}: group baskets by column
640 ex. $extra->{groupby} = aqbasket.basketgroupid
641 $extra->{orderby}: order baskets by column
642 $extra->{limit}: limit number of results (can be helpful for pagination)
644 =back
646 =cut
648 sub GetBasketsByBookseller {
649 my ($booksellerid, $extra) = @_;
650 my $query = "SELECT * FROM aqbasket WHERE booksellerid=?";
651 if ($extra){
652 if ($extra->{groupby}) {
653 $query .= " GROUP by $extra->{groupby}";
655 if ($extra->{orderby}){
656 $query .= " ORDER by $extra->{orderby}";
658 if ($extra->{limit}){
659 $query .= " LIMIT $extra->{limit}";
662 my $dbh = C4::Context->dbh;
663 my $sth = $dbh->prepare($query);
664 $sth->execute($booksellerid);
665 return $sth->fetchall_arrayref({});
668 =head3 GetBasketsInfosByBookseller
670 my $baskets = GetBasketsInfosByBookseller($supplierid, $allbaskets);
672 The optional second parameter allbaskets is a boolean allowing you to
673 select all baskets from the supplier; by default only active baskets (open or
674 closed but still something to receive) are returned.
676 Returns in a arrayref of hashref all about booksellers baskets, plus:
677 total_biblios: Number of distinct biblios in basket
678 total_items: Number of items in basket
679 expected_items: Number of non-received items in basket
681 =cut
683 sub GetBasketsInfosByBookseller {
684 my ($supplierid, $allbaskets) = @_;
686 return unless $supplierid;
688 my $dbh = C4::Context->dbh;
689 my $query = q{
690 SELECT aqbasket.*,
691 SUM(aqorders.quantity) AS total_items,
692 SUM(
693 IF ( aqorders.orderstatus = 'cancelled', aqorders.quantity, 0 )
694 ) AS total_items_cancelled,
695 COUNT(DISTINCT aqorders.biblionumber) AS total_biblios,
696 SUM(
697 IF(aqorders.datereceived IS NULL
698 AND aqorders.datecancellationprinted IS NULL
699 , aqorders.quantity
700 , 0)
701 ) AS expected_items
702 FROM aqbasket
703 LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
704 WHERE booksellerid = ?};
706 unless ( $allbaskets ) {
707 $query.=" AND (closedate IS NULL OR (aqorders.quantity > aqorders.quantityreceived AND datecancellationprinted IS NULL))";
709 $query.=" GROUP BY aqbasket.basketno";
711 my $sth = $dbh->prepare($query);
712 $sth->execute($supplierid);
713 my $baskets = $sth->fetchall_arrayref({});
715 # Retrieve the number of biblios cancelled
716 my $cancelled_biblios = $dbh->selectall_hashref( q|
717 SELECT COUNT(DISTINCT(biblionumber)) AS total_biblios_cancelled, aqbasket.basketno
718 FROM aqbasket
719 LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
720 WHERE booksellerid = ?
721 AND aqorders.orderstatus = 'cancelled'
722 GROUP BY aqbasket.basketno
723 |, 'basketno', {}, $supplierid );
724 map {
725 $_->{total_biblios_cancelled} = $cancelled_biblios->{$_->{basketno}}{total_biblios_cancelled} || 0
726 } @$baskets;
728 return $baskets;
731 =head3 GetBasketUsers
733 $basketusers_ids = &GetBasketUsers($basketno);
735 Returns a list of all borrowernumbers that are in basket users list
737 =cut
739 sub GetBasketUsers {
740 my $basketno = shift;
742 return unless $basketno;
744 my $query = qq{
745 SELECT borrowernumber
746 FROM aqbasketusers
747 WHERE basketno = ?
749 my $dbh = C4::Context->dbh;
750 my $sth = $dbh->prepare($query);
751 $sth->execute($basketno);
752 my $results = $sth->fetchall_arrayref( {} );
754 my @borrowernumbers;
755 foreach (@$results) {
756 push @borrowernumbers, $_->{'borrowernumber'};
759 return @borrowernumbers;
762 =head3 ModBasketUsers
764 my @basketusers_ids = (1, 2, 3);
765 &ModBasketUsers($basketno, @basketusers_ids);
767 Delete all users from basket users list, and add users in C<@basketusers_ids>
768 to this users list.
770 =cut
772 sub ModBasketUsers {
773 my ($basketno, @basketusers_ids) = @_;
775 return unless $basketno;
777 my $dbh = C4::Context->dbh;
778 my $query = qq{
779 DELETE FROM aqbasketusers
780 WHERE basketno = ?
782 my $sth = $dbh->prepare($query);
783 $sth->execute($basketno);
785 $query = qq{
786 INSERT INTO aqbasketusers (basketno, borrowernumber)
787 VALUES (?, ?)
789 $sth = $dbh->prepare($query);
790 foreach my $basketuser_id (@basketusers_ids) {
791 $sth->execute($basketno, $basketuser_id);
793 return;
796 =head3 CanUserManageBasket
798 my $bool = CanUserManageBasket($borrower, $basket[, $userflags]);
799 my $bool = CanUserManageBasket($borrowernumber, $basketno[, $userflags]);
801 Check if a borrower can manage a basket, according to system preference
802 AcqViewBaskets, user permissions and basket properties (creator, users list,
803 branch).
805 First parameter can be either a borrowernumber or a hashref as returned by
806 Koha::Patron->unblessed
808 Second parameter can be either a basketno or a hashref as returned by
809 C4::Acquisition::GetBasket.
811 The third parameter is optional. If given, it should be a hashref as returned
812 by C4::Auth::getuserflags. If not, getuserflags is called.
814 If user is authorised to manage basket, returns 1.
815 Otherwise returns 0.
817 =cut
819 sub CanUserManageBasket {
820 my ($borrower, $basket, $userflags) = @_;
822 if (!ref $borrower) {
823 # FIXME This needs to be replaced
824 # We should not accept both scalar and array
825 # Tests need to be updated
826 $borrower = Koha::Patrons->find( $borrower )->unblessed;
828 if (!ref $basket) {
829 $basket = GetBasket($basket);
832 return 0 unless ($basket and $borrower);
834 my $borrowernumber = $borrower->{borrowernumber};
835 my $basketno = $basket->{basketno};
837 my $AcqViewBaskets = C4::Context->preference('AcqViewBaskets');
839 if (!defined $userflags) {
840 my $dbh = C4::Context->dbh;
841 my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE borrowernumber = ?");
842 $sth->execute($borrowernumber);
843 my ($flags) = $sth->fetchrow_array;
844 $sth->finish;
846 $userflags = C4::Auth::getuserflags($flags, $borrower->{userid}, $dbh);
849 unless ($userflags->{superlibrarian}
850 || (ref $userflags->{acquisition} && $userflags->{acquisition}->{order_manage_all})
851 || (!ref $userflags->{acquisition} && $userflags->{acquisition}))
853 if (not exists $userflags->{acquisition}) {
854 return 0;
857 if ( (ref $userflags->{acquisition} && !$userflags->{acquisition}->{order_manage})
858 || (!ref $userflags->{acquisition} && !$userflags->{acquisition}) ) {
859 return 0;
862 if ($AcqViewBaskets eq 'user'
863 && $basket->{authorisedby} != $borrowernumber
864 && ! grep { $borrowernumber eq $_ } GetBasketUsers($basketno)) {
865 return 0;
868 if ($AcqViewBaskets eq 'branch' && defined $basket->{branch}
869 && $basket->{branch} ne $borrower->{branchcode}) {
870 return 0;
874 return 1;
877 #------------------------------------------------------------#
879 =head3 GetBasketsByBasketgroup
881 $baskets = &GetBasketsByBasketgroup($basketgroupid);
883 Returns a reference to all baskets that belong to basketgroup $basketgroupid.
885 =cut
887 sub GetBasketsByBasketgroup {
888 my $basketgroupid = shift;
889 my $query = qq{
890 SELECT *, aqbasket.booksellerid as booksellerid
891 FROM aqbasket
892 LEFT JOIN aqcontract USING(contractnumber) WHERE basketgroupid=?
894 my $dbh = C4::Context->dbh;
895 my $sth = $dbh->prepare($query);
896 $sth->execute($basketgroupid);
897 return $sth->fetchall_arrayref({});
900 #------------------------------------------------------------#
902 =head3 NewBasketgroup
904 $basketgroupid = NewBasketgroup(\%hashref);
906 Adds a basketgroup to the aqbasketgroups table, and add the initial baskets to it.
908 $hashref->{'booksellerid'} is the 'id' field of the bookseller in the aqbooksellers table,
910 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
912 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
914 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
916 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
918 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
920 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
922 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
924 =cut
926 sub NewBasketgroup {
927 my $basketgroupinfo = shift;
928 die "booksellerid is required to create a basketgroup" unless $basketgroupinfo->{'booksellerid'};
929 my $query = "INSERT INTO aqbasketgroups (";
930 my @params;
931 foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
932 if ( defined $basketgroupinfo->{$field} ) {
933 $query .= "$field, ";
934 push(@params, $basketgroupinfo->{$field});
937 $query .= "booksellerid) VALUES (";
938 foreach (@params) {
939 $query .= "?, ";
941 $query .= "?)";
942 push(@params, $basketgroupinfo->{'booksellerid'});
943 my $dbh = C4::Context->dbh;
944 my $sth = $dbh->prepare($query);
945 $sth->execute(@params);
946 my $basketgroupid = $dbh->{'mysql_insertid'};
947 if( $basketgroupinfo->{'basketlist'} ) {
948 foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
949 my $query2 = "UPDATE aqbasket SET basketgroupid=? WHERE basketno=?";
950 my $sth2 = $dbh->prepare($query2);
951 $sth2->execute($basketgroupid, $basketno);
954 return $basketgroupid;
957 #------------------------------------------------------------#
959 =head3 ModBasketgroup
961 ModBasketgroup(\%hashref);
963 Modifies a basketgroup in the aqbasketgroups table, and add the baskets to it.
965 $hashref->{'id'} is the 'id' field of the basketgroup in the aqbasketgroup table, this parameter is mandatory,
967 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
969 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
971 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
973 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
975 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
977 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
979 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
981 =cut
983 sub ModBasketgroup {
984 my $basketgroupinfo = shift;
985 die "basketgroup id is required to edit a basketgroup" unless $basketgroupinfo->{'id'};
986 my $dbh = C4::Context->dbh;
987 my $query = "UPDATE aqbasketgroups SET ";
988 my @params;
989 foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
990 if ( defined $basketgroupinfo->{$field} ) {
991 $query .= "$field=?, ";
992 push(@params, $basketgroupinfo->{$field});
995 chop($query);
996 chop($query);
997 $query .= " WHERE id=?";
998 push(@params, $basketgroupinfo->{'id'});
999 my $sth = $dbh->prepare($query);
1000 $sth->execute(@params);
1002 $sth = $dbh->prepare('UPDATE aqbasket SET basketgroupid = NULL WHERE basketgroupid = ?');
1003 $sth->execute($basketgroupinfo->{'id'});
1005 if($basketgroupinfo->{'basketlist'} && @{$basketgroupinfo->{'basketlist'}}){
1006 $sth = $dbh->prepare("UPDATE aqbasket SET basketgroupid=? WHERE basketno=?");
1007 foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
1008 $sth->execute($basketgroupinfo->{'id'}, $basketno);
1011 return;
1014 #------------------------------------------------------------#
1016 =head3 DelBasketgroup
1018 DelBasketgroup($basketgroupid);
1020 Deletes a basketgroup in the aqbasketgroups table, and removes the reference to it from the baskets,
1022 =over
1024 =item C<$basketgroupid> is the 'id' field of the basket in the aqbasketgroup table
1026 =back
1028 =cut
1030 sub DelBasketgroup {
1031 my $basketgroupid = shift;
1032 die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
1033 my $query = "DELETE FROM aqbasketgroups WHERE id=?";
1034 my $dbh = C4::Context->dbh;
1035 my $sth = $dbh->prepare($query);
1036 $sth->execute($basketgroupid);
1037 return;
1040 #------------------------------------------------------------#
1043 =head2 FUNCTIONS ABOUT ORDERS
1045 =head3 GetBasketgroup
1047 $basketgroup = &GetBasketgroup($basketgroupid);
1049 Returns a reference to the hash containing all information about the basketgroup.
1051 =cut
1053 sub GetBasketgroup {
1054 my $basketgroupid = shift;
1055 die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
1056 my $dbh = C4::Context->dbh;
1057 my $result_set = $dbh->selectall_arrayref(
1058 'SELECT * FROM aqbasketgroups WHERE id=?',
1059 { Slice => {} },
1060 $basketgroupid
1062 return $result_set->[0]; # id is unique
1065 #------------------------------------------------------------#
1067 =head3 GetBasketgroups
1069 $basketgroups = &GetBasketgroups($booksellerid);
1071 Returns a reference to the array of all the basketgroups of bookseller $booksellerid.
1073 =cut
1075 sub GetBasketgroups {
1076 my $booksellerid = shift;
1077 die 'bookseller id is required to edit a basketgroup' unless $booksellerid;
1078 my $query = 'SELECT * FROM aqbasketgroups WHERE booksellerid=? ORDER BY id DESC';
1079 my $dbh = C4::Context->dbh;
1080 my $sth = $dbh->prepare($query);
1081 $sth->execute($booksellerid);
1082 return $sth->fetchall_arrayref({});
1085 #------------------------------------------------------------#
1087 =head2 FUNCTIONS ABOUT ORDERS
1089 =head3 GetOrders
1091 @orders = &GetOrders( $basketno, { orderby => 'biblio.title', cancelled => 0|1 } );
1093 Looks up the pending (non-cancelled) orders with the given basket
1094 number.
1096 If cancelled is set, only cancelled orders will be returned.
1098 =cut
1100 sub GetOrders {
1101 my ( $basketno, $params ) = @_;
1103 return () unless $basketno;
1105 my $orderby = $params->{orderby};
1106 my $cancelled = $params->{cancelled} || 0;
1108 my $dbh = C4::Context->dbh;
1109 my $query = q|
1110 SELECT biblio.*,biblioitems.*,
1111 aqorders.*,
1112 aqbudgets.*,
1114 $query .= $cancelled
1115 ? q|
1116 aqorders_transfers.ordernumber_to AS transferred_to,
1117 aqorders_transfers.timestamp AS transferred_to_timestamp
1119 : q|
1120 aqorders_transfers.ordernumber_from AS transferred_from,
1121 aqorders_transfers.timestamp AS transferred_from_timestamp
1123 $query .= q|
1124 FROM aqorders
1125 LEFT JOIN aqbudgets ON aqbudgets.budget_id = aqorders.budget_id
1126 LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1127 LEFT JOIN biblioitems ON biblioitems.biblionumber =biblio.biblionumber
1129 $query .= $cancelled
1130 ? q|
1131 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_from = aqorders.ordernumber
1133 : q|
1134 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1137 $query .= q|
1138 WHERE basketno=?
1141 if ($cancelled) {
1142 $orderby ||= q|biblioitems.publishercode, biblio.title|;
1143 $query .= q|
1144 AND (datecancellationprinted IS NOT NULL
1145 AND datecancellationprinted <> '0000-00-00')
1148 else {
1149 $orderby ||=
1150 q|aqorders.datecancellationprinted desc, aqorders.timestamp desc|;
1151 $query .= q|
1152 AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')
1156 $query .= " ORDER BY $orderby";
1157 my $orders =
1158 $dbh->selectall_arrayref( $query, { Slice => {} }, $basketno );
1159 return @{$orders};
1163 #------------------------------------------------------------#
1165 =head3 GetOrdersByBiblionumber
1167 @orders = &GetOrdersByBiblionumber($biblionumber);
1169 Looks up the orders with linked to a specific $biblionumber, including
1170 cancelled orders and received orders.
1172 return :
1173 C<@orders> is an array of references-to-hash, whose keys are the
1174 fields from the aqorders, biblio, and biblioitems tables in the Koha database.
1176 =cut
1178 sub GetOrdersByBiblionumber {
1179 my $biblionumber = shift;
1180 return unless $biblionumber;
1181 my $dbh = C4::Context->dbh;
1182 my $query ="
1183 SELECT biblio.*,biblioitems.*,
1184 aqorders.*,
1185 aqbudgets.*
1186 FROM aqorders
1187 LEFT JOIN aqbudgets ON aqbudgets.budget_id = aqorders.budget_id
1188 LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1189 LEFT JOIN biblioitems ON biblioitems.biblionumber =biblio.biblionumber
1190 WHERE aqorders.biblionumber=?
1192 my $result_set =
1193 $dbh->selectall_arrayref( $query, { Slice => {} }, $biblionumber );
1194 return @{$result_set};
1198 #------------------------------------------------------------#
1200 =head3 GetOrder
1202 $order = &GetOrder($ordernumber);
1204 Looks up an order by order number.
1206 Returns a reference-to-hash describing the order. The keys of
1207 C<$order> are fields from the biblio, biblioitems, aqorders tables of the Koha database.
1209 =cut
1211 sub GetOrder {
1212 my ($ordernumber) = @_;
1213 return unless $ordernumber;
1215 my $dbh = C4::Context->dbh;
1216 my $query = qq{SELECT
1217 aqorders.*,
1218 biblio.title,
1219 biblio.author,
1220 aqbasket.basketname,
1221 borrowers.branchcode,
1222 biblioitems.publicationyear,
1223 biblio.copyrightdate,
1224 biblioitems.editionstatement,
1225 biblioitems.isbn,
1226 biblioitems.ean,
1227 biblio.seriestitle,
1228 biblioitems.publishercode,
1229 aqorders.rrp AS unitpricesupplier,
1230 aqorders.ecost AS unitpricelib,
1231 aqorders.claims_count AS claims_count,
1232 aqorders.claimed_date AS claimed_date,
1233 aqbudgets.budget_name AS budget,
1234 aqbooksellers.name AS supplier,
1235 aqbooksellers.id AS supplierid,
1236 biblioitems.publishercode AS publisher,
1237 ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
1238 DATE(aqbasket.closedate) AS orderdate,
1239 aqorders.quantity - COALESCE(aqorders.quantityreceived,0) AS quantity_to_receive,
1240 (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
1241 DATEDIFF(CURDATE( ),closedate) AS latesince
1242 FROM aqorders LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1243 LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
1244 LEFT JOIN aqbudgets ON aqorders.budget_id = aqbudgets.budget_id,
1245 aqbasket LEFT JOIN borrowers ON aqbasket.authorisedby = borrowers.borrowernumber
1246 LEFT JOIN aqbooksellers ON aqbasket.booksellerid = aqbooksellers.id
1247 WHERE aqorders.basketno = aqbasket.basketno
1248 AND ordernumber=?};
1249 my $result_set =
1250 $dbh->selectall_arrayref( $query, { Slice => {} }, $ordernumber );
1252 # result_set assumed to contain 1 match
1253 return $result_set->[0];
1256 =head3 GetLastOrderNotReceivedFromSubscriptionid
1258 $order = &GetLastOrderNotReceivedFromSubscriptionid($subscriptionid);
1260 Returns a reference-to-hash describing the last order not received for a subscription.
1262 =cut
1264 sub GetLastOrderNotReceivedFromSubscriptionid {
1265 my ( $subscriptionid ) = @_;
1266 my $dbh = C4::Context->dbh;
1267 my $query = qq|
1268 SELECT * FROM aqorders
1269 LEFT JOIN subscription
1270 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1271 WHERE aqorders.subscriptionid = ?
1272 AND aqorders.datereceived IS NULL
1273 LIMIT 1
1275 my $result_set =
1276 $dbh->selectall_arrayref( $query, { Slice => {} }, $subscriptionid );
1278 # result_set assumed to contain 1 match
1279 return $result_set->[0];
1282 =head3 GetLastOrderReceivedFromSubscriptionid
1284 $order = &GetLastOrderReceivedFromSubscriptionid($subscriptionid);
1286 Returns a reference-to-hash describing the last order received for a subscription.
1288 =cut
1290 sub GetLastOrderReceivedFromSubscriptionid {
1291 my ( $subscriptionid ) = @_;
1292 my $dbh = C4::Context->dbh;
1293 my $query = qq|
1294 SELECT * FROM aqorders
1295 LEFT JOIN subscription
1296 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1297 WHERE aqorders.subscriptionid = ?
1298 AND aqorders.datereceived =
1300 SELECT MAX( aqorders.datereceived )
1301 FROM aqorders
1302 LEFT JOIN subscription
1303 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1304 WHERE aqorders.subscriptionid = ?
1305 AND aqorders.datereceived IS NOT NULL
1307 ORDER BY ordernumber DESC
1308 LIMIT 1
1310 my $result_set =
1311 $dbh->selectall_arrayref( $query, { Slice => {} }, $subscriptionid, $subscriptionid );
1313 # result_set assumed to contain 1 match
1314 return $result_set->[0];
1318 #------------------------------------------------------------#
1320 =head3 ModOrder
1322 &ModOrder(\%hashref);
1324 Modifies an existing order. Updates the order with order number
1325 $hashref->{'ordernumber'} and biblionumber $hashref->{'biblionumber'}. All
1326 other keys of the hash update the fields with the same name in the aqorders
1327 table of the Koha database.
1329 =cut
1331 sub ModOrder {
1332 my $orderinfo = shift;
1334 die "Ordernumber is required" if $orderinfo->{'ordernumber'} eq '';
1336 my $dbh = C4::Context->dbh;
1337 my @params;
1339 # update uncertainprice to an integer, just in case (under FF, checked boxes have the value "ON" by default)
1340 $orderinfo->{uncertainprice}=1 if $orderinfo->{uncertainprice};
1342 # delete($orderinfo->{'branchcode'});
1343 # the hash contains a lot of entries not in aqorders, so get the columns ...
1344 my $sth = $dbh->prepare("SELECT * FROM aqorders LIMIT 1;");
1345 $sth->execute;
1346 my $colnames = $sth->{NAME};
1347 #FIXME Be careful. If aqorders would have columns with diacritics,
1348 #you should need to decode what you get back from NAME.
1349 #See report 10110 and guided_reports.pl
1350 my $query = "UPDATE aqorders SET ";
1352 foreach my $orderinfokey (grep(!/ordernumber/, keys %$orderinfo)){
1353 # ... and skip hash entries that are not in the aqorders table
1354 # FIXME : probably not the best way to do it (would be better to have a correct hash)
1355 next unless grep(/^$orderinfokey$/, @$colnames);
1356 $query .= "$orderinfokey=?, ";
1357 push(@params, $orderinfo->{$orderinfokey});
1360 $query .= "timestamp=NOW() WHERE ordernumber=?";
1361 push(@params, $orderinfo->{'ordernumber'} );
1362 $sth = $dbh->prepare($query);
1363 $sth->execute(@params);
1364 return;
1367 #------------------------------------------------------------#
1369 =head3 ModItemOrder
1371 ModItemOrder($itemnumber, $ordernumber);
1373 Modifies the ordernumber of an item in aqorders_items.
1375 =cut
1377 sub ModItemOrder {
1378 my ($itemnumber, $ordernumber) = @_;
1380 return unless ($itemnumber and $ordernumber);
1382 my $dbh = C4::Context->dbh;
1383 my $query = qq{
1384 UPDATE aqorders_items
1385 SET ordernumber = ?
1386 WHERE itemnumber = ?
1388 my $sth = $dbh->prepare($query);
1389 return $sth->execute($ordernumber, $itemnumber);
1392 #------------------------------------------------------------#
1394 =head3 ModReceiveOrder
1396 my ( $date_received, $new_ordernumber ) = ModReceiveOrder(
1398 biblionumber => $biblionumber,
1399 order => $order,
1400 quantityreceived => $quantityreceived,
1401 user => $user,
1402 invoice => $invoice,
1403 budget_id => $budget_id,
1404 received_itemnumbers => \@received_itemnumbers,
1405 order_internalnote => $order_internalnote,
1409 Updates an order, to reflect the fact that it was received, at least
1410 in part.
1412 If a partial order is received, splits the order into two.
1414 Updates the order with biblionumber C<$biblionumber> and ordernumber
1415 C<$order->{ordernumber}>.
1417 =cut
1420 sub ModReceiveOrder {
1421 my ($params) = @_;
1422 my $biblionumber = $params->{biblionumber};
1423 my $order = { %{ $params->{order} } }; # Copy the order, we don't want to modify it
1424 my $invoice = $params->{invoice};
1425 my $quantrec = $params->{quantityreceived};
1426 my $user = $params->{user};
1427 my $budget_id = $params->{budget_id};
1428 my $received_items = $params->{received_items};
1430 my $dbh = C4::Context->dbh;
1431 my $datereceived = ( $invoice and $invoice->{datereceived} ) ? $invoice->{datereceived} : dt_from_string;
1432 my $suggestionid = GetSuggestionFromBiblionumber( $biblionumber );
1433 if ($suggestionid) {
1434 ModSuggestion( {suggestionid=>$suggestionid,
1435 STATUS=>'AVAILABLE',
1436 biblionumber=> $biblionumber}
1440 my $result_set = $dbh->selectrow_arrayref(
1441 q{SELECT aqbasket.is_standing
1442 FROM aqbasket
1443 WHERE basketno=?},{ Slice => {} }, $order->{basketno});
1444 my $is_standing = $result_set->[0]; # we assume we have a unique basket
1446 my $new_ordernumber = $order->{ordernumber};
1447 if ( $is_standing || $order->{quantity} > $quantrec ) {
1448 # Split order line in two parts: the first is the original order line
1449 # without received items (the quantity is decreased),
1450 # the second part is a new order line with quantity=quantityrec
1451 # (entirely received)
1452 my $query = q|
1453 UPDATE aqorders
1454 SET quantity = ?,
1455 orderstatus = 'partial'|;
1456 $query .= q|, order_internalnote = ?| if defined $order->{order_internalnote};
1457 $query .= q| WHERE ordernumber = ?|;
1458 my $sth = $dbh->prepare($query);
1460 $sth->execute(
1461 ( $is_standing ? 1 : ($order->{quantity} - $quantrec) ),
1462 ( defined $order->{order_internalnote} ? $order->{order_internalnote} : () ),
1463 $order->{ordernumber}
1466 # Recalculate tax_value
1467 $dbh->do(q|
1468 UPDATE aqorders
1470 tax_value_on_ordering = quantity * ecost_tax_excluded * tax_rate_on_ordering,
1471 tax_value_on_receiving = quantity * unitprice_tax_excluded * tax_rate_on_receiving
1472 WHERE ordernumber = ?
1473 |, undef, $order->{ordernumber});
1475 delete $order->{ordernumber};
1476 $order->{budget_id} = ( $budget_id || $order->{budget_id} );
1477 $order->{quantity} = $quantrec;
1478 $order->{quantityreceived} = $quantrec;
1479 $order->{ecost_tax_excluded} //= 0;
1480 $order->{tax_rate_on_ordering} //= 0;
1481 $order->{unitprice_tax_excluded} //= 0;
1482 $order->{tax_rate_on_receiving} //= 0;
1483 $order->{tax_value_on_ordering} = $order->{quantity} * $order->{ecost_tax_excluded} * $order->{tax_rate_on_ordering};
1484 $order->{tax_value_on_receiving} = $order->{quantity} * $order->{unitprice_tax_excluded} * $order->{tax_rate_on_receiving};
1485 $order->{datereceived} = $datereceived;
1486 $order->{invoiceid} = $invoice->{invoiceid};
1487 $order->{orderstatus} = 'complete';
1488 $new_ordernumber = Koha::Acquisition::Order->new($order)->insert->{ordernumber};
1490 if ($received_items) {
1491 foreach my $itemnumber (@$received_items) {
1492 ModItemOrder($itemnumber, $new_ordernumber);
1495 } else {
1496 my $query = q|
1497 UPDATE aqorders
1498 SET quantityreceived = ?,
1499 datereceived = ?,
1500 invoiceid = ?,
1501 budget_id = ?,
1502 orderstatus = 'complete'
1505 $query .= q|
1506 , unitprice = ?, unitprice_tax_included = ?, unitprice_tax_excluded = ?
1507 | if defined $order->{unitprice};
1509 $query .= q|
1510 ,tax_value_on_receiving = ?
1511 | if defined $order->{tax_value_on_receiving};
1513 $query .= q|
1514 ,tax_rate_on_receiving = ?
1515 | if defined $order->{tax_rate_on_receiving};
1517 $query .= q|
1518 , order_internalnote = ?
1519 | if defined $order->{order_internalnote};
1521 $query .= q| where biblionumber=? and ordernumber=?|;
1523 my $sth = $dbh->prepare( $query );
1524 my @params = ( $quantrec, $datereceived, $invoice->{invoiceid}, ( $budget_id ? $budget_id : $order->{budget_id} ) );
1526 if ( defined $order->{unitprice} ) {
1527 push @params, $order->{unitprice}, $order->{unitprice_tax_included}, $order->{unitprice_tax_excluded};
1530 if ( defined $order->{tax_value_on_receiving} ) {
1531 push @params, $order->{tax_value_on_receiving};
1534 if ( defined $order->{tax_rate_on_receiving} ) {
1535 push @params, $order->{tax_rate_on_receiving};
1538 if ( defined $order->{order_internalnote} ) {
1539 push @params, $order->{order_internalnote};
1542 push @params, ( $biblionumber, $order->{ordernumber} );
1544 $sth->execute( @params );
1546 # All items have been received, sent a notification to users
1547 NotifyOrderUsers( $order->{ordernumber} );
1550 return ($datereceived, $new_ordernumber);
1553 =head3 CancelReceipt
1555 my $parent_ordernumber = CancelReceipt($ordernumber);
1557 Cancel an order line receipt and update the parent order line, as if no
1558 receipt was made.
1559 If items are created at receipt (AcqCreateItem = receiving) then delete
1560 these items.
1562 =cut
1564 sub CancelReceipt {
1565 my $ordernumber = shift;
1567 return unless $ordernumber;
1569 my $dbh = C4::Context->dbh;
1570 my $query = qq{
1571 SELECT datereceived, parent_ordernumber, quantity
1572 FROM aqorders
1573 WHERE ordernumber = ?
1575 my $sth = $dbh->prepare($query);
1576 $sth->execute($ordernumber);
1577 my $order = $sth->fetchrow_hashref;
1578 unless($order) {
1579 warn "CancelReceipt: order $ordernumber does not exist";
1580 return;
1582 unless($order->{'datereceived'}) {
1583 warn "CancelReceipt: order $ordernumber is not received";
1584 return;
1587 my $parent_ordernumber = $order->{'parent_ordernumber'};
1589 my @itemnumbers = GetItemnumbersFromOrder( $ordernumber );
1591 if($parent_ordernumber == $ordernumber || not $parent_ordernumber) {
1592 # The order line has no parent, just mark it as not received
1593 $query = qq{
1594 UPDATE aqorders
1595 SET quantityreceived = ?,
1596 datereceived = ?,
1597 invoiceid = ?,
1598 orderstatus = 'ordered'
1599 WHERE ordernumber = ?
1601 $sth = $dbh->prepare($query);
1602 $sth->execute(0, undef, undef, $ordernumber);
1603 _cancel_items_receipt( $ordernumber );
1604 } else {
1605 # The order line has a parent, increase parent quantity and delete
1606 # the order line.
1607 $query = qq{
1608 SELECT quantity, datereceived
1609 FROM aqorders
1610 WHERE ordernumber = ?
1612 $sth = $dbh->prepare($query);
1613 $sth->execute($parent_ordernumber);
1614 my $parent_order = $sth->fetchrow_hashref;
1615 unless($parent_order) {
1616 warn "Parent order $parent_ordernumber does not exist.";
1617 return;
1619 if($parent_order->{'datereceived'}) {
1620 warn "CancelReceipt: parent order is received.".
1621 " Can't cancel receipt.";
1622 return;
1624 $query = qq{
1625 UPDATE aqorders
1626 SET quantity = ?,
1627 orderstatus = 'ordered'
1628 WHERE ordernumber = ?
1630 $sth = $dbh->prepare($query);
1631 my $rv = $sth->execute(
1632 $order->{'quantity'} + $parent_order->{'quantity'},
1633 $parent_ordernumber
1635 unless($rv) {
1636 warn "Cannot update parent order line, so do not cancel".
1637 " receipt";
1638 return;
1641 # Recalculate tax_value
1642 $dbh->do(q|
1643 UPDATE aqorders
1645 tax_value_on_ordering = quantity * ecost_tax_excluded * tax_rate_on_ordering,
1646 tax_value_on_receiving = quantity * unitprice_tax_excluded * tax_rate_on_receiving
1647 WHERE ordernumber = ?
1648 |, undef, $parent_ordernumber);
1650 _cancel_items_receipt( $ordernumber, $parent_ordernumber );
1651 # Delete order line
1652 $query = qq{
1653 DELETE FROM aqorders
1654 WHERE ordernumber = ?
1656 $sth = $dbh->prepare($query);
1657 $sth->execute($ordernumber);
1661 if(C4::Context->preference('AcqCreateItem') eq 'ordering') {
1662 my @affects = split q{\|}, C4::Context->preference("AcqItemSetSubfieldsWhenReceiptIsCancelled");
1663 if ( @affects ) {
1664 for my $in ( @itemnumbers ) {
1665 my $item = Koha::Items->find( $in );
1666 my $biblio = $item->biblio;
1667 my ( $itemfield ) = GetMarcFromKohaField( 'items.itemnumber', $biblio->frameworkcode );
1668 my $item_marc = C4::Items::GetMarcItem( $biblio->biblionumber, $in );
1669 for my $affect ( @affects ) {
1670 my ( $sf, $v ) = split q{=}, $affect, 2;
1671 foreach ( $item_marc->field($itemfield) ) {
1672 $_->update( $sf => $v );
1675 C4::Items::ModItemFromMarc( $item_marc, $biblio->biblionumber, $in );
1680 return $parent_ordernumber;
1683 sub _cancel_items_receipt {
1684 my ( $ordernumber, $parent_ordernumber ) = @_;
1685 $parent_ordernumber ||= $ordernumber;
1687 my @itemnumbers = GetItemnumbersFromOrder($ordernumber);
1688 if(C4::Context->preference('AcqCreateItem') eq 'receiving') {
1689 # Remove items that were created at receipt
1690 my $query = qq{
1691 DELETE FROM items, aqorders_items
1692 USING items, aqorders_items
1693 WHERE items.itemnumber = ? AND aqorders_items.itemnumber = ?
1695 my $dbh = C4::Context->dbh;
1696 my $sth = $dbh->prepare($query);
1697 foreach my $itemnumber (@itemnumbers) {
1698 $sth->execute($itemnumber, $itemnumber);
1700 } else {
1701 # Update items
1702 foreach my $itemnumber (@itemnumbers) {
1703 ModItemOrder($itemnumber, $parent_ordernumber);
1708 #------------------------------------------------------------#
1710 =head3 SearchOrders
1712 @results = &SearchOrders({
1713 ordernumber => $ordernumber,
1714 search => $search,
1715 ean => $ean,
1716 booksellerid => $booksellerid,
1717 basketno => $basketno,
1718 basketname => $basketname,
1719 basketgroupname => $basketgroupname,
1720 owner => $owner,
1721 pending => $pending
1722 ordered => $ordered
1723 biblionumber => $biblionumber,
1724 budget_id => $budget_id
1727 Searches for orders filtered by criteria.
1729 C<$ordernumber> Finds matching orders or transferred orders by ordernumber.
1730 C<$search> Finds orders matching %$search% in title, author, or isbn.
1731 C<$owner> Finds order for the logged in user.
1732 C<$pending> Finds pending orders. Ignores completed and cancelled orders.
1733 C<$ordered> Finds orders to receive only (status 'ordered' or 'partial').
1736 C<@results> is an array of references-to-hash with the keys are fields
1737 from aqorders, biblio, biblioitems and aqbasket tables.
1739 =cut
1741 sub SearchOrders {
1742 my ( $params ) = @_;
1743 my $ordernumber = $params->{ordernumber};
1744 my $search = $params->{search};
1745 my $ean = $params->{ean};
1746 my $booksellerid = $params->{booksellerid};
1747 my $basketno = $params->{basketno};
1748 my $basketname = $params->{basketname};
1749 my $basketgroupname = $params->{basketgroupname};
1750 my $owner = $params->{owner};
1751 my $pending = $params->{pending};
1752 my $ordered = $params->{ordered};
1753 my $biblionumber = $params->{biblionumber};
1754 my $budget_id = $params->{budget_id};
1756 my $dbh = C4::Context->dbh;
1757 my @args = ();
1758 my $query = q{
1759 SELECT aqbasket.basketno,
1760 borrowers.surname,
1761 borrowers.firstname,
1762 biblio.*,
1763 biblioitems.isbn,
1764 biblioitems.biblioitemnumber,
1765 biblioitems.publishercode,
1766 biblioitems.publicationyear,
1767 aqbasket.authorisedby,
1768 aqbasket.booksellerid,
1769 aqbasket.closedate,
1770 aqbasket.creationdate,
1771 aqbasket.basketname,
1772 aqbasketgroups.id as basketgroupid,
1773 aqbasketgroups.name as basketgroupname,
1774 aqorders.*
1775 FROM aqorders
1776 LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
1777 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
1778 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
1779 LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
1780 LEFT JOIN biblioitems ON biblioitems.biblionumber=biblio.biblionumber
1783 # If we search on ordernumber, we retrieve the transferred order if a transfer has been done.
1784 $query .= q{
1785 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1786 } if $ordernumber;
1788 $query .= q{
1789 WHERE (datecancellationprinted is NULL)
1792 if ( $pending or $ordered ) {
1793 $query .= q{
1794 AND (
1795 ( aqbasket.is_standing AND aqorders.orderstatus IN ( "new", "ordered", "partial" ) )
1796 OR (
1797 ( quantity > quantityreceived OR quantityreceived is NULL )
1800 if ( $ordered ) {
1801 $query .= q{ AND aqorders.orderstatus IN ( "ordered", "partial" )};
1803 $query .= q{
1809 my $userenv = C4::Context->userenv;
1810 if ( C4::Context->preference("IndependentBranches") ) {
1811 unless ( C4::Context->IsSuperLibrarian() ) {
1812 $query .= q{
1813 AND (
1814 borrowers.branchcode = ?
1815 OR borrowers.branchcode = ''
1818 push @args, $userenv->{branch};
1822 if ( $ordernumber ) {
1823 $query .= ' AND ( aqorders.ordernumber = ? OR aqorders_transfers.ordernumber_from = ? ) ';
1824 push @args, ( $ordernumber, $ordernumber );
1826 if ( $biblionumber ) {
1827 $query .= 'AND aqorders.biblionumber = ?';
1828 push @args, $biblionumber;
1830 if( $search ) {
1831 $query .= ' AND (biblio.title LIKE ? OR biblio.author LIKE ? OR biblioitems.isbn LIKE ?)';
1832 push @args, ("%$search%","%$search%","%$search%");
1834 if ( $ean ) {
1835 $query .= ' AND biblioitems.ean = ?';
1836 push @args, $ean;
1838 if ( $booksellerid ) {
1839 $query .= 'AND aqbasket.booksellerid = ?';
1840 push @args, $booksellerid;
1842 if( $basketno ) {
1843 $query .= 'AND aqbasket.basketno = ?';
1844 push @args, $basketno;
1846 if( $basketname ) {
1847 $query .= 'AND aqbasket.basketname LIKE ?';
1848 push @args, "%$basketname%";
1850 if( $basketgroupname ) {
1851 $query .= ' AND aqbasketgroups.name LIKE ?';
1852 push @args, "%$basketgroupname%";
1855 if ( $owner ) {
1856 $query .= ' AND aqbasket.authorisedby=? ';
1857 push @args, $userenv->{'number'};
1860 if ( $budget_id ) {
1861 $query .= ' AND aqorders.budget_id = ?';
1862 push @args, $budget_id;
1865 $query .= ' ORDER BY aqbasket.basketno';
1867 my $sth = $dbh->prepare($query);
1868 $sth->execute(@args);
1869 return $sth->fetchall_arrayref({});
1872 #------------------------------------------------------------#
1874 =head3 DelOrder
1876 &DelOrder($biblionumber, $ordernumber);
1878 Cancel the order with the given order and biblio numbers. It does not
1879 delete any entries in the aqorders table, it merely marks them as
1880 cancelled.
1882 =cut
1884 sub DelOrder {
1885 my ( $bibnum, $ordernumber, $delete_biblio, $reason ) = @_;
1887 my $error;
1888 my $dbh = C4::Context->dbh;
1889 my $query = "
1890 UPDATE aqorders
1891 SET datecancellationprinted=now(), orderstatus='cancelled'
1893 if($reason) {
1894 $query .= ", cancellationreason = ? ";
1896 $query .= "
1897 WHERE biblionumber=? AND ordernumber=?
1899 my $sth = $dbh->prepare($query);
1900 if($reason) {
1901 $sth->execute($reason, $bibnum, $ordernumber);
1902 } else {
1903 $sth->execute( $bibnum, $ordernumber );
1905 $sth->finish;
1907 my @itemnumbers = GetItemnumbersFromOrder( $ordernumber );
1908 foreach my $itemnumber (@itemnumbers){
1909 my $delcheck = C4::Items::DelItemCheck( $bibnum, $itemnumber );
1911 if($delcheck != 1) {
1912 $error->{'delitem'} = 1;
1916 if($delete_biblio) {
1917 # We get the number of remaining items
1918 my $biblio = Koha::Biblios->find( $bibnum );
1919 my $itemcount = $biblio->items->count;
1921 # If there are no items left,
1922 if ( $itemcount == 0 ) {
1923 # We delete the record
1924 my $delcheck = DelBiblio($bibnum);
1926 if($delcheck) {
1927 $error->{'delbiblio'} = 1;
1932 return $error;
1935 =head3 TransferOrder
1937 my $newordernumber = TransferOrder($ordernumber, $basketno);
1939 Transfer an order line to a basket.
1940 Mark $ordernumber as cancelled with an internal note 'Cancelled and transferred
1941 to BOOKSELLER on DATE' and create new order with internal note
1942 'Transferred from BOOKSELLER on DATE'.
1943 Move all attached items to the new order.
1944 Received orders cannot be transferred.
1945 Return the ordernumber of created order.
1947 =cut
1949 sub TransferOrder {
1950 my ($ordernumber, $basketno) = @_;
1952 return unless ($ordernumber and $basketno);
1954 my $order = GetOrder( $ordernumber );
1955 return if $order->{datereceived};
1956 my $basket = GetBasket($basketno);
1957 return unless $basket;
1959 my $dbh = C4::Context->dbh;
1960 my ($query, $sth, $rv);
1962 $query = q{
1963 UPDATE aqorders
1964 SET datecancellationprinted = CAST(NOW() AS date), orderstatus = ?
1965 WHERE ordernumber = ?
1967 $sth = $dbh->prepare($query);
1968 $rv = $sth->execute('cancelled', $ordernumber);
1970 delete $order->{'ordernumber'};
1971 delete $order->{parent_ordernumber};
1972 $order->{'basketno'} = $basketno;
1974 my $newordernumber = Koha::Acquisition::Order->new($order)->insert->{ordernumber};
1976 $query = q{
1977 UPDATE aqorders_items
1978 SET ordernumber = ?
1979 WHERE ordernumber = ?
1981 $sth = $dbh->prepare($query);
1982 $sth->execute($newordernumber, $ordernumber);
1984 $query = q{
1985 INSERT INTO aqorders_transfers (ordernumber_from, ordernumber_to)
1986 VALUES (?, ?)
1988 $sth = $dbh->prepare($query);
1989 $sth->execute($ordernumber, $newordernumber);
1991 return $newordernumber;
1994 =head2 FUNCTIONS ABOUT PARCELS
1996 =head3 GetParcels
1998 $results = &GetParcels($bookseller, $order, $code, $datefrom, $dateto);
2000 get a lists of parcels.
2002 * Input arg :
2004 =over
2006 =item $bookseller
2007 is the bookseller this function has to get parcels.
2009 =item $order
2010 To know on what criteria the results list has to be ordered.
2012 =item $code
2013 is the booksellerinvoicenumber.
2015 =item $datefrom & $dateto
2016 to know on what date this function has to filter its search.
2018 =back
2020 * return:
2021 a pointer on a hash list containing parcel informations as such :
2023 =over
2025 =item Creation date
2027 =item Last operation
2029 =item Number of biblio
2031 =item Number of items
2033 =back
2035 =cut
2037 sub GetParcels {
2038 my ($bookseller,$order, $code, $datefrom, $dateto) = @_;
2039 my $dbh = C4::Context->dbh;
2040 my @query_params = ();
2041 my $strsth ="
2042 SELECT aqinvoices.invoicenumber,
2043 datereceived,purchaseordernumber,
2044 count(DISTINCT biblionumber) AS biblio,
2045 sum(quantity) AS itemsexpected,
2046 sum(quantityreceived) AS itemsreceived
2047 FROM aqorders LEFT JOIN aqbasket ON aqbasket.basketno = aqorders.basketno
2048 LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
2049 WHERE aqbasket.booksellerid = ? and datereceived IS NOT NULL
2051 push @query_params, $bookseller;
2053 if ( defined $code ) {
2054 $strsth .= ' and aqinvoices.invoicenumber like ? ';
2055 # add a % to the end of the code to allow stemming.
2056 push @query_params, "$code%";
2059 if ( defined $datefrom ) {
2060 $strsth .= ' and datereceived >= ? ';
2061 push @query_params, $datefrom;
2064 if ( defined $dateto ) {
2065 $strsth .= 'and datereceived <= ? ';
2066 push @query_params, $dateto;
2069 $strsth .= "group by aqinvoices.invoicenumber,datereceived ";
2071 # can't use a placeholder to place this column name.
2072 # but, we could probably be checking to make sure it is a column that will be fetched.
2073 $strsth .= "order by $order " if ($order);
2075 my $sth = $dbh->prepare($strsth);
2077 $sth->execute( @query_params );
2078 my $results = $sth->fetchall_arrayref({});
2079 return @{$results};
2082 #------------------------------------------------------------#
2084 =head3 GetLateOrders
2086 @results = &GetLateOrders;
2088 Searches for bookseller with late orders.
2090 return:
2091 the table of supplier with late issues. This table is full of hashref.
2093 =cut
2095 sub GetLateOrders {
2096 my $delay = shift;
2097 my $supplierid = shift;
2098 my $branch = shift;
2099 my $estimateddeliverydatefrom = shift;
2100 my $estimateddeliverydateto = shift;
2102 my $dbh = C4::Context->dbh;
2104 #BEWARE, order of parenthesis and LEFT JOIN is important for speed
2105 my $dbdriver = C4::Context->config("db_scheme") || "mysql";
2107 my @query_params = ();
2108 my $select = "
2109 SELECT aqbasket.basketno,
2110 aqorders.ordernumber,
2111 DATE(aqbasket.closedate) AS orderdate,
2112 aqbasket.basketname AS basketname,
2113 aqbasket.basketgroupid AS basketgroupid,
2114 aqbasketgroups.name AS basketgroupname,
2115 aqorders.rrp AS unitpricesupplier,
2116 aqorders.ecost AS unitpricelib,
2117 aqorders.claims_count AS claims_count,
2118 aqorders.claimed_date AS claimed_date,
2119 aqbudgets.budget_name AS budget,
2120 borrowers.branchcode AS branch,
2121 aqbooksellers.name AS supplier,
2122 aqbooksellers.id AS supplierid,
2123 biblio.author, biblio.title,
2124 biblioitems.publishercode AS publisher,
2125 biblioitems.publicationyear,
2126 ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
2128 my $from = "
2129 FROM
2130 aqorders LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
2131 LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
2132 LEFT JOIN aqbudgets ON aqorders.budget_id = aqbudgets.budget_id,
2133 aqbasket LEFT JOIN borrowers ON aqbasket.authorisedby = borrowers.borrowernumber
2134 LEFT JOIN aqbooksellers ON aqbasket.booksellerid = aqbooksellers.id
2135 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
2136 WHERE aqorders.basketno = aqbasket.basketno
2137 AND ( datereceived = ''
2138 OR datereceived IS NULL
2139 OR aqorders.quantityreceived < aqorders.quantity
2141 AND aqbasket.closedate IS NOT NULL
2142 AND (aqorders.datecancellationprinted IS NULL OR aqorders.datecancellationprinted='0000-00-00')
2144 my $having = "";
2145 if ($dbdriver eq "mysql") {
2146 $select .= "
2147 aqorders.quantity - COALESCE(aqorders.quantityreceived,0) AS quantity,
2148 (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
2149 DATEDIFF(CAST(now() AS date),closedate) AS latesince
2151 if ( defined $delay ) {
2152 $from .= " AND (closedate <= DATE_SUB(CAST(now() AS date),INTERVAL ? DAY)) " ;
2153 push @query_params, $delay;
2155 $having = "HAVING quantity <> 0";
2156 } else {
2157 # FIXME: account for IFNULL as above
2158 $select .= "
2159 aqorders.quantity AS quantity,
2160 aqorders.quantity * aqorders.rrp AS subtotal,
2161 (CAST(now() AS date) - closedate) AS latesince
2163 if ( defined $delay ) {
2164 $from .= " AND (closedate <= (CAST(now() AS date) -(INTERVAL ? DAY)) ";
2165 push @query_params, $delay;
2168 if (defined $supplierid) {
2169 $from .= ' AND aqbasket.booksellerid = ? ';
2170 push @query_params, $supplierid;
2172 if (defined $branch) {
2173 $from .= ' AND borrowers.branchcode LIKE ? ';
2174 push @query_params, $branch;
2177 if ( defined $estimateddeliverydatefrom or defined $estimateddeliverydateto ) {
2178 $from .= ' AND aqbooksellers.deliverytime IS NOT NULL ';
2180 if ( defined $estimateddeliverydatefrom ) {
2181 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) >= ?';
2182 push @query_params, $estimateddeliverydatefrom;
2184 if ( defined $estimateddeliverydateto ) {
2185 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) <= ?';
2186 push @query_params, $estimateddeliverydateto;
2188 if ( defined $estimateddeliverydatefrom and not defined $estimateddeliverydateto ) {
2189 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) <= CAST(now() AS date)';
2191 if (C4::Context->preference("IndependentBranches")
2192 && !C4::Context->IsSuperLibrarian() ) {
2193 $from .= ' AND borrowers.branchcode LIKE ? ';
2194 push @query_params, C4::Context->userenv->{branch};
2196 $from .= " AND orderstatus <> 'cancelled' ";
2197 my $query = "$select $from $having\nORDER BY latesince, basketno, borrowers.branchcode, supplier";
2198 $debug and print STDERR "GetLateOrders query: $query\nGetLateOrders args: " . join(" ",@query_params);
2199 my $sth = $dbh->prepare($query);
2200 $sth->execute(@query_params);
2201 my @results;
2202 while (my $data = $sth->fetchrow_hashref) {
2203 push @results, $data;
2205 return @results;
2208 #------------------------------------------------------------#
2210 =head3 GetHistory
2212 \@order_loop = GetHistory( %params );
2214 Retreives some acquisition history information
2216 params:
2217 title
2218 author
2219 name
2220 isbn
2222 from_placed_on
2223 to_placed_on
2224 basket - search both basket name and number
2225 booksellerinvoicenumber
2226 basketgroupname
2227 budget
2228 orderstatus (note that orderstatus '' will retrieve orders
2229 of any status except cancelled)
2230 biblionumber
2231 get_canceled_order (if set to a true value, cancelled orders will
2232 be included)
2234 returns:
2235 $order_loop is a list of hashrefs that each look like this:
2237 'author' => 'Twain, Mark',
2238 'basketno' => '1',
2239 'biblionumber' => '215',
2240 'count' => 1,
2241 'creationdate' => 'MM/DD/YYYY',
2242 'datereceived' => undef,
2243 'ecost' => '1.00',
2244 'id' => '1',
2245 'invoicenumber' => undef,
2246 'name' => '',
2247 'ordernumber' => '1',
2248 'quantity' => 1,
2249 'quantityreceived' => undef,
2250 'title' => 'The Adventures of Huckleberry Finn'
2253 =cut
2255 sub GetHistory {
2256 # don't run the query if there are no parameters (list would be too long for sure !)
2257 croak "No search params" unless @_;
2258 my %params = @_;
2259 my $title = $params{title};
2260 my $author = $params{author};
2261 my $isbn = $params{isbn};
2262 my $ean = $params{ean};
2263 my $name = $params{name};
2264 my $from_placed_on = $params{from_placed_on};
2265 my $to_placed_on = $params{to_placed_on};
2266 my $basket = $params{basket};
2267 my $booksellerinvoicenumber = $params{booksellerinvoicenumber};
2268 my $basketgroupname = $params{basketgroupname};
2269 my $budget = $params{budget};
2270 my $orderstatus = $params{orderstatus};
2271 my $biblionumber = $params{biblionumber};
2272 my $get_canceled_order = $params{get_canceled_order} || 0;
2273 my $ordernumber = $params{ordernumber};
2274 my $search_children_too = $params{search_children_too} || 0;
2275 my $created_by = $params{created_by} || [];
2277 my @order_loop;
2278 my $total_qty = 0;
2279 my $total_qtyreceived = 0;
2280 my $total_price = 0;
2282 my $dbh = C4::Context->dbh;
2283 my $query ="
2284 SELECT
2285 COALESCE(biblio.title, deletedbiblio.title) AS title,
2286 COALESCE(biblio.author, deletedbiblio.author) AS author,
2287 COALESCE(biblioitems.isbn, deletedbiblioitems.isbn) AS isbn,
2288 COALESCE(biblioitems.ean, deletedbiblioitems.ean) AS ean,
2289 aqorders.basketno,
2290 aqbasket.basketname,
2291 aqbasket.basketgroupid,
2292 aqbasket.authorisedby,
2293 concat( borrowers.firstname,' ',borrowers.surname) AS authorisedbyname,
2294 aqbasketgroups.name as groupname,
2295 aqbooksellers.name,
2296 aqbasket.creationdate,
2297 aqorders.datereceived,
2298 aqorders.quantity,
2299 aqorders.quantityreceived,
2300 aqorders.ecost,
2301 aqorders.ordernumber,
2302 aqorders.invoiceid,
2303 aqinvoices.invoicenumber,
2304 aqbooksellers.id as id,
2305 aqorders.biblionumber,
2306 aqorders.orderstatus,
2307 aqorders.parent_ordernumber,
2308 aqbudgets.budget_name
2310 $query .= ", aqbudgets.budget_id AS budget" if defined $budget;
2311 $query .= "
2312 FROM aqorders
2313 LEFT JOIN aqbasket ON aqorders.basketno=aqbasket.basketno
2314 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid=aqbasketgroups.id
2315 LEFT JOIN aqbooksellers ON aqbasket.booksellerid=aqbooksellers.id
2316 LEFT JOIN biblioitems ON biblioitems.biblionumber=aqorders.biblionumber
2317 LEFT JOIN biblio ON biblio.biblionumber=aqorders.biblionumber
2318 LEFT JOIN aqbudgets ON aqorders.budget_id=aqbudgets.budget_id
2319 LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
2320 LEFT JOIN deletedbiblio ON deletedbiblio.biblionumber=aqorders.biblionumber
2321 LEFT JOIN deletedbiblioitems ON deletedbiblioitems.biblionumber=aqorders.biblionumber
2322 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
2325 $query .= " WHERE 1 ";
2327 unless ($get_canceled_order or (defined $orderstatus and $orderstatus eq 'cancelled')) {
2328 $query .= " AND (datecancellationprinted is NULL or datecancellationprinted='0000-00-00') ";
2331 my @query_params = ();
2333 if ( $biblionumber ) {
2334 $query .= " AND biblio.biblionumber = ?";
2335 push @query_params, $biblionumber;
2338 if ( $title ) {
2339 $query .= " AND biblio.title LIKE ? ";
2340 $title =~ s/\s+/%/g;
2341 push @query_params, "%$title%";
2344 if ( $author ) {
2345 $query .= " AND biblio.author LIKE ? ";
2346 push @query_params, "%$author%";
2349 if ( $isbn ) {
2350 $query .= " AND biblioitems.isbn LIKE ? ";
2351 push @query_params, "%$isbn%";
2353 if ( $ean ) {
2354 $query .= " AND biblioitems.ean = ? ";
2355 push @query_params, "$ean";
2357 if ( $name ) {
2358 $query .= " AND aqbooksellers.name LIKE ? ";
2359 push @query_params, "%$name%";
2362 if ( $budget ) {
2363 $query .= " AND aqbudgets.budget_id = ? ";
2364 push @query_params, "$budget";
2367 if ( $from_placed_on ) {
2368 $query .= " AND creationdate >= ? ";
2369 push @query_params, $from_placed_on;
2372 if ( $to_placed_on ) {
2373 $query .= " AND creationdate <= ? ";
2374 push @query_params, $to_placed_on;
2377 if ( defined $orderstatus and $orderstatus ne '') {
2378 $query .= " AND aqorders.orderstatus = ? ";
2379 push @query_params, "$orderstatus";
2382 if ($basket) {
2383 if ($basket =~ m/^\d+$/) {
2384 $query .= " AND aqorders.basketno = ? ";
2385 push @query_params, $basket;
2386 } else {
2387 $query .= " AND aqbasket.basketname LIKE ? ";
2388 push @query_params, "%$basket%";
2392 if ($booksellerinvoicenumber) {
2393 $query .= " AND aqinvoices.invoicenumber LIKE ? ";
2394 push @query_params, "%$booksellerinvoicenumber%";
2397 if ($basketgroupname) {
2398 $query .= " AND aqbasketgroups.name LIKE ? ";
2399 push @query_params, "%$basketgroupname%";
2402 if ($ordernumber) {
2403 $query .= " AND (aqorders.ordernumber = ? ";
2404 push @query_params, $ordernumber;
2405 if ($search_children_too) {
2406 $query .= " OR aqorders.parent_ordernumber = ? ";
2407 push @query_params, $ordernumber;
2409 $query .= ") ";
2412 if ( @$created_by ) {
2413 $query .= ' AND aqbasket.authorisedby IN ( ' . join( ',', ('?') x @$created_by ) . ')';
2414 push @query_params, @$created_by;
2418 if ( C4::Context->preference("IndependentBranches") ) {
2419 unless ( C4::Context->IsSuperLibrarian() ) {
2420 $query .= " AND (borrowers.branchcode = ? OR borrowers.branchcode ='' ) ";
2421 push @query_params, C4::Context->userenv->{branch};
2424 $query .= " ORDER BY id";
2426 return $dbh->selectall_arrayref( $query, { Slice => {} }, @query_params );
2429 =head2 GetRecentAcqui
2431 $results = GetRecentAcqui($days);
2433 C<$results> is a ref to a table which containts hashref
2435 =cut
2437 sub GetRecentAcqui {
2438 my $limit = shift;
2439 my $dbh = C4::Context->dbh;
2440 my $query = "
2441 SELECT *
2442 FROM biblio
2443 ORDER BY timestamp DESC
2444 LIMIT 0,".$limit;
2446 my $sth = $dbh->prepare($query);
2447 $sth->execute;
2448 my $results = $sth->fetchall_arrayref({});
2449 return $results;
2452 #------------------------------------------------------------#
2454 =head3 AddClaim
2456 &AddClaim($ordernumber);
2458 Add a claim for an order
2460 =cut
2462 sub AddClaim {
2463 my ($ordernumber) = @_;
2464 my $dbh = C4::Context->dbh;
2465 my $query = "
2466 UPDATE aqorders SET
2467 claims_count = claims_count + 1,
2468 claimed_date = CURDATE()
2469 WHERE ordernumber = ?
2471 my $sth = $dbh->prepare($query);
2472 $sth->execute($ordernumber);
2475 =head3 GetInvoices
2477 my @invoices = GetInvoices(
2478 invoicenumber => $invoicenumber,
2479 supplierid => $supplierid,
2480 suppliername => $suppliername,
2481 shipmentdatefrom => $shipmentdatefrom, # ISO format
2482 shipmentdateto => $shipmentdateto, # ISO format
2483 billingdatefrom => $billingdatefrom, # ISO format
2484 billingdateto => $billingdateto, # ISO format
2485 isbneanissn => $isbn_or_ean_or_issn,
2486 title => $title,
2487 author => $author,
2488 publisher => $publisher,
2489 publicationyear => $publicationyear,
2490 branchcode => $branchcode,
2491 order_by => $order_by
2494 Return a list of invoices that match all given criteria.
2496 $order_by is "column_name (asc|desc)", where column_name is any of
2497 'invoicenumber', 'booksellerid', 'shipmentdate', 'billingdate', 'closedate',
2498 'shipmentcost', 'shipmentcost_budgetid'.
2500 asc is the default if omitted
2502 =cut
2504 sub GetInvoices {
2505 my %args = @_;
2507 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2508 closedate shipmentcost shipmentcost_budgetid);
2510 my $dbh = C4::Context->dbh;
2511 my $query = qq{
2512 SELECT aqinvoices.*, aqbooksellers.name AS suppliername,
2513 COUNT(
2514 DISTINCT IF(
2515 aqorders.datereceived IS NOT NULL,
2516 aqorders.biblionumber,
2517 NULL
2519 ) AS receivedbiblios,
2520 COUNT(
2521 DISTINCT IF(
2522 aqorders.subscriptionid IS NOT NULL,
2523 aqorders.subscriptionid,
2524 NULL
2526 ) AS is_linked_to_subscriptions,
2527 SUM(aqorders.quantityreceived) AS receiveditems
2528 FROM aqinvoices
2529 LEFT JOIN aqbooksellers ON aqbooksellers.id = aqinvoices.booksellerid
2530 LEFT JOIN aqorders ON aqorders.invoiceid = aqinvoices.invoiceid
2531 LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
2532 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
2533 LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2534 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
2535 LEFT JOIN subscription ON biblio.biblionumber = subscription.biblionumber
2538 my @bind_args;
2539 my @bind_strs;
2540 if($args{supplierid}) {
2541 push @bind_strs, " aqinvoices.booksellerid = ? ";
2542 push @bind_args, $args{supplierid};
2544 if($args{invoicenumber}) {
2545 push @bind_strs, " aqinvoices.invoicenumber LIKE ? ";
2546 push @bind_args, "%$args{invoicenumber}%";
2548 if($args{suppliername}) {
2549 push @bind_strs, " aqbooksellers.name LIKE ? ";
2550 push @bind_args, "%$args{suppliername}%";
2552 if($args{shipmentdatefrom}) {
2553 push @bind_strs, " aqinvoices.shipmentdate >= ? ";
2554 push @bind_args, $args{shipmentdatefrom};
2556 if($args{shipmentdateto}) {
2557 push @bind_strs, " aqinvoices.shipmentdate <= ? ";
2558 push @bind_args, $args{shipmentdateto};
2560 if($args{billingdatefrom}) {
2561 push @bind_strs, " aqinvoices.billingdate >= ? ";
2562 push @bind_args, $args{billingdatefrom};
2564 if($args{billingdateto}) {
2565 push @bind_strs, " aqinvoices.billingdate <= ? ";
2566 push @bind_args, $args{billingdateto};
2568 if($args{isbneanissn}) {
2569 push @bind_strs, " (biblioitems.isbn LIKE CONCAT('%', ?, '%') OR biblioitems.ean LIKE CONCAT('%', ?, '%') OR biblioitems.issn LIKE CONCAT('%', ?, '%') ) ";
2570 push @bind_args, $args{isbneanissn}, $args{isbneanissn}, $args{isbneanissn};
2572 if($args{title}) {
2573 push @bind_strs, " biblio.title LIKE CONCAT('%', ?, '%') ";
2574 push @bind_args, $args{title};
2576 if($args{author}) {
2577 push @bind_strs, " biblio.author LIKE CONCAT('%', ?, '%') ";
2578 push @bind_args, $args{author};
2580 if($args{publisher}) {
2581 push @bind_strs, " biblioitems.publishercode LIKE CONCAT('%', ?, '%') ";
2582 push @bind_args, $args{publisher};
2584 if($args{publicationyear}) {
2585 push @bind_strs, " ((biblioitems.publicationyear LIKE CONCAT('%', ?, '%')) OR (biblio.copyrightdate LIKE CONCAT('%', ?, '%'))) ";
2586 push @bind_args, $args{publicationyear}, $args{publicationyear};
2588 if($args{branchcode}) {
2589 push @bind_strs, " borrowers.branchcode = ? ";
2590 push @bind_args, $args{branchcode};
2592 if($args{message_id}) {
2593 push @bind_strs, " aqinvoices.message_id = ? ";
2594 push @bind_args, $args{message_id};
2597 $query .= " WHERE " . join(" AND ", @bind_strs) if @bind_strs;
2598 $query .= " GROUP BY aqinvoices.invoiceid ";
2600 if($args{order_by}) {
2601 my ($column, $direction) = split / /, $args{order_by};
2602 if(grep /^$column$/, @columns) {
2603 $direction ||= 'ASC';
2604 $query .= " ORDER BY $column $direction";
2608 my $sth = $dbh->prepare($query);
2609 $sth->execute(@bind_args);
2611 my $results = $sth->fetchall_arrayref({});
2612 return @$results;
2615 =head3 GetInvoice
2617 my $invoice = GetInvoice($invoiceid);
2619 Get informations about invoice with given $invoiceid
2621 Return a hash filled with aqinvoices.* fields
2623 =cut
2625 sub GetInvoice {
2626 my ($invoiceid) = @_;
2627 my $invoice;
2629 return unless $invoiceid;
2631 my $dbh = C4::Context->dbh;
2632 my $query = qq{
2633 SELECT *
2634 FROM aqinvoices
2635 WHERE invoiceid = ?
2637 my $sth = $dbh->prepare($query);
2638 $sth->execute($invoiceid);
2640 $invoice = $sth->fetchrow_hashref;
2641 return $invoice;
2644 =head3 GetInvoiceDetails
2646 my $invoice = GetInvoiceDetails($invoiceid)
2648 Return informations about an invoice + the list of related order lines
2650 Orders informations are in $invoice->{orders} (array ref)
2652 =cut
2654 sub GetInvoiceDetails {
2655 my ($invoiceid) = @_;
2657 if ( !defined $invoiceid ) {
2658 carp 'GetInvoiceDetails called without an invoiceid';
2659 return;
2662 my $dbh = C4::Context->dbh;
2663 my $query = q{
2664 SELECT aqinvoices.*, aqbooksellers.name AS suppliername
2665 FROM aqinvoices
2666 LEFT JOIN aqbooksellers ON aqinvoices.booksellerid = aqbooksellers.id
2667 WHERE invoiceid = ?
2669 my $sth = $dbh->prepare($query);
2670 $sth->execute($invoiceid);
2672 my $invoice = $sth->fetchrow_hashref;
2674 $query = q{
2675 SELECT aqorders.*,
2676 biblio.*,
2677 biblio.copyrightdate,
2678 biblioitems.isbn,
2679 biblioitems.publishercode,
2680 biblioitems.publicationyear,
2681 aqbasket.basketname,
2682 aqbasketgroups.id AS basketgroupid,
2683 aqbasketgroups.name AS basketgroupname
2684 FROM aqorders
2685 LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
2686 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
2687 LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2688 LEFT JOIN biblioitems ON aqorders.biblionumber = biblioitems.biblionumber
2689 WHERE invoiceid = ?
2691 $sth = $dbh->prepare($query);
2692 $sth->execute($invoiceid);
2693 $invoice->{orders} = $sth->fetchall_arrayref({});
2694 $invoice->{orders} ||= []; # force an empty arrayref if fetchall_arrayref fails
2696 return $invoice;
2699 =head3 AddInvoice
2701 my $invoiceid = AddInvoice(
2702 invoicenumber => $invoicenumber,
2703 booksellerid => $booksellerid,
2704 shipmentdate => $shipmentdate,
2705 billingdate => $billingdate,
2706 closedate => $closedate,
2707 shipmentcost => $shipmentcost,
2708 shipmentcost_budgetid => $shipmentcost_budgetid
2711 Create a new invoice and return its id or undef if it fails.
2713 =cut
2715 sub AddInvoice {
2716 my %invoice = @_;
2718 return unless(%invoice and $invoice{invoicenumber});
2720 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2721 closedate shipmentcost shipmentcost_budgetid message_id);
2723 my @set_strs;
2724 my @set_args;
2725 foreach my $key (keys %invoice) {
2726 if(0 < grep(/^$key$/, @columns)) {
2727 push @set_strs, "$key = ?";
2728 push @set_args, ($invoice{$key} || undef);
2732 my $rv;
2733 if(@set_args > 0) {
2734 my $dbh = C4::Context->dbh;
2735 my $query = "INSERT INTO aqinvoices SET ";
2736 $query .= join (",", @set_strs);
2737 my $sth = $dbh->prepare($query);
2738 $rv = $sth->execute(@set_args);
2739 if($rv) {
2740 $rv = $dbh->last_insert_id(undef, undef, 'aqinvoices', undef);
2743 return $rv;
2746 =head3 ModInvoice
2748 ModInvoice(
2749 invoiceid => $invoiceid, # Mandatory
2750 invoicenumber => $invoicenumber,
2751 booksellerid => $booksellerid,
2752 shipmentdate => $shipmentdate,
2753 billingdate => $billingdate,
2754 closedate => $closedate,
2755 shipmentcost => $shipmentcost,
2756 shipmentcost_budgetid => $shipmentcost_budgetid
2759 Modify an invoice, invoiceid is mandatory.
2761 Return undef if it fails.
2763 =cut
2765 sub ModInvoice {
2766 my %invoice = @_;
2768 return unless(%invoice and $invoice{invoiceid});
2770 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2771 closedate shipmentcost shipmentcost_budgetid);
2773 my @set_strs;
2774 my @set_args;
2775 foreach my $key (keys %invoice) {
2776 if(0 < grep(/^$key$/, @columns)) {
2777 push @set_strs, "$key = ?";
2778 push @set_args, ($invoice{$key} || undef);
2782 my $dbh = C4::Context->dbh;
2783 my $query = "UPDATE aqinvoices SET ";
2784 $query .= join(",", @set_strs);
2785 $query .= " WHERE invoiceid = ?";
2787 my $sth = $dbh->prepare($query);
2788 $sth->execute(@set_args, $invoice{invoiceid});
2791 =head3 CloseInvoice
2793 CloseInvoice($invoiceid);
2795 Close an invoice.
2797 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => undef);
2799 =cut
2801 sub CloseInvoice {
2802 my ($invoiceid) = @_;
2804 return unless $invoiceid;
2806 my $dbh = C4::Context->dbh;
2807 my $query = qq{
2808 UPDATE aqinvoices
2809 SET closedate = CAST(NOW() AS DATE)
2810 WHERE invoiceid = ?
2812 my $sth = $dbh->prepare($query);
2813 $sth->execute($invoiceid);
2816 =head3 ReopenInvoice
2818 ReopenInvoice($invoiceid);
2820 Reopen an invoice
2822 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => output_pref({ dt=>dt_from_string, dateonly=>1, otputpref=>'iso' }))
2824 =cut
2826 sub ReopenInvoice {
2827 my ($invoiceid) = @_;
2829 return unless $invoiceid;
2831 my $dbh = C4::Context->dbh;
2832 my $query = qq{
2833 UPDATE aqinvoices
2834 SET closedate = NULL
2835 WHERE invoiceid = ?
2837 my $sth = $dbh->prepare($query);
2838 $sth->execute($invoiceid);
2841 =head3 DelInvoice
2843 DelInvoice($invoiceid);
2845 Delete an invoice if there are no items attached to it.
2847 =cut
2849 sub DelInvoice {
2850 my ($invoiceid) = @_;
2852 return unless $invoiceid;
2854 my $dbh = C4::Context->dbh;
2855 my $query = qq{
2856 SELECT COUNT(*)
2857 FROM aqorders
2858 WHERE invoiceid = ?
2860 my $sth = $dbh->prepare($query);
2861 $sth->execute($invoiceid);
2862 my $res = $sth->fetchrow_arrayref;
2863 if ( $res && $res->[0] == 0 ) {
2864 $query = qq{
2865 DELETE FROM aqinvoices
2866 WHERE invoiceid = ?
2868 my $sth = $dbh->prepare($query);
2869 return ( $sth->execute($invoiceid) > 0 );
2871 return;
2874 =head3 MergeInvoices
2876 MergeInvoices($invoiceid, \@sourceids);
2878 Merge the invoices identified by the IDs in \@sourceids into
2879 the invoice identified by $invoiceid.
2881 =cut
2883 sub MergeInvoices {
2884 my ($invoiceid, $sourceids) = @_;
2886 return unless $invoiceid;
2887 foreach my $sourceid (@$sourceids) {
2888 next if $sourceid == $invoiceid;
2889 my $source = GetInvoiceDetails($sourceid);
2890 foreach my $order (@{$source->{'orders'}}) {
2891 $order->{'invoiceid'} = $invoiceid;
2892 ModOrder($order);
2894 DelInvoice($source->{'invoiceid'});
2896 return;
2899 =head3 GetBiblioCountByBasketno
2901 $biblio_count = &GetBiblioCountByBasketno($basketno);
2903 Looks up the biblio's count that has basketno value $basketno
2905 Returns a quantity
2907 =cut
2909 sub GetBiblioCountByBasketno {
2910 my ($basketno) = @_;
2911 my $dbh = C4::Context->dbh;
2912 my $query = "
2913 SELECT COUNT( DISTINCT( biblionumber ) )
2914 FROM aqorders
2915 WHERE basketno = ?
2916 AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')
2919 my $sth = $dbh->prepare($query);
2920 $sth->execute($basketno);
2921 return $sth->fetchrow;
2924 # Note this subroutine should be moved to Koha::Acquisition::Order
2925 # Will do when a DBIC decision will be taken.
2926 sub populate_order_with_prices {
2927 my ($params) = @_;
2929 my $order = $params->{order};
2930 my $booksellerid = $params->{booksellerid};
2931 return unless $booksellerid;
2933 my $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
2935 my $receiving = $params->{receiving};
2936 my $ordering = $params->{ordering};
2937 my $discount = $order->{discount};
2938 $discount /= 100 if $discount > 1;
2940 if ($ordering) {
2941 $order->{tax_rate_on_ordering} //= $order->{tax_rate};
2942 if ( $bookseller->listincgst ) {
2943 # The user entered the rrp tax included
2944 $order->{rrp_tax_included} = $order->{rrp};
2946 # rrp tax excluded = rrp tax included / ( 1 + tax rate )
2947 $order->{rrp_tax_excluded} = $order->{rrp_tax_included} / ( 1 + $order->{tax_rate_on_ordering} );
2949 # ecost tax excluded = rrp tax excluded * ( 1 - discount )
2950 $order->{ecost_tax_excluded} = $order->{rrp_tax_excluded} * ( 1 - $discount );
2952 # ecost tax included = rrp tax included ( 1 - discount )
2953 $order->{ecost_tax_included} = $order->{rrp_tax_included} * ( 1 - $discount );
2955 else {
2956 # The user entered the rrp tax excluded
2957 $order->{rrp_tax_excluded} = $order->{rrp};
2959 # rrp tax included = rrp tax excluded * ( 1 - tax rate )
2960 $order->{rrp_tax_included} = $order->{rrp_tax_excluded} * ( 1 + $order->{tax_rate_on_ordering} );
2962 # ecost tax excluded = rrp tax excluded * ( 1 - discount )
2963 $order->{ecost_tax_excluded} = $order->{rrp_tax_excluded} * ( 1 - $discount );
2965 # ecost tax included = rrp tax excluded * ( 1 - tax rate ) * ( 1 - discount )
2966 $order->{ecost_tax_included} =
2967 $order->{rrp_tax_excluded} *
2968 ( 1 + $order->{tax_rate_on_ordering} ) *
2969 ( 1 - $discount );
2972 # tax value = quantity * ecost tax excluded * tax rate
2973 $order->{tax_value_on_ordering} =
2974 $order->{quantity} * $order->{ecost_tax_excluded} * $order->{tax_rate_on_ordering};
2977 if ($receiving) {
2978 $order->{tax_rate_on_receiving} //= $order->{tax_rate};
2979 if ( $bookseller->invoiceincgst ) {
2980 # Trick for unitprice. If the unit price rounded value is the same as the ecost rounded value
2981 # we need to keep the exact ecost value
2982 if ( Koha::Number::Price->new( $order->{unitprice} )->round == Koha::Number::Price->new( $order->{ecost_tax_included} )->round ) {
2983 $order->{unitprice} = $order->{ecost_tax_included};
2986 # The user entered the unit price tax included
2987 $order->{unitprice_tax_included} = $order->{unitprice};
2989 # unit price tax excluded = unit price tax included / ( 1 + tax rate )
2990 $order->{unitprice_tax_excluded} = $order->{unitprice_tax_included} / ( 1 + $order->{tax_rate_on_receiving} );
2992 else {
2993 # Trick for unitprice. If the unit price rounded value is the same as the ecost rounded value
2994 # we need to keep the exact ecost value
2995 if ( Koha::Number::Price->new( $order->{unitprice} )->round == Koha::Number::Price->new( $order->{ecost_tax_excluded} )->round ) {
2996 $order->{unitprice} = $order->{ecost_tax_excluded};
2999 # The user entered the unit price tax excluded
3000 $order->{unitprice_tax_excluded} = $order->{unitprice};
3003 # unit price tax included = unit price tax included * ( 1 + tax rate )
3004 $order->{unitprice_tax_included} = $order->{unitprice_tax_excluded} * ( 1 + $order->{tax_rate_on_receiving} );
3007 # tax value = quantity * unit price tax excluded * tax rate
3008 $order->{tax_value_on_receiving} = $order->{quantity} * $order->{unitprice_tax_excluded} * $order->{tax_rate_on_receiving};
3011 return $order;
3014 =head3 GetOrderUsers
3016 $order_users_ids = &GetOrderUsers($ordernumber);
3018 Returns a list of all borrowernumbers that are in order users list
3020 =cut
3022 sub GetOrderUsers {
3023 my ($ordernumber) = @_;
3025 return unless $ordernumber;
3027 my $query = q|
3028 SELECT borrowernumber
3029 FROM aqorder_users
3030 WHERE ordernumber = ?
3032 my $dbh = C4::Context->dbh;
3033 my $sth = $dbh->prepare($query);
3034 $sth->execute($ordernumber);
3035 my $results = $sth->fetchall_arrayref( {} );
3037 my @borrowernumbers;
3038 foreach (@$results) {
3039 push @borrowernumbers, $_->{'borrowernumber'};
3042 return @borrowernumbers;
3045 =head3 ModOrderUsers
3047 my @order_users_ids = (1, 2, 3);
3048 &ModOrderUsers($ordernumber, @basketusers_ids);
3050 Delete all users from order users list, and add users in C<@order_users_ids>
3051 to this users list.
3053 =cut
3055 sub ModOrderUsers {
3056 my ( $ordernumber, @order_users_ids ) = @_;
3058 return unless $ordernumber;
3060 my $dbh = C4::Context->dbh;
3061 my $query = q|
3062 DELETE FROM aqorder_users
3063 WHERE ordernumber = ?
3065 my $sth = $dbh->prepare($query);
3066 $sth->execute($ordernumber);
3068 $query = q|
3069 INSERT INTO aqorder_users (ordernumber, borrowernumber)
3070 VALUES (?, ?)
3072 $sth = $dbh->prepare($query);
3073 foreach my $order_user_id (@order_users_ids) {
3074 $sth->execute( $ordernumber, $order_user_id );
3078 sub NotifyOrderUsers {
3079 my ($ordernumber) = @_;
3081 my @borrowernumbers = GetOrderUsers($ordernumber);
3082 return unless @borrowernumbers;
3084 my $order = GetOrder( $ordernumber );
3085 for my $borrowernumber (@borrowernumbers) {
3086 my $patron = Koha::Patrons->find( $borrowernumber );
3087 my $library = $patron->library->unblessed;
3088 my $biblio = Koha::Biblios->find( $order->{biblionumber} )->unblessed;
3089 my $letter = C4::Letters::GetPreparedLetter(
3090 module => 'acquisition',
3091 letter_code => 'ACQ_NOTIF_ON_RECEIV',
3092 branchcode => $library->{branchcode},
3093 lang => $patron->lang,
3094 tables => {
3095 'branches' => $library,
3096 'borrowers' => $patron->unblessed,
3097 'biblio' => $biblio,
3098 'aqorders' => $order,
3101 if ( $letter ) {
3102 C4::Letters::EnqueueLetter(
3104 letter => $letter,
3105 borrowernumber => $borrowernumber,
3106 LibraryName => C4::Context->preference("LibraryName"),
3107 message_transport_type => 'email',
3109 ) or warn "can't enqueue letter $letter";
3114 =head3 FillWithDefaultValues
3116 FillWithDefaultValues( $marc_record );
3118 This will update the record with default value defined in the ACQ framework.
3119 For all existing fields, if a default value exists and there are no subfield, it will be created.
3120 If the field does not exist, it will be created too.
3122 =cut
3124 sub FillWithDefaultValues {
3125 my ($record) = @_;
3126 my $tagslib = C4::Biblio::GetMarcStructure( 1, 'ACQ', { unsafe => 1 } );
3127 if ($tagslib) {
3128 my ($itemfield) =
3129 C4::Biblio::GetMarcFromKohaField( 'items.itemnumber', '' );
3130 for my $tag ( sort keys %$tagslib ) {
3131 next unless $tag;
3132 next if $tag == $itemfield;
3133 for my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
3134 next if IsMarcStructureInternal($tagslib->{$tag}{$subfield});
3135 my $defaultvalue = $tagslib->{$tag}{$subfield}{defaultvalue};
3136 if ( defined $defaultvalue and $defaultvalue ne '' ) {
3137 my @fields = $record->field($tag);
3138 if (@fields) {
3139 for my $field (@fields) {
3140 unless ( defined $field->subfield($subfield) ) {
3141 $field->add_subfields(
3142 $subfield => $defaultvalue );
3146 else {
3147 $record->insert_fields_ordered(
3148 MARC::Field->new(
3149 $tag, '', '', $subfield => $defaultvalue
3160 __END__
3162 =head1 AUTHOR
3164 Koha Development Team <http://koha-community.org/>
3166 =cut