Bug 26803: Fix PLUGIN_DIR when plugin_dirs is multivalued
[koha.git] / C4 / Acquisition.pm
blobe29cbe8733c8183032936c36f4bd7c694b33d82e
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 Text::CSV_XS;
24 use C4::Context;
25 use C4::Debug;
26 use C4::Suggestions;
27 use C4::Biblio;
28 use C4::Contract;
29 use C4::Debug;
30 use C4::Templates qw(gettemplate);
31 use Koha::DateUtils qw( dt_from_string output_pref );
32 use Koha::Acquisition::Baskets;
33 use Koha::Acquisition::Booksellers;
34 use Koha::Acquisition::Orders;
35 use Koha::Biblios;
36 use Koha::Exceptions;
37 use Koha::Items;
38 use Koha::Number::Price;
39 use Koha::Libraries;
40 use Koha::CsvProfiles;
41 use Koha::Patrons;
43 use C4::Koha;
45 use MARC::Field;
46 use MARC::Record;
48 use Time::localtime;
50 use vars qw(@ISA @EXPORT);
52 BEGIN {
53 require Exporter;
54 @ISA = qw(Exporter);
55 @EXPORT = qw(
56 &GetBasket &NewBasket &CloseBasket &ReopenBasket &ModBasket
57 &GetBasketAsCSV &GetBasketGroupAsCSV
58 &GetBasketsByBookseller &GetBasketsByBasketgroup
59 &GetBasketsInfosByBookseller
61 &GetBasketUsers &ModBasketUsers
62 &CanUserManageBasket
64 &ModBasketHeader
66 &ModBasketgroup &NewBasketgroup &DelBasketgroup &GetBasketgroup &CloseBasketgroup
67 &GetBasketgroups &ReOpenBasketgroup
69 &ModOrder &GetOrder &GetOrders &GetOrdersByBiblionumber
70 &GetOrderFromItemnumber
71 &SearchOrders &GetHistory &GetRecentAcqui
72 &ModReceiveOrder &CancelReceipt
73 &TransferOrder
74 &ModItemOrder
76 &GetParcels
78 &GetInvoices
79 &GetInvoice
80 &GetInvoiceDetails
81 &AddInvoice
82 &ModInvoice
83 &CloseInvoice
84 &ReopenInvoice
85 &DelInvoice
86 &MergeInvoices
88 &AddClaim
89 &GetBiblioCountByBasketno
91 &GetOrderUsers
92 &ModOrderUsers
93 &NotifyOrderUsers
95 &FillWithDefaultValues
97 &get_rounded_price
98 &get_rounding_sql
106 sub GetOrderFromItemnumber {
107 my ($itemnumber) = @_;
108 my $dbh = C4::Context->dbh;
109 my $query = qq|
111 SELECT * from aqorders LEFT JOIN aqorders_items
112 ON ( aqorders.ordernumber = aqorders_items.ordernumber )
113 WHERE itemnumber = ? |;
115 my $sth = $dbh->prepare($query);
117 # $sth->trace(3);
119 $sth->execute($itemnumber);
121 my $order = $sth->fetchrow_hashref;
122 return ( $order );
126 =head1 NAME
128 C4::Acquisition - Koha functions for dealing with orders and acquisitions
130 =head1 SYNOPSIS
132 use C4::Acquisition;
134 =head1 DESCRIPTION
136 The functions in this module deal with acquisitions, managing book
137 orders, basket and parcels.
139 =head1 FUNCTIONS
141 =head2 FUNCTIONS ABOUT BASKETS
143 =head3 GetBasket
145 $aqbasket = &GetBasket($basketnumber);
147 get all basket informations in aqbasket for a given basket
149 B<returns:> informations for a given basket returned as a hashref.
151 =cut
153 sub GetBasket {
154 my ($basketno) = @_;
155 my $dbh = C4::Context->dbh;
156 my $query = "
157 SELECT aqbasket.*,
158 concat( b.firstname,' ',b.surname) AS authorisedbyname
159 FROM aqbasket
160 LEFT JOIN borrowers b ON aqbasket.authorisedby=b.borrowernumber
161 WHERE basketno=?
163 my $sth=$dbh->prepare($query);
164 $sth->execute($basketno);
165 my $basket = $sth->fetchrow_hashref;
166 return ( $basket );
169 #------------------------------------------------------------#
171 =head3 NewBasket
173 $basket = &NewBasket( $booksellerid, $authorizedby, $basketname,
174 $basketnote, $basketbooksellernote, $basketcontractnumber, $deliveryplace, $billingplace, $is_standing, $create_items );
176 Create a new basket in aqbasket table
178 =over
180 =item C<$booksellerid> is a foreign key in the aqbasket table
182 =item C<$authorizedby> is the username of who created the basket
184 =back
186 The other parameters are optional, see ModBasketHeader for more info on them.
188 =cut
190 sub NewBasket {
191 my ( $booksellerid, $authorisedby, $basketname, $basketnote,
192 $basketbooksellernote, $basketcontractnumber, $deliveryplace,
193 $billingplace, $is_standing, $create_items ) = @_;
194 my $dbh = C4::Context->dbh;
195 my $query =
196 'INSERT INTO aqbasket (creationdate,booksellerid,authorisedby) '
197 . 'VALUES (now(),?,?)';
198 $dbh->do( $query, {}, $booksellerid, $authorisedby );
200 my $basket = $dbh->{mysql_insertid};
201 $basketname ||= q{}; # default to empty strings
202 $basketnote ||= q{};
203 $basketbooksellernote ||= q{};
204 ModBasketHeader( $basket, $basketname, $basketnote, $basketbooksellernote,
205 $basketcontractnumber, $booksellerid, $deliveryplace, $billingplace, $is_standing, $create_items );
206 return $basket;
209 #------------------------------------------------------------#
211 =head3 CloseBasket
213 &CloseBasket($basketno);
215 close a basket (becomes unmodifiable, except for receives)
217 =cut
219 sub CloseBasket {
220 my ($basketno) = @_;
221 my $dbh = C4::Context->dbh;
222 $dbh->do('UPDATE aqbasket SET closedate=now() WHERE basketno=?', {}, $basketno );
224 $dbh->do(
225 q{UPDATE aqorders SET orderstatus = 'ordered' WHERE basketno = ? AND orderstatus NOT IN ( 'complete', 'cancelled')},
226 {}, $basketno
228 return;
231 =head3 ReopenBasket
233 &ReopenBasket($basketno);
235 reopen a basket
237 =cut
239 sub ReopenBasket {
240 my ($basketno) = @_;
241 my $dbh = C4::Context->dbh;
242 $dbh->do( q{UPDATE aqbasket SET closedate=NULL WHERE basketno=?}, {}, $basketno );
244 $dbh->do( q{
245 UPDATE aqorders
246 SET orderstatus = 'new'
247 WHERE basketno = ?
248 AND orderstatus NOT IN ( 'complete', 'cancelled' )
249 }, {}, $basketno);
250 return;
253 #------------------------------------------------------------#
255 =head3 GetBasketAsCSV
257 &GetBasketAsCSV($basketno);
259 Export a basket as CSV
261 $cgi parameter is needed for column name translation
263 =cut
265 sub GetBasketAsCSV {
266 my ($basketno, $cgi, $csv_profile_id) = @_;
267 my $basket = GetBasket($basketno);
268 my @orders = GetOrders($basketno);
269 my $contract = GetContract({
270 contractnumber => $basket->{'contractnumber'}
273 my $template = C4::Templates::gettemplate("acqui/csv/basket.tt", "intranet", $cgi);
274 my @rows;
275 if ($csv_profile_id) {
276 my $csv_profile = Koha::CsvProfiles->find( $csv_profile_id );
277 Koha::Exceptions::ObjectNotFound->throw( 'There is no valid csv profile given') unless $csv_profile;
279 my $csv = Text::CSV_XS->new({'quote_char'=>'"','escape_char'=>'"','sep_char'=>$csv_profile->csv_separator,'binary'=>1});
280 my $csv_profile_content = $csv_profile->content;
281 my ( @headers, @fields );
282 while ( $csv_profile_content =~ /
283 ([^=\|]+) # header
285 ([^\|]*) # fieldname (table.row or row)
286 \|? /gxms
288 my $header = $1;
289 my $field = ($2 eq '') ? $1 : $2;
291 $header =~ s/^\s+|\s+$//g; # Trim whitespaces
292 push @headers, $header;
294 $field =~ s/[^\.]*\.{1}//; # Remove the table name if exists.
295 $field =~ s/^\s+|\s+$//g; # Trim whitespaces
296 push @fields, $field;
298 for my $order (@orders) {
299 my @row;
300 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
301 my $biblioitem = $biblio->biblioitem;
302 $order = { %$order, %{ $biblioitem->unblessed } };
303 if ($contract) {
304 $order = {%$order, %$contract};
306 $order = {%$order, %$basket, %{ $biblio->unblessed }};
307 for my $field (@fields) {
308 push @row, $order->{$field};
310 push @rows, \@row;
312 my $content = join( $csv_profile->csv_separator, @headers ) . "\n";
313 for my $row ( @rows ) {
314 $csv->combine(@$row);
315 my $string = $csv->string;
316 $content .= $string . "\n";
318 return $content;
320 else {
321 foreach my $order (@orders) {
322 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
323 my $biblioitem = $biblio->biblioitem;
324 my $row = {
325 contractname => $contract->{'contractname'},
326 ordernumber => $order->{'ordernumber'},
327 entrydate => $order->{'entrydate'},
328 isbn => $order->{'isbn'},
329 author => $biblio->author,
330 title => $biblio->title,
331 publicationyear => $biblioitem->publicationyear,
332 publishercode => $biblioitem->publishercode,
333 collectiontitle => $biblioitem->collectiontitle,
334 notes => $order->{'order_vendornote'},
335 quantity => $order->{'quantity'},
336 rrp => $order->{'rrp'},
338 for my $place ( qw( deliveryplace billingplace ) ) {
339 if ( my $library = Koha::Libraries->find( $row->{deliveryplace} ) ) {
340 $row->{$place} = $library->branchname
343 foreach(qw(
344 contractname author title publishercode collectiontitle notes
345 deliveryplace billingplace
346 ) ) {
347 # Double the quotes to not be interpreted as a field end
348 $row->{$_} =~ s/"/""/g if $row->{$_};
350 push @rows, $row;
353 @rows = sort {
354 if(defined $a->{publishercode} and defined $b->{publishercode}) {
355 $a->{publishercode} cmp $b->{publishercode};
357 } @rows;
359 $template->param(rows => \@rows);
361 return $template->output;
366 =head3 GetBasketGroupAsCSV
368 &GetBasketGroupAsCSV($basketgroupid);
370 Export a basket group as CSV
372 $cgi parameter is needed for column name translation
374 =cut
376 sub GetBasketGroupAsCSV {
377 my ($basketgroupid, $cgi) = @_;
378 my $baskets = GetBasketsByBasketgroup($basketgroupid);
380 my $template = C4::Templates::gettemplate('acqui/csv/basketgroup.tt', 'intranet', $cgi);
382 my @rows;
383 for my $basket (@$baskets) {
384 my @orders = GetOrders( $basket->{basketno} );
385 my $contract = GetContract({
386 contractnumber => $basket->{contractnumber}
388 my $bookseller = Koha::Acquisition::Booksellers->find( $basket->{booksellerid} );
389 my $basketgroup = GetBasketgroup( $$basket{basketgroupid} );
391 foreach my $order (@orders) {
392 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
393 my $biblioitem = $biblio->biblioitem;
394 my $row = {
395 clientnumber => $bookseller->accountnumber,
396 basketname => $basket->{basketname},
397 ordernumber => $order->{ordernumber},
398 author => $biblio->author,
399 title => $biblio->title,
400 publishercode => $biblioitem->publishercode,
401 publicationyear => $biblioitem->publicationyear,
402 collectiontitle => $biblioitem->collectiontitle,
403 isbn => $order->{isbn},
404 quantity => $order->{quantity},
405 rrp_tax_included => $order->{rrp_tax_included},
406 rrp_tax_excluded => $order->{rrp_tax_excluded},
407 discount => $bookseller->discount,
408 ecost_tax_included => $order->{ecost_tax_included},
409 ecost_tax_excluded => $order->{ecost_tax_excluded},
410 notes => $order->{order_vendornote},
411 entrydate => $order->{entrydate},
412 booksellername => $bookseller->name,
413 bookselleraddress => $bookseller->address1,
414 booksellerpostal => $bookseller->postal,
415 contractnumber => $contract->{contractnumber},
416 contractname => $contract->{contractname},
418 my $temp = {
419 basketgroupdeliveryplace => $basketgroup->{deliveryplace},
420 basketgroupbillingplace => $basketgroup->{billingplace},
421 basketdeliveryplace => $basket->{deliveryplace},
422 basketbillingplace => $basket->{billingplace},
424 for my $place (qw( basketgroupdeliveryplace basketgroupbillingplace basketdeliveryplace basketbillingplace )) {
425 if ( my $library = Koha::Libraries->find( $temp->{$place} ) ) {
426 $row->{$place} = $library->branchname;
429 foreach(qw(
430 basketname author title publishercode collectiontitle notes
431 booksellername bookselleraddress booksellerpostal contractname
432 basketgroupdeliveryplace basketgroupbillingplace
433 basketdeliveryplace basketbillingplace
434 ) ) {
435 # Double the quotes to not be interpreted as a field end
436 $row->{$_} =~ s/"/""/g if $row->{$_};
438 push @rows, $row;
441 $template->param(rows => \@rows);
443 return $template->output;
447 =head3 CloseBasketgroup
449 &CloseBasketgroup($basketgroupno);
451 close a basketgroup
453 =cut
455 sub CloseBasketgroup {
456 my ($basketgroupno) = @_;
457 my $dbh = C4::Context->dbh;
458 my $sth = $dbh->prepare("
459 UPDATE aqbasketgroups
460 SET closed=1
461 WHERE id=?
463 $sth->execute($basketgroupno);
466 #------------------------------------------------------------#
468 =head3 ReOpenBaskergroup($basketgroupno)
470 &ReOpenBaskergroup($basketgroupno);
472 reopen a basketgroup
474 =cut
476 sub ReOpenBasketgroup {
477 my ($basketgroupno) = @_;
478 my $dbh = C4::Context->dbh;
479 my $sth = $dbh->prepare("
480 UPDATE aqbasketgroups
481 SET closed=0
482 WHERE id=?
484 $sth->execute($basketgroupno);
487 #------------------------------------------------------------#
489 =head3 ModBasket
491 &ModBasket($basketinfo);
493 Modifies a basket, using a hashref $basketinfo for the relevant information, only $basketinfo->{'basketno'} is required.
495 =over
497 =item C<$basketno> is the primary key of the basket in the aqbasket table.
499 =back
501 =cut
503 sub ModBasket {
504 my $basketinfo = shift;
505 my $query = "UPDATE aqbasket SET ";
506 my @params;
507 foreach my $key (keys %$basketinfo){
508 if ($key ne 'basketno'){
509 $query .= "$key=?, ";
510 push(@params, $basketinfo->{$key} || undef );
513 # get rid of the "," at the end of $query
514 if (substr($query, length($query)-2) eq ', '){
515 chop($query);
516 chop($query);
517 $query .= ' ';
519 $query .= "WHERE basketno=?";
520 push(@params, $basketinfo->{'basketno'});
521 my $dbh = C4::Context->dbh;
522 my $sth = $dbh->prepare($query);
523 $sth->execute(@params);
525 return;
528 #------------------------------------------------------------#
530 =head3 ModBasketHeader
532 &ModBasketHeader($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid);
534 Modifies a basket's header.
536 =over
538 =item C<$basketno> is the "basketno" field in the "aqbasket" table;
540 =item C<$basketname> is the "basketname" field in the "aqbasket" table;
542 =item C<$note> is the "note" field in the "aqbasket" table;
544 =item C<$booksellernote> is the "booksellernote" field in the "aqbasket" table;
546 =item C<$contractnumber> is the "contractnumber" (foreign) key in the "aqbasket" table.
548 =item C<$booksellerid> is the id (foreign) key in the "aqbooksellers" table for the vendor.
550 =item C<$deliveryplace> is the "deliveryplace" field in the aqbasket table.
552 =item C<$billingplace> is the "billingplace" field in the aqbasket table.
554 =item C<$is_standing> is the "is_standing" field in the aqbasket table.
556 =item C<$create_items> should be set to 'ordering', 'receiving' or 'cataloguing' (or undef, in which
557 case the AcqCreateItem syspref takes precedence).
559 =back
561 =cut
563 sub ModBasketHeader {
564 my ($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid, $deliveryplace, $billingplace, $is_standing, $create_items) = @_;
566 $is_standing ||= 0;
567 my $query = qq{
568 UPDATE aqbasket
569 SET basketname=?, note=?, booksellernote=?, booksellerid=?, deliveryplace=?, billingplace=?, is_standing=?, create_items=?
570 WHERE basketno=?
573 my $dbh = C4::Context->dbh;
574 my $sth = $dbh->prepare($query);
575 $sth->execute($basketname, $note, $booksellernote, $booksellerid, $deliveryplace, $billingplace, $is_standing, $create_items || undef, $basketno);
577 if ( $contractnumber ) {
578 my $query2 ="UPDATE aqbasket SET contractnumber=? WHERE basketno=?";
579 my $sth2 = $dbh->prepare($query2);
580 $sth2->execute($contractnumber,$basketno);
582 return;
585 #------------------------------------------------------------#
587 =head3 GetBasketsByBookseller
589 @results = &GetBasketsByBookseller($booksellerid, $extra);
591 Returns a list of hashes of all the baskets that belong to bookseller 'booksellerid'.
593 =over
595 =item C<$booksellerid> is the 'id' field of the bookseller in the aqbooksellers table
597 =item C<$extra> is the extra sql parameters, can be
599 $extra->{groupby}: group baskets by column
600 ex. $extra->{groupby} = aqbasket.basketgroupid
601 $extra->{orderby}: order baskets by column
602 $extra->{limit}: limit number of results (can be helpful for pagination)
604 =back
606 =cut
608 sub GetBasketsByBookseller {
609 my ($booksellerid, $extra) = @_;
610 my $query = "SELECT * FROM aqbasket WHERE booksellerid=?";
611 if ($extra){
612 if ($extra->{groupby}) {
613 $query .= " GROUP by $extra->{groupby}";
615 if ($extra->{orderby}){
616 $query .= " ORDER by $extra->{orderby}";
618 if ($extra->{limit}){
619 $query .= " LIMIT $extra->{limit}";
622 my $dbh = C4::Context->dbh;
623 my $sth = $dbh->prepare($query);
624 $sth->execute($booksellerid);
625 return $sth->fetchall_arrayref({});
628 =head3 GetBasketsInfosByBookseller
630 my $baskets = GetBasketsInfosByBookseller($supplierid, $allbaskets);
632 The optional second parameter allbaskets is a boolean allowing you to
633 select all baskets from the supplier; by default only active baskets (open or
634 closed but still something to receive) are returned.
636 Returns in a arrayref of hashref all about booksellers baskets, plus:
637 total_biblios: Number of distinct biblios in basket
638 total_items: Number of items in basket
639 expected_items: Number of non-received items in basket
641 =cut
643 sub GetBasketsInfosByBookseller {
644 my ($supplierid, $allbaskets) = @_;
646 return unless $supplierid;
648 my $dbh = C4::Context->dbh;
649 my $query = q{
650 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,
651 SUM(aqorders.quantity) AS total_items,
652 SUM(
653 IF ( aqorders.orderstatus = 'cancelled', aqorders.quantity, 0 )
654 ) AS total_items_cancelled,
655 COUNT(DISTINCT aqorders.biblionumber) AS total_biblios,
656 SUM(
657 IF(aqorders.datereceived IS NULL
658 AND aqorders.datecancellationprinted IS NULL
659 , aqorders.quantity
660 , 0)
661 ) AS expected_items,
662 SUM( aqorders.uncertainprice ) AS uncertainprices
663 FROM aqbasket
664 LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
665 WHERE booksellerid = ?};
667 $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";
669 unless ( $allbaskets ) {
670 # Don't show the basket if it's NOT CLOSED or is FULLY RECEIVED
671 $query.=" HAVING (closedate IS NULL OR (
672 SUM(
673 IF(aqorders.datereceived IS NULL
674 AND aqorders.datecancellationprinted IS NULL
675 , aqorders.quantity
676 , 0)
677 ) > 0))"
680 my $sth = $dbh->prepare($query);
681 $sth->execute($supplierid);
682 my $baskets = $sth->fetchall_arrayref({});
684 # Retrieve the number of biblios cancelled
685 my $cancelled_biblios = $dbh->selectall_hashref( q|
686 SELECT COUNT(DISTINCT(biblionumber)) AS total_biblios_cancelled, aqbasket.basketno
687 FROM aqbasket
688 LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
689 WHERE booksellerid = ?
690 AND aqorders.orderstatus = 'cancelled'
691 GROUP BY aqbasket.basketno
692 |, 'basketno', {}, $supplierid );
693 map {
694 $_->{total_biblios_cancelled} = $cancelled_biblios->{$_->{basketno}}{total_biblios_cancelled} || 0
695 } @$baskets;
697 return $baskets;
700 =head3 GetBasketUsers
702 $basketusers_ids = &GetBasketUsers($basketno);
704 Returns a list of all borrowernumbers that are in basket users list
706 =cut
708 sub GetBasketUsers {
709 my $basketno = shift;
711 return unless $basketno;
713 my $query = qq{
714 SELECT borrowernumber
715 FROM aqbasketusers
716 WHERE basketno = ?
718 my $dbh = C4::Context->dbh;
719 my $sth = $dbh->prepare($query);
720 $sth->execute($basketno);
721 my $results = $sth->fetchall_arrayref( {} );
723 my @borrowernumbers;
724 foreach (@$results) {
725 push @borrowernumbers, $_->{'borrowernumber'};
728 return @borrowernumbers;
731 =head3 ModBasketUsers
733 my @basketusers_ids = (1, 2, 3);
734 &ModBasketUsers($basketno, @basketusers_ids);
736 Delete all users from basket users list, and add users in C<@basketusers_ids>
737 to this users list.
739 =cut
741 sub ModBasketUsers {
742 my ($basketno, @basketusers_ids) = @_;
744 return unless $basketno;
746 my $dbh = C4::Context->dbh;
747 my $query = qq{
748 DELETE FROM aqbasketusers
749 WHERE basketno = ?
751 my $sth = $dbh->prepare($query);
752 $sth->execute($basketno);
754 $query = qq{
755 INSERT INTO aqbasketusers (basketno, borrowernumber)
756 VALUES (?, ?)
758 $sth = $dbh->prepare($query);
759 foreach my $basketuser_id (@basketusers_ids) {
760 $sth->execute($basketno, $basketuser_id);
762 return;
765 =head3 CanUserManageBasket
767 my $bool = CanUserManageBasket($borrower, $basket[, $userflags]);
768 my $bool = CanUserManageBasket($borrowernumber, $basketno[, $userflags]);
770 Check if a borrower can manage a basket, according to system preference
771 AcqViewBaskets, user permissions and basket properties (creator, users list,
772 branch).
774 First parameter can be either a borrowernumber or a hashref as returned by
775 Koha::Patron->unblessed
777 Second parameter can be either a basketno or a hashref as returned by
778 C4::Acquisition::GetBasket.
780 The third parameter is optional. If given, it should be a hashref as returned
781 by C4::Auth::getuserflags. If not, getuserflags is called.
783 If user is authorised to manage basket, returns 1.
784 Otherwise returns 0.
786 =cut
788 sub CanUserManageBasket {
789 my ($borrower, $basket, $userflags) = @_;
791 if (!ref $borrower) {
792 # FIXME This needs to be replaced
793 # We should not accept both scalar and array
794 # Tests need to be updated
795 $borrower = Koha::Patrons->find( $borrower )->unblessed;
797 if (!ref $basket) {
798 $basket = GetBasket($basket);
801 return 0 unless ($basket and $borrower);
803 my $borrowernumber = $borrower->{borrowernumber};
804 my $basketno = $basket->{basketno};
806 my $AcqViewBaskets = C4::Context->preference('AcqViewBaskets');
808 if (!defined $userflags) {
809 my $dbh = C4::Context->dbh;
810 my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE borrowernumber = ?");
811 $sth->execute($borrowernumber);
812 my ($flags) = $sth->fetchrow_array;
813 $sth->finish;
815 $userflags = C4::Auth::getuserflags($flags, $borrower->{userid}, $dbh);
818 unless ($userflags->{superlibrarian}
819 || (ref $userflags->{acquisition} && $userflags->{acquisition}->{order_manage_all})
820 || (!ref $userflags->{acquisition} && $userflags->{acquisition}))
822 if (not exists $userflags->{acquisition}) {
823 return 0;
826 if ( (ref $userflags->{acquisition} && !$userflags->{acquisition}->{order_manage})
827 || (!ref $userflags->{acquisition} && !$userflags->{acquisition}) ) {
828 return 0;
831 if ($AcqViewBaskets eq 'user'
832 && $basket->{authorisedby} != $borrowernumber
833 && ! grep { $borrowernumber eq $_ } GetBasketUsers($basketno)) {
834 return 0;
837 if ($AcqViewBaskets eq 'branch' && defined $basket->{branch}
838 && $basket->{branch} ne $borrower->{branchcode}) {
839 return 0;
843 return 1;
846 #------------------------------------------------------------#
848 =head3 GetBasketsByBasketgroup
850 $baskets = &GetBasketsByBasketgroup($basketgroupid);
852 Returns a reference to all baskets that belong to basketgroup $basketgroupid.
854 =cut
856 sub GetBasketsByBasketgroup {
857 my $basketgroupid = shift;
858 my $query = qq{
859 SELECT *, aqbasket.booksellerid as booksellerid
860 FROM aqbasket
861 LEFT JOIN aqcontract USING(contractnumber) WHERE basketgroupid=?
863 my $dbh = C4::Context->dbh;
864 my $sth = $dbh->prepare($query);
865 $sth->execute($basketgroupid);
866 return $sth->fetchall_arrayref({});
869 #------------------------------------------------------------#
871 =head3 NewBasketgroup
873 $basketgroupid = NewBasketgroup(\%hashref);
875 Adds a basketgroup to the aqbasketgroups table, and add the initial baskets to it.
877 $hashref->{'booksellerid'} is the 'id' field of the bookseller in the aqbooksellers table,
879 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
881 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
883 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
885 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
887 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
889 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
891 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
893 =cut
895 sub NewBasketgroup {
896 my $basketgroupinfo = shift;
897 die "booksellerid is required to create a basketgroup" unless $basketgroupinfo->{'booksellerid'};
898 my $query = "INSERT INTO aqbasketgroups (";
899 my @params;
900 foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
901 if ( defined $basketgroupinfo->{$field} ) {
902 $query .= "$field, ";
903 push(@params, $basketgroupinfo->{$field});
906 $query .= "booksellerid) VALUES (";
907 foreach (@params) {
908 $query .= "?, ";
910 $query .= "?)";
911 push(@params, $basketgroupinfo->{'booksellerid'});
912 my $dbh = C4::Context->dbh;
913 my $sth = $dbh->prepare($query);
914 $sth->execute(@params);
915 my $basketgroupid = $dbh->{'mysql_insertid'};
916 if( $basketgroupinfo->{'basketlist'} ) {
917 foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
918 my $query2 = "UPDATE aqbasket SET basketgroupid=? WHERE basketno=?";
919 my $sth2 = $dbh->prepare($query2);
920 $sth2->execute($basketgroupid, $basketno);
923 return $basketgroupid;
926 #------------------------------------------------------------#
928 =head3 ModBasketgroup
930 ModBasketgroup(\%hashref);
932 Modifies a basketgroup in the aqbasketgroups table, and add the baskets to it.
934 $hashref->{'id'} is the 'id' field of the basketgroup in the aqbasketgroup table, this parameter is mandatory,
936 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
938 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
940 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
942 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
944 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
946 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
948 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
950 =cut
952 sub ModBasketgroup {
953 my $basketgroupinfo = shift;
954 die "basketgroup id is required to edit a basketgroup" unless $basketgroupinfo->{'id'};
955 my $dbh = C4::Context->dbh;
956 my $query = "UPDATE aqbasketgroups SET ";
957 my @params;
958 foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
959 if ( defined $basketgroupinfo->{$field} ) {
960 $query .= "$field=?, ";
961 push(@params, $basketgroupinfo->{$field});
964 chop($query);
965 chop($query);
966 $query .= " WHERE id=?";
967 push(@params, $basketgroupinfo->{'id'});
968 my $sth = $dbh->prepare($query);
969 $sth->execute(@params);
971 $sth = $dbh->prepare('UPDATE aqbasket SET basketgroupid = NULL WHERE basketgroupid = ?');
972 $sth->execute($basketgroupinfo->{'id'});
974 if($basketgroupinfo->{'basketlist'} && @{$basketgroupinfo->{'basketlist'}}){
975 $sth = $dbh->prepare("UPDATE aqbasket SET basketgroupid=? WHERE basketno=?");
976 foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
977 $sth->execute($basketgroupinfo->{'id'}, $basketno);
980 return;
983 #------------------------------------------------------------#
985 =head3 DelBasketgroup
987 DelBasketgroup($basketgroupid);
989 Deletes a basketgroup in the aqbasketgroups table, and removes the reference to it from the baskets,
991 =over
993 =item C<$basketgroupid> is the 'id' field of the basket in the aqbasketgroup table
995 =back
997 =cut
999 sub DelBasketgroup {
1000 my $basketgroupid = shift;
1001 die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
1002 my $query = "DELETE FROM aqbasketgroups WHERE id=?";
1003 my $dbh = C4::Context->dbh;
1004 my $sth = $dbh->prepare($query);
1005 $sth->execute($basketgroupid);
1006 return;
1009 #------------------------------------------------------------#
1012 =head2 FUNCTIONS ABOUT ORDERS
1014 =head3 GetBasketgroup
1016 $basketgroup = &GetBasketgroup($basketgroupid);
1018 Returns a reference to the hash containing all information about the basketgroup.
1020 =cut
1022 sub GetBasketgroup {
1023 my $basketgroupid = shift;
1024 die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
1025 my $dbh = C4::Context->dbh;
1026 my $result_set = $dbh->selectall_arrayref(
1027 'SELECT * FROM aqbasketgroups WHERE id=?',
1028 { Slice => {} },
1029 $basketgroupid
1031 return $result_set->[0]; # id is unique
1034 #------------------------------------------------------------#
1036 =head3 GetBasketgroups
1038 $basketgroups = &GetBasketgroups($booksellerid);
1040 Returns a reference to the array of all the basketgroups of bookseller $booksellerid.
1042 =cut
1044 sub GetBasketgroups {
1045 my $booksellerid = shift;
1046 die 'bookseller id is required to edit a basketgroup' unless $booksellerid;
1047 my $query = 'SELECT * FROM aqbasketgroups WHERE booksellerid=? ORDER BY id DESC';
1048 my $dbh = C4::Context->dbh;
1049 my $sth = $dbh->prepare($query);
1050 $sth->execute($booksellerid);
1051 return $sth->fetchall_arrayref({});
1054 #------------------------------------------------------------#
1056 =head2 FUNCTIONS ABOUT ORDERS
1058 =head3 GetOrders
1060 @orders = &GetOrders( $basketno, { orderby => 'biblio.title', cancelled => 0|1 } );
1062 Looks up the pending (non-cancelled) orders with the given basket
1063 number.
1065 If cancelled is set, only cancelled orders will be returned.
1067 =cut
1069 sub GetOrders {
1070 my ( $basketno, $params ) = @_;
1072 return () unless $basketno;
1074 my $orderby = $params->{orderby};
1075 my $cancelled = $params->{cancelled} || 0;
1077 my $dbh = C4::Context->dbh;
1078 my $query = q|
1079 SELECT biblio.*,biblioitems.*,
1080 aqorders.*,
1081 aqbudgets.*,
1083 $query .= $cancelled
1084 ? q|
1085 aqorders_transfers.ordernumber_to AS transferred_to,
1086 aqorders_transfers.timestamp AS transferred_to_timestamp
1088 : q|
1089 aqorders_transfers.ordernumber_from AS transferred_from,
1090 aqorders_transfers.timestamp AS transferred_from_timestamp
1092 $query .= q|
1093 FROM aqorders
1094 LEFT JOIN aqbudgets ON aqbudgets.budget_id = aqorders.budget_id
1095 LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1096 LEFT JOIN biblioitems ON biblioitems.biblionumber =biblio.biblionumber
1098 $query .= $cancelled
1099 ? q|
1100 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_from = aqorders.ordernumber
1102 : q|
1103 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1106 $query .= q|
1107 WHERE basketno=?
1110 if ($cancelled) {
1111 $orderby ||= q|biblioitems.publishercode, biblio.title|;
1112 $query .= q|
1113 AND datecancellationprinted IS NOT NULL
1116 else {
1117 $orderby ||=
1118 q|aqorders.datecancellationprinted desc, aqorders.timestamp desc|;
1119 $query .= q|
1120 AND datecancellationprinted IS NULL
1124 $query .= " ORDER BY $orderby";
1125 my $orders =
1126 $dbh->selectall_arrayref( $query, { Slice => {} }, $basketno );
1127 return @{$orders};
1131 #------------------------------------------------------------#
1133 =head3 GetOrdersByBiblionumber
1135 @orders = &GetOrdersByBiblionumber($biblionumber);
1137 Looks up the orders with linked to a specific $biblionumber, including
1138 cancelled orders and received orders.
1140 return :
1141 C<@orders> is an array of references-to-hash, whose keys are the
1142 fields from the aqorders, biblio, and biblioitems tables in the Koha database.
1144 =cut
1146 sub GetOrdersByBiblionumber {
1147 my $biblionumber = shift;
1148 return unless $biblionumber;
1149 my $dbh = C4::Context->dbh;
1150 my $query ="
1151 SELECT biblio.*,biblioitems.*,
1152 aqorders.*,
1153 aqbudgets.*
1154 FROM aqorders
1155 LEFT JOIN aqbudgets ON aqbudgets.budget_id = aqorders.budget_id
1156 LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1157 LEFT JOIN biblioitems ON biblioitems.biblionumber =biblio.biblionumber
1158 WHERE aqorders.biblionumber=?
1160 my $result_set =
1161 $dbh->selectall_arrayref( $query, { Slice => {} }, $biblionumber );
1162 return @{$result_set};
1166 #------------------------------------------------------------#
1168 =head3 GetOrder
1170 $order = &GetOrder($ordernumber);
1172 Looks up an order by order number.
1174 Returns a reference-to-hash describing the order. The keys of
1175 C<$order> are fields from the biblio, biblioitems, aqorders tables of the Koha database.
1177 =cut
1179 sub GetOrder {
1180 my ($ordernumber) = @_;
1181 return unless $ordernumber;
1183 my $dbh = C4::Context->dbh;
1184 my $query = qq{SELECT
1185 aqorders.*,
1186 biblio.title,
1187 biblio.author,
1188 aqbasket.basketname,
1189 borrowers.branchcode,
1190 biblioitems.publicationyear,
1191 biblio.copyrightdate,
1192 biblioitems.editionstatement,
1193 biblioitems.isbn,
1194 biblioitems.ean,
1195 biblio.seriestitle,
1196 biblioitems.publishercode,
1197 aqorders.rrp AS unitpricesupplier,
1198 aqorders.ecost AS unitpricelib,
1199 aqbudgets.budget_name AS budget,
1200 aqbooksellers.name AS supplier,
1201 aqbooksellers.id AS supplierid,
1202 biblioitems.publishercode AS publisher,
1203 ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
1204 DATE(aqbasket.closedate) AS orderdate,
1205 aqorders.quantity - COALESCE(aqorders.quantityreceived,0) AS quantity_to_receive,
1206 (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
1207 DATEDIFF(CURDATE( ),closedate) AS latesince
1208 FROM aqorders LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1209 LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
1210 LEFT JOIN aqbudgets ON aqorders.budget_id = aqbudgets.budget_id,
1211 aqbasket LEFT JOIN borrowers ON aqbasket.authorisedby = borrowers.borrowernumber
1212 LEFT JOIN aqbooksellers ON aqbasket.booksellerid = aqbooksellers.id
1213 WHERE aqorders.basketno = aqbasket.basketno
1214 AND ordernumber=?};
1215 my $result_set =
1216 $dbh->selectall_arrayref( $query, { Slice => {} }, $ordernumber );
1218 # result_set assumed to contain 1 match
1219 return $result_set->[0];
1222 =head3 ModOrder
1224 &ModOrder(\%hashref);
1226 Modifies an existing order. Updates the order with order number
1227 $hashref->{'ordernumber'} and biblionumber $hashref->{'biblionumber'}. All
1228 other keys of the hash update the fields with the same name in the aqorders
1229 table of the Koha database.
1231 =cut
1233 sub ModOrder {
1234 my $orderinfo = shift;
1236 die "Ordernumber is required" if $orderinfo->{'ordernumber'} eq '';
1238 my $dbh = C4::Context->dbh;
1239 my @params;
1241 # update uncertainprice to an integer, just in case (under FF, checked boxes have the value "ON" by default)
1242 $orderinfo->{uncertainprice}=1 if $orderinfo->{uncertainprice};
1244 # delete($orderinfo->{'branchcode'});
1245 # the hash contains a lot of entries not in aqorders, so get the columns ...
1246 my $sth = $dbh->prepare("SELECT * FROM aqorders LIMIT 1;");
1247 $sth->execute;
1248 my $colnames = $sth->{NAME};
1249 #FIXME Be careful. If aqorders would have columns with diacritics,
1250 #you should need to decode what you get back from NAME.
1251 #See report 10110 and guided_reports.pl
1252 my $query = "UPDATE aqorders SET ";
1254 foreach my $orderinfokey (grep(!/ordernumber/, keys %$orderinfo)){
1255 # ... and skip hash entries that are not in the aqorders table
1256 # FIXME : probably not the best way to do it (would be better to have a correct hash)
1257 next unless grep { $_ eq $orderinfokey } @$colnames;
1258 $query .= "$orderinfokey=?, ";
1259 push(@params, $orderinfo->{$orderinfokey});
1262 $query .= "timestamp=NOW() WHERE ordernumber=?";
1263 push(@params, $orderinfo->{'ordernumber'} );
1264 $sth = $dbh->prepare($query);
1265 $sth->execute(@params);
1266 return;
1269 #------------------------------------------------------------#
1271 =head3 ModItemOrder
1273 ModItemOrder($itemnumber, $ordernumber);
1275 Modifies the ordernumber of an item in aqorders_items.
1277 =cut
1279 sub ModItemOrder {
1280 my ($itemnumber, $ordernumber) = @_;
1282 return unless ($itemnumber and $ordernumber);
1284 my $dbh = C4::Context->dbh;
1285 my $query = qq{
1286 UPDATE aqorders_items
1287 SET ordernumber = ?
1288 WHERE itemnumber = ?
1290 my $sth = $dbh->prepare($query);
1291 return $sth->execute($ordernumber, $itemnumber);
1294 #------------------------------------------------------------#
1296 =head3 ModReceiveOrder
1298 my ( $date_received, $new_ordernumber ) = ModReceiveOrder(
1300 biblionumber => $biblionumber,
1301 order => $order,
1302 quantityreceived => $quantityreceived,
1303 user => $user,
1304 invoice => $invoice,
1305 budget_id => $budget_id,
1306 datereceived => $datereceived,
1307 received_itemnumbers => \@received_itemnumbers,
1311 Updates an order, to reflect the fact that it was received, at least
1312 in part.
1314 If a partial order is received, splits the order into two.
1316 Updates the order with biblionumber C<$biblionumber> and ordernumber
1317 C<$order->{ordernumber}>.
1319 =cut
1322 sub ModReceiveOrder {
1323 my ($params) = @_;
1324 my $biblionumber = $params->{biblionumber};
1325 my $order = { %{ $params->{order} } }; # Copy the order, we don't want to modify it
1326 my $invoice = $params->{invoice};
1327 my $quantrec = $params->{quantityreceived};
1328 my $user = $params->{user};
1329 my $budget_id = $params->{budget_id};
1330 my $datereceived = $params->{datereceived};
1331 my $received_items = $params->{received_items};
1333 my $dbh = C4::Context->dbh;
1334 $datereceived = output_pref(
1336 dt => ( $datereceived ? dt_from_string( $datereceived ) : dt_from_string ),
1337 dateformat => 'iso',
1338 dateonly => 1,
1342 my $suggestionid = GetSuggestionFromBiblionumber( $biblionumber );
1343 if ($suggestionid) {
1344 ModSuggestion( {suggestionid=>$suggestionid,
1345 STATUS=>'AVAILABLE',
1346 biblionumber=> $biblionumber}
1350 my $result_set = $dbh->selectrow_arrayref(
1351 q{SELECT aqbasket.is_standing
1352 FROM aqbasket
1353 WHERE basketno=?},{ Slice => {} }, $order->{basketno});
1354 my $is_standing = $result_set->[0]; # we assume we have a unique basket
1356 my $new_ordernumber = $order->{ordernumber};
1357 if ( $is_standing || $order->{quantity} > $quantrec ) {
1358 # Split order line in two parts: the first is the original order line
1359 # without received items (the quantity is decreased),
1360 # the second part is a new order line with quantity=quantityrec
1361 # (entirely received)
1362 my $query = q|
1363 UPDATE aqorders
1364 SET quantity = ?,
1365 orderstatus = 'partial'|;
1366 $query .= q| WHERE ordernumber = ?|;
1367 my $sth = $dbh->prepare($query);
1369 $sth->execute(
1370 ( $is_standing ? 1 : ($order->{quantity} - $quantrec) ),
1371 $order->{ordernumber}
1374 if ( not $order->{subscriptionid} && defined $order->{order_internalnote} ) {
1375 $dbh->do(
1376 q|UPDATE aqorders
1377 SET order_internalnote = ?
1378 WHERE ordernumber = ?|, {},
1379 $order->{order_internalnote}, $order->{ordernumber}
1383 # Recalculate tax_value
1384 $dbh->do(q|
1385 UPDATE aqorders
1387 tax_value_on_ordering = quantity * | . get_rounding_sql(q|ecost_tax_excluded|) . q| * tax_rate_on_ordering,
1388 tax_value_on_receiving = quantity * | . get_rounding_sql(q|unitprice_tax_excluded|) . q| * tax_rate_on_receiving
1389 WHERE ordernumber = ?
1390 |, undef, $order->{ordernumber});
1392 delete $order->{ordernumber};
1393 $order->{budget_id} = ( $budget_id || $order->{budget_id} );
1394 $order->{quantity} = $quantrec;
1395 $order->{quantityreceived} = $quantrec;
1396 $order->{ecost_tax_excluded} //= 0;
1397 $order->{tax_rate_on_ordering} //= 0;
1398 $order->{unitprice_tax_excluded} //= 0;
1399 $order->{tax_rate_on_receiving} //= 0;
1400 $order->{tax_value_on_ordering} = $order->{quantity} * get_rounded_price($order->{ecost_tax_excluded}) * $order->{tax_rate_on_ordering};
1401 $order->{tax_value_on_receiving} = $order->{quantity} * get_rounded_price($order->{unitprice_tax_excluded}) * $order->{tax_rate_on_receiving};
1402 $order->{datereceived} = $datereceived;
1403 $order->{invoiceid} = $invoice->{invoiceid};
1404 $order->{orderstatus} = 'complete';
1405 $new_ordernumber = Koha::Acquisition::Order->new($order)->store->ordernumber; # TODO What if the store fails?
1407 if ($received_items) {
1408 foreach my $itemnumber (@$received_items) {
1409 ModItemOrder($itemnumber, $new_ordernumber);
1412 } else {
1413 my $query = q|
1414 UPDATE aqorders
1415 SET quantityreceived = ?,
1416 datereceived = ?,
1417 invoiceid = ?,
1418 budget_id = ?,
1419 orderstatus = 'complete'
1422 $query .= q|
1423 , replacementprice = ?
1424 | if defined $order->{replacementprice};
1426 $query .= q|
1427 , unitprice = ?, unitprice_tax_included = ?, unitprice_tax_excluded = ?
1428 | if defined $order->{unitprice};
1430 $query .= q|
1431 ,tax_value_on_receiving = ?
1432 | if defined $order->{tax_value_on_receiving};
1434 $query .= q|
1435 ,tax_rate_on_receiving = ?
1436 | if defined $order->{tax_rate_on_receiving};
1438 $query .= q|
1439 , order_internalnote = ?
1440 | if defined $order->{order_internalnote};
1442 $query .= q| where biblionumber=? and ordernumber=?|;
1444 my $sth = $dbh->prepare( $query );
1445 my @params = ( $quantrec, $datereceived, $invoice->{invoiceid}, ( $budget_id ? $budget_id : $order->{budget_id} ) );
1447 if ( defined $order->{replacementprice} ) {
1448 push @params, $order->{replacementprice};
1451 if ( defined $order->{unitprice} ) {
1452 push @params, $order->{unitprice}, $order->{unitprice_tax_included}, $order->{unitprice_tax_excluded};
1455 if ( defined $order->{tax_value_on_receiving} ) {
1456 push @params, $order->{tax_value_on_receiving};
1459 if ( defined $order->{tax_rate_on_receiving} ) {
1460 push @params, $order->{tax_rate_on_receiving};
1463 if ( defined $order->{order_internalnote} ) {
1464 push @params, $order->{order_internalnote};
1467 push @params, ( $biblionumber, $order->{ordernumber} );
1469 $sth->execute( @params );
1471 # All items have been received, sent a notification to users
1472 NotifyOrderUsers( $order->{ordernumber} );
1475 return ($datereceived, $new_ordernumber);
1478 =head3 CancelReceipt
1480 my $parent_ordernumber = CancelReceipt($ordernumber);
1482 Cancel an order line receipt and update the parent order line, as if no
1483 receipt was made.
1484 If items are created at receipt (AcqCreateItem = receiving) then delete
1485 these items.
1487 =cut
1489 sub CancelReceipt {
1490 my $ordernumber = shift;
1492 return unless $ordernumber;
1494 my $dbh = C4::Context->dbh;
1495 my $query = qq{
1496 SELECT datereceived, parent_ordernumber, quantity
1497 FROM aqorders
1498 WHERE ordernumber = ?
1500 my $sth = $dbh->prepare($query);
1501 $sth->execute($ordernumber);
1502 my $order = $sth->fetchrow_hashref;
1503 unless($order) {
1504 warn "CancelReceipt: order $ordernumber does not exist";
1505 return;
1507 unless($order->{'datereceived'}) {
1508 warn "CancelReceipt: order $ordernumber is not received";
1509 return;
1512 my $parent_ordernumber = $order->{'parent_ordernumber'};
1514 my $order_obj = Koha::Acquisition::Orders->find( $ordernumber ); # FIXME rewrite all this subroutine using this object
1515 my @itemnumbers = $order_obj->items->get_column('itemnumber');
1517 if($parent_ordernumber == $ordernumber || not $parent_ordernumber) {
1518 # The order line has no parent, just mark it as not received
1519 $query = qq{
1520 UPDATE aqorders
1521 SET quantityreceived = ?,
1522 datereceived = ?,
1523 invoiceid = ?,
1524 orderstatus = 'ordered'
1525 WHERE ordernumber = ?
1527 $sth = $dbh->prepare($query);
1528 $sth->execute(0, undef, undef, $ordernumber);
1529 _cancel_items_receipt( $order_obj );
1530 } else {
1531 # The order line has a parent, increase parent quantity and delete
1532 # the order line.
1533 unless ( $order_obj->basket->is_standing ) {
1534 $query = qq{
1535 SELECT quantity, datereceived
1536 FROM aqorders
1537 WHERE ordernumber = ?
1539 $sth = $dbh->prepare($query);
1540 $sth->execute($parent_ordernumber);
1541 my $parent_order = $sth->fetchrow_hashref;
1542 unless($parent_order) {
1543 warn "Parent order $parent_ordernumber does not exist.";
1544 return;
1546 if($parent_order->{'datereceived'}) {
1547 warn "CancelReceipt: parent order is received.".
1548 " Can't cancel receipt.";
1549 return;
1551 $query = qq{
1552 UPDATE aqorders
1553 SET quantity = ?,
1554 orderstatus = 'ordered'
1555 WHERE ordernumber = ?
1557 $sth = $dbh->prepare($query);
1558 my $rv = $sth->execute(
1559 $order->{'quantity'} + $parent_order->{'quantity'},
1560 $parent_ordernumber
1562 unless($rv) {
1563 warn "Cannot update parent order line, so do not cancel".
1564 " receipt";
1565 return;
1568 # Recalculate tax_value
1569 $dbh->do(q|
1570 UPDATE aqorders
1572 tax_value_on_ordering = quantity * | . get_rounding_sql(q|ecost_tax_excluded|) . q| * tax_rate_on_ordering,
1573 tax_value_on_receiving = quantity * | . get_rounding_sql(q|unitprice_tax_excluded|) . q| * tax_rate_on_receiving
1574 WHERE ordernumber = ?
1575 |, undef, $parent_ordernumber);
1578 _cancel_items_receipt( $order_obj, $parent_ordernumber );
1579 # Delete order line
1580 $query = qq{
1581 DELETE FROM aqorders
1582 WHERE ordernumber = ?
1584 $sth = $dbh->prepare($query);
1585 $sth->execute($ordernumber);
1589 if( $order_obj->basket->effective_create_items eq 'ordering' ) {
1590 my @affects = split q{\|}, C4::Context->preference("AcqItemSetSubfieldsWhenReceiptIsCancelled");
1591 if ( @affects ) {
1592 for my $in ( @itemnumbers ) {
1593 my $item = Koha::Items->find( $in ); # FIXME We do not need that, we already have Koha::Items from $order_obj->items
1594 my $biblio = $item->biblio;
1595 my ( $itemfield ) = GetMarcFromKohaField( 'items.itemnumber' );
1596 my $item_marc = C4::Items::GetMarcItem( $biblio->biblionumber, $in );
1597 for my $affect ( @affects ) {
1598 my ( $sf, $v ) = split q{=}, $affect, 2;
1599 foreach ( $item_marc->field($itemfield) ) {
1600 $_->update( $sf => $v );
1603 C4::Items::ModItemFromMarc( $item_marc, $biblio->biblionumber, $in );
1608 return $parent_ordernumber;
1611 sub _cancel_items_receipt {
1612 my ( $order, $parent_ordernumber ) = @_;
1613 $parent_ordernumber ||= $order->ordernumber;
1615 my $items = $order->items;
1616 if ( $order->basket->effective_create_items eq 'receiving' ) {
1617 # Remove items that were created at receipt
1618 my $query = qq{
1619 DELETE FROM items, aqorders_items
1620 USING items, aqorders_items
1621 WHERE items.itemnumber = ? AND aqorders_items.itemnumber = ?
1623 my $dbh = C4::Context->dbh;
1624 my $sth = $dbh->prepare($query);
1625 while ( my $item = $items->next ) {
1626 $sth->execute($item->itemnumber, $item->itemnumber);
1628 } else {
1629 # Update items
1630 while ( my $item = $items->next ) {
1631 ModItemOrder($item->itemnumber, $parent_ordernumber);
1636 #------------------------------------------------------------#
1638 =head3 SearchOrders
1640 @results = &SearchOrders({
1641 ordernumber => $ordernumber,
1642 search => $search,
1643 ean => $ean,
1644 booksellerid => $booksellerid,
1645 basketno => $basketno,
1646 basketname => $basketname,
1647 basketgroupname => $basketgroupname,
1648 owner => $owner,
1649 pending => $pending
1650 ordered => $ordered
1651 biblionumber => $biblionumber,
1652 budget_id => $budget_id
1655 Searches for orders filtered by criteria.
1657 C<$ordernumber> Finds matching orders or transferred orders by ordernumber.
1658 C<$search> Finds orders matching %$search% in title, author, or isbn.
1659 C<$owner> Finds order for the logged in user.
1660 C<$pending> Finds pending orders. Ignores completed and cancelled orders.
1661 C<$ordered> Finds orders to receive only (status 'ordered' or 'partial').
1664 C<@results> is an array of references-to-hash with the keys are fields
1665 from aqorders, biblio, biblioitems and aqbasket tables.
1667 =cut
1669 sub SearchOrders {
1670 my ( $params ) = @_;
1671 my $ordernumber = $params->{ordernumber};
1672 my $search = $params->{search};
1673 my $ean = $params->{ean};
1674 my $booksellerid = $params->{booksellerid};
1675 my $basketno = $params->{basketno};
1676 my $basketname = $params->{basketname};
1677 my $basketgroupname = $params->{basketgroupname};
1678 my $owner = $params->{owner};
1679 my $pending = $params->{pending};
1680 my $ordered = $params->{ordered};
1681 my $biblionumber = $params->{biblionumber};
1682 my $budget_id = $params->{budget_id};
1684 my $dbh = C4::Context->dbh;
1685 my @args = ();
1686 my $query = q{
1687 SELECT aqbasket.basketno,
1688 borrowers.surname,
1689 borrowers.firstname,
1690 biblio.*,
1691 biblioitems.isbn,
1692 biblioitems.biblioitemnumber,
1693 biblioitems.publishercode,
1694 biblioitems.publicationyear,
1695 aqbasket.authorisedby,
1696 aqbasket.booksellerid,
1697 aqbasket.closedate,
1698 aqbasket.creationdate,
1699 aqbasket.basketname,
1700 aqbasketgroups.id as basketgroupid,
1701 aqbasketgroups.name as basketgroupname,
1702 aqorders.*
1703 FROM aqorders
1704 LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
1705 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
1706 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
1707 LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
1708 LEFT JOIN biblioitems ON biblioitems.biblionumber=biblio.biblionumber
1711 # If we search on ordernumber, we retrieve the transferred order if a transfer has been done.
1712 $query .= q{
1713 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1714 } if $ordernumber;
1716 $query .= q{
1717 WHERE (datecancellationprinted is NULL)
1720 if ( $pending or $ordered ) {
1721 $query .= q{
1722 AND (
1723 ( aqbasket.is_standing AND aqorders.orderstatus IN ( "new", "ordered", "partial" ) )
1724 OR (
1725 ( quantity > quantityreceived OR quantityreceived is NULL )
1728 if ( $ordered ) {
1729 $query .= q{ AND aqorders.orderstatus IN ( "ordered", "partial" )};
1731 $query .= q{
1737 my $userenv = C4::Context->userenv;
1738 if ( C4::Context->preference("IndependentBranches") ) {
1739 unless ( C4::Context->IsSuperLibrarian() ) {
1740 $query .= q{
1741 AND (
1742 borrowers.branchcode = ?
1743 OR borrowers.branchcode = ''
1746 push @args, $userenv->{branch};
1750 if ( $ordernumber ) {
1751 $query .= ' AND ( aqorders.ordernumber = ? OR aqorders_transfers.ordernumber_from = ? ) ';
1752 push @args, ( $ordernumber, $ordernumber );
1754 if ( $biblionumber ) {
1755 $query .= 'AND aqorders.biblionumber = ?';
1756 push @args, $biblionumber;
1758 if( $search ) {
1759 $query .= ' AND (biblio.title LIKE ? OR biblio.author LIKE ? OR biblioitems.isbn LIKE ?)';
1760 push @args, ("%$search%","%$search%","%$search%");
1762 if ( $ean ) {
1763 $query .= ' AND biblioitems.ean = ?';
1764 push @args, $ean;
1766 if ( $booksellerid ) {
1767 $query .= 'AND aqbasket.booksellerid = ?';
1768 push @args, $booksellerid;
1770 if( $basketno ) {
1771 $query .= 'AND aqbasket.basketno = ?';
1772 push @args, $basketno;
1774 if( $basketname ) {
1775 $query .= 'AND aqbasket.basketname LIKE ?';
1776 push @args, "%$basketname%";
1778 if( $basketgroupname ) {
1779 $query .= ' AND aqbasketgroups.name LIKE ?';
1780 push @args, "%$basketgroupname%";
1783 if ( $owner ) {
1784 $query .= ' AND aqbasket.authorisedby=? ';
1785 push @args, $userenv->{'number'};
1788 if ( $budget_id ) {
1789 $query .= ' AND aqorders.budget_id = ?';
1790 push @args, $budget_id;
1793 $query .= ' ORDER BY aqbasket.basketno';
1795 my $sth = $dbh->prepare($query);
1796 $sth->execute(@args);
1797 return $sth->fetchall_arrayref({});
1800 #------------------------------------------------------------#
1802 =head3 TransferOrder
1804 my $newordernumber = TransferOrder($ordernumber, $basketno);
1806 Transfer an order line to a basket.
1807 Mark $ordernumber as cancelled with an internal note 'Cancelled and transferred
1808 to BOOKSELLER on DATE' and create new order with internal note
1809 'Transferred from BOOKSELLER on DATE'.
1810 Move all attached items to the new order.
1811 Received orders cannot be transferred.
1812 Return the ordernumber of created order.
1814 =cut
1816 sub TransferOrder {
1817 my ($ordernumber, $basketno) = @_;
1819 return unless ($ordernumber and $basketno);
1821 my $order = Koha::Acquisition::Orders->find( $ordernumber ) or return;
1822 return if $order->datereceived;
1824 $order = $order->unblessed;
1826 my $basket = GetBasket($basketno);
1827 return unless $basket;
1829 my $dbh = C4::Context->dbh;
1830 my ($query, $sth, $rv);
1832 $query = q{
1833 UPDATE aqorders
1834 SET datecancellationprinted = CAST(NOW() AS date), orderstatus = ?
1835 WHERE ordernumber = ?
1837 $sth = $dbh->prepare($query);
1838 $rv = $sth->execute('cancelled', $ordernumber);
1840 delete $order->{'ordernumber'};
1841 delete $order->{parent_ordernumber};
1842 $order->{'basketno'} = $basketno;
1844 my $newordernumber = Koha::Acquisition::Order->new($order)->store->ordernumber;
1846 $query = q{
1847 UPDATE aqorders_items
1848 SET ordernumber = ?
1849 WHERE ordernumber = ?
1851 $sth = $dbh->prepare($query);
1852 $sth->execute($newordernumber, $ordernumber);
1854 $query = q{
1855 INSERT INTO aqorders_transfers (ordernumber_from, ordernumber_to)
1856 VALUES (?, ?)
1858 $sth = $dbh->prepare($query);
1859 $sth->execute($ordernumber, $newordernumber);
1861 return $newordernumber;
1864 =head3 get_rounding_sql
1866 $rounding_sql = get_rounding_sql($column_name);
1868 returns the correct SQL routine based on OrderPriceRounding system preference.
1870 =cut
1872 sub get_rounding_sql {
1873 my ( $round_string ) = @_;
1874 my $rounding_pref = C4::Context->preference('OrderPriceRounding') // q{};
1875 if ( $rounding_pref eq "nearest_cent" ) {
1876 return "CAST($round_string*100 AS SIGNED)/100";
1878 return $round_string;
1881 =head3 get_rounded_price
1883 $rounded_price = get_rounded_price( $price );
1885 returns a price rounded as specified in OrderPriceRounding system preference.
1887 =cut
1889 sub get_rounded_price {
1890 my ( $price ) = @_;
1891 my $rounding_pref = C4::Context->preference('OrderPriceRounding') // q{};
1892 if( $rounding_pref eq 'nearest_cent' ) {
1893 return Koha::Number::Price->new( $price )->round();
1895 return $price;
1899 =head2 FUNCTIONS ABOUT PARCELS
1901 =head3 GetParcels
1903 $results = &GetParcels($bookseller, $order, $code, $datefrom, $dateto);
1905 get a lists of parcels.
1907 * Input arg :
1909 =over
1911 =item $bookseller
1912 is the bookseller this function has to get parcels.
1914 =item $order
1915 To know on what criteria the results list has to be ordered.
1917 =item $code
1918 is the booksellerinvoicenumber.
1920 =item $datefrom & $dateto
1921 to know on what date this function has to filter its search.
1923 =back
1925 * return:
1926 a pointer on a hash list containing parcel informations as such :
1928 =over
1930 =item Creation date
1932 =item Last operation
1934 =item Number of biblio
1936 =item Number of items
1938 =back
1940 =cut
1942 sub GetParcels {
1943 my ($bookseller,$order, $code, $datefrom, $dateto) = @_;
1944 my $dbh = C4::Context->dbh;
1945 my @query_params = ();
1946 my $strsth ="
1947 SELECT aqinvoices.invoicenumber,
1948 datereceived,purchaseordernumber,
1949 count(DISTINCT biblionumber) AS biblio,
1950 sum(quantity) AS itemsexpected,
1951 sum(quantityreceived) AS itemsreceived
1952 FROM aqorders LEFT JOIN aqbasket ON aqbasket.basketno = aqorders.basketno
1953 LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
1954 WHERE aqbasket.booksellerid = ? and datereceived IS NOT NULL
1956 push @query_params, $bookseller;
1958 if ( defined $code ) {
1959 $strsth .= ' and aqinvoices.invoicenumber like ? ';
1960 # add a % to the end of the code to allow stemming.
1961 push @query_params, "$code%";
1964 if ( defined $datefrom ) {
1965 $strsth .= ' and datereceived >= ? ';
1966 push @query_params, $datefrom;
1969 if ( defined $dateto ) {
1970 $strsth .= 'and datereceived <= ? ';
1971 push @query_params, $dateto;
1974 $strsth .= "group by aqinvoices.invoicenumber,datereceived ";
1976 # can't use a placeholder to place this column name.
1977 # but, we could probably be checking to make sure it is a column that will be fetched.
1978 $strsth .= "order by $order " if ($order);
1980 my $sth = $dbh->prepare($strsth);
1982 $sth->execute( @query_params );
1983 my $results = $sth->fetchall_arrayref({});
1984 return @{$results};
1987 #------------------------------------------------------------#
1989 =head3 GetHistory
1991 \@order_loop = GetHistory( %params );
1993 Retreives some acquisition history information
1995 params:
1996 title
1997 author
1998 name
1999 isbn
2001 from_placed_on
2002 to_placed_on
2003 basket - search both basket name and number
2004 booksellerinvoicenumber
2005 basketgroupname
2006 budget
2007 orderstatus (note that orderstatus '' will retrieve orders
2008 of any status except cancelled)
2009 is_standing
2010 managing_library
2011 biblionumber
2012 get_canceled_order (if set to a true value, cancelled orders will
2013 be included)
2015 returns:
2016 $order_loop is a list of hashrefs that each look like this:
2018 'author' => 'Twain, Mark',
2019 'basketno' => '1',
2020 'biblionumber' => '215',
2021 'count' => 1,
2022 'creationdate' => 'MM/DD/YYYY',
2023 'datereceived' => undef,
2024 'ecost' => '1.00',
2025 'id' => '1',
2026 'invoicenumber' => undef,
2027 'name' => '',
2028 'ordernumber' => '1',
2029 'quantity' => 1,
2030 'quantityreceived' => undef,
2031 'title' => 'The Adventures of Huckleberry Finn',
2032 'managing_library' => 'CPL'
2033 'is_standing' => '1'
2036 =cut
2038 sub GetHistory {
2039 # don't run the query if there are no parameters (list would be too long for sure !)
2040 croak "No search params" unless @_;
2041 my %params = @_;
2042 my $title = $params{title};
2043 my $author = $params{author};
2044 my $isbn = $params{isbn};
2045 my $ean = $params{ean};
2046 my $name = $params{name};
2047 my $from_placed_on = $params{from_placed_on};
2048 my $to_placed_on = $params{to_placed_on};
2049 my $basket = $params{basket};
2050 my $booksellerinvoicenumber = $params{booksellerinvoicenumber};
2051 my $basketgroupname = $params{basketgroupname};
2052 my $budget = $params{budget};
2053 my $orderstatus = $params{orderstatus};
2054 my $is_standing = $params{is_standing};
2055 my $biblionumber = $params{biblionumber};
2056 my $get_canceled_order = $params{get_canceled_order} || 0;
2057 my $ordernumber = $params{ordernumber};
2058 my $search_children_too = $params{search_children_too} || 0;
2059 my $created_by = $params{created_by} || [];
2060 my $managing_library = $params{managing_library};
2061 my $ordernumbers = $params{ordernumbers} || [];
2062 my $additional_fields = $params{additional_fields} // [];
2064 my $total_qty = 0;
2065 my $total_qtyreceived = 0;
2066 my $total_price = 0;
2068 #get variation of isbn
2069 my @isbn_params;
2070 my @isbns;
2071 if ($isbn){
2072 if ( C4::Context->preference("SearchWithISBNVariations") ){
2073 @isbns = C4::Koha::GetVariationsOfISBN( $isbn );
2074 foreach my $isb (@isbns){
2075 push @isbn_params, '?';
2078 unless (@isbns){
2079 push @isbns, $isbn;
2080 push @isbn_params, '?';
2084 my $dbh = C4::Context->dbh;
2085 my $query ="
2086 SELECT
2087 COALESCE(biblio.title, deletedbiblio.title) AS title,
2088 COALESCE(biblio.author, deletedbiblio.author) AS author,
2089 COALESCE(biblioitems.isbn, deletedbiblioitems.isbn) AS isbn,
2090 COALESCE(biblioitems.ean, deletedbiblioitems.ean) AS ean,
2091 aqorders.basketno,
2092 aqbasket.basketname,
2093 aqbasket.basketgroupid,
2094 aqbasket.authorisedby,
2095 aqbasket.is_standing,
2096 concat( borrowers.firstname,' ',borrowers.surname) AS authorisedbyname,
2097 branch as managing_library,
2098 aqbasketgroups.name as groupname,
2099 aqbooksellers.name,
2100 aqbasket.creationdate,
2101 aqorders.datereceived,
2102 aqorders.quantity,
2103 aqorders.quantityreceived,
2104 aqorders.ecost,
2105 aqorders.ordernumber,
2106 aqorders.invoiceid,
2107 aqinvoices.invoicenumber,
2108 aqbooksellers.id as id,
2109 aqorders.biblionumber,
2110 aqorders.orderstatus,
2111 aqorders.parent_ordernumber,
2112 aqbudgets.budget_name
2114 $query .= ", aqbudgets.budget_id AS budget" if defined $budget;
2115 $query .= "
2116 FROM aqorders
2117 LEFT JOIN aqbasket ON aqorders.basketno=aqbasket.basketno
2118 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid=aqbasketgroups.id
2119 LEFT JOIN aqbooksellers ON aqbasket.booksellerid=aqbooksellers.id
2120 LEFT JOIN biblioitems ON biblioitems.biblionumber=aqorders.biblionumber
2121 LEFT JOIN biblio ON biblio.biblionumber=aqorders.biblionumber
2122 LEFT JOIN aqbudgets ON aqorders.budget_id=aqbudgets.budget_id
2123 LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
2124 LEFT JOIN deletedbiblio ON deletedbiblio.biblionumber=aqorders.biblionumber
2125 LEFT JOIN deletedbiblioitems ON deletedbiblioitems.biblionumber=aqorders.biblionumber
2126 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
2129 $query .= " WHERE 1 ";
2131 unless ($get_canceled_order or (defined $orderstatus and $orderstatus eq 'cancelled')) {
2132 $query .= " AND datecancellationprinted IS NULL ";
2135 my @query_params = ();
2137 if ( $biblionumber ) {
2138 $query .= " AND biblio.biblionumber = ?";
2139 push @query_params, $biblionumber;
2142 if ( $title ) {
2143 $query .= " AND biblio.title LIKE ? ";
2144 $title =~ s/\s+/%/g;
2145 push @query_params, "%$title%";
2148 if ( $author ) {
2149 $query .= " AND biblio.author LIKE ? ";
2150 push @query_params, "%$author%";
2153 if ( @isbns ) {
2154 $query .= " AND ( biblioitems.isbn LIKE " . join (" OR biblioitems.isbn LIKE ", @isbn_params ) . ")";
2155 foreach my $isb (@isbns){
2156 push @query_params, "%$isb%";
2160 if ( $ean ) {
2161 $query .= " AND biblioitems.ean = ? ";
2162 push @query_params, "$ean";
2164 if ( $name ) {
2165 $query .= " AND aqbooksellers.name LIKE ? ";
2166 push @query_params, "%$name%";
2169 if ( $budget ) {
2170 $query .= " AND aqbudgets.budget_id = ? ";
2171 push @query_params, "$budget";
2174 if ( $from_placed_on ) {
2175 $query .= " AND creationdate >= ? ";
2176 push @query_params, $from_placed_on;
2179 if ( $to_placed_on ) {
2180 $query .= " AND creationdate <= ? ";
2181 push @query_params, $to_placed_on;
2184 if ( defined $orderstatus and $orderstatus ne '') {
2185 $query .= " AND aqorders.orderstatus = ? ";
2186 push @query_params, "$orderstatus";
2189 if ( $is_standing ) {
2190 $query .= " AND is_standing = ? ";
2191 push @query_params, $is_standing;
2194 if ($basket) {
2195 if ($basket =~ m/^\d+$/) {
2196 $query .= " AND aqorders.basketno = ? ";
2197 push @query_params, $basket;
2198 } else {
2199 $query .= " AND aqbasket.basketname LIKE ? ";
2200 push @query_params, "%$basket%";
2204 if ($booksellerinvoicenumber) {
2205 $query .= " AND aqinvoices.invoicenumber LIKE ? ";
2206 push @query_params, "%$booksellerinvoicenumber%";
2209 if ($basketgroupname) {
2210 $query .= " AND aqbasketgroups.name LIKE ? ";
2211 push @query_params, "%$basketgroupname%";
2214 if ($ordernumber) {
2215 $query .= " AND (aqorders.ordernumber = ? ";
2216 push @query_params, $ordernumber;
2217 if ($search_children_too) {
2218 $query .= " OR aqorders.parent_ordernumber = ? ";
2219 push @query_params, $ordernumber;
2221 $query .= ") ";
2224 if ( @$created_by ) {
2225 $query .= ' AND aqbasket.authorisedby IN ( ' . join( ',', ('?') x @$created_by ) . ')';
2226 push @query_params, @$created_by;
2229 if ( $managing_library ) {
2230 $query .= " AND aqbasket.branch = ? ";
2231 push @query_params, $managing_library;
2234 if ( @$ordernumbers ) {
2235 $query .= ' AND (aqorders.ordernumber IN ( ' . join (',', ('?') x @$ordernumbers ) . '))';
2236 push @query_params, @$ordernumbers;
2238 if ( @$additional_fields ) {
2239 my @baskets = Koha::Acquisition::Baskets->filter_by_additional_fields($additional_fields);
2241 return [] unless @baskets;
2243 # No parameterization because record IDs come directly from DB
2244 $query .= ' AND aqbasket.basketno IN ( ' . join( ',', map { $_->basketno } @baskets ) . ' )';
2247 if ( C4::Context->preference("IndependentBranches") ) {
2248 unless ( C4::Context->IsSuperLibrarian() ) {
2249 $query .= " AND (borrowers.branchcode = ? OR borrowers.branchcode ='' ) ";
2250 push @query_params, C4::Context->userenv->{branch};
2253 $query .= " ORDER BY id";
2255 return $dbh->selectall_arrayref( $query, { Slice => {} }, @query_params );
2258 =head2 GetRecentAcqui
2260 $results = GetRecentAcqui($days);
2262 C<$results> is a ref to a table which contains hashref
2264 =cut
2266 sub GetRecentAcqui {
2267 my $limit = shift;
2268 my $dbh = C4::Context->dbh;
2269 my $query = "
2270 SELECT *
2271 FROM biblio
2272 ORDER BY timestamp DESC
2273 LIMIT 0,".$limit;
2275 my $sth = $dbh->prepare($query);
2276 $sth->execute;
2277 my $results = $sth->fetchall_arrayref({});
2278 return $results;
2281 #------------------------------------------------------------#
2283 =head3 AddClaim
2285 &AddClaim($ordernumber);
2287 Add a claim for an order
2289 =cut
2291 sub AddClaim {
2292 my ($ordernumber) = @_;
2293 my $dbh = C4::Context->dbh;
2294 my $query = "
2295 UPDATE aqorders SET
2296 claims_count = claims_count + 1,
2297 claimed_date = CURDATE()
2298 WHERE ordernumber = ?
2300 my $sth = $dbh->prepare($query);
2301 $sth->execute($ordernumber);
2304 =head3 GetInvoices
2306 my @invoices = GetInvoices(
2307 invoicenumber => $invoicenumber,
2308 supplierid => $supplierid,
2309 suppliername => $suppliername,
2310 shipmentdatefrom => $shipmentdatefrom, # ISO format
2311 shipmentdateto => $shipmentdateto, # ISO format
2312 billingdatefrom => $billingdatefrom, # ISO format
2313 billingdateto => $billingdateto, # ISO format
2314 isbneanissn => $isbn_or_ean_or_issn,
2315 title => $title,
2316 author => $author,
2317 publisher => $publisher,
2318 publicationyear => $publicationyear,
2319 branchcode => $branchcode,
2320 order_by => $order_by
2323 Return a list of invoices that match all given criteria.
2325 $order_by is "column_name (asc|desc)", where column_name is any of
2326 'invoicenumber', 'booksellerid', 'shipmentdate', 'billingdate', 'closedate',
2327 'shipmentcost', 'shipmentcost_budgetid'.
2329 asc is the default if omitted
2331 =cut
2333 sub GetInvoices {
2334 my %args = @_;
2336 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2337 closedate shipmentcost shipmentcost_budgetid);
2339 my $dbh = C4::Context->dbh;
2340 my $query = qq{
2341 SELECT aqinvoices.invoiceid, aqinvoices.invoicenumber, aqinvoices.booksellerid, aqinvoices.shipmentdate, aqinvoices.billingdate, aqinvoices.closedate, aqinvoices.shipmentcost, aqinvoices.shipmentcost_budgetid, aqinvoices.message_id,
2342 aqbooksellers.name AS suppliername,
2343 COUNT(
2344 DISTINCT IF(
2345 aqorders.datereceived IS NOT NULL,
2346 aqorders.biblionumber,
2347 NULL
2349 ) AS receivedbiblios,
2350 COUNT(
2351 DISTINCT IF(
2352 aqorders.subscriptionid IS NOT NULL,
2353 aqorders.subscriptionid,
2354 NULL
2356 ) AS is_linked_to_subscriptions,
2357 SUM(aqorders.quantityreceived) AS receiveditems
2358 FROM aqinvoices
2359 LEFT JOIN aqbooksellers ON aqbooksellers.id = aqinvoices.booksellerid
2360 LEFT JOIN aqorders ON aqorders.invoiceid = aqinvoices.invoiceid
2361 LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
2362 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
2363 LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2364 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
2365 LEFT JOIN subscription ON biblio.biblionumber = subscription.biblionumber
2368 my @bind_args;
2369 my @bind_strs;
2370 if($args{supplierid}) {
2371 push @bind_strs, " aqinvoices.booksellerid = ? ";
2372 push @bind_args, $args{supplierid};
2374 if($args{invoicenumber}) {
2375 push @bind_strs, " aqinvoices.invoicenumber LIKE ? ";
2376 push @bind_args, "%$args{invoicenumber}%";
2378 if($args{suppliername}) {
2379 push @bind_strs, " aqbooksellers.name LIKE ? ";
2380 push @bind_args, "%$args{suppliername}%";
2382 if($args{shipmentdatefrom}) {
2383 push @bind_strs, " aqinvoices.shipmentdate >= ? ";
2384 push @bind_args, $args{shipmentdatefrom};
2386 if($args{shipmentdateto}) {
2387 push @bind_strs, " aqinvoices.shipmentdate <= ? ";
2388 push @bind_args, $args{shipmentdateto};
2390 if($args{billingdatefrom}) {
2391 push @bind_strs, " aqinvoices.billingdate >= ? ";
2392 push @bind_args, $args{billingdatefrom};
2394 if($args{billingdateto}) {
2395 push @bind_strs, " aqinvoices.billingdate <= ? ";
2396 push @bind_args, $args{billingdateto};
2398 if($args{isbneanissn}) {
2399 push @bind_strs, " (biblioitems.isbn LIKE CONCAT('%', ?, '%') OR biblioitems.ean LIKE CONCAT('%', ?, '%') OR biblioitems.issn LIKE CONCAT('%', ?, '%') ) ";
2400 push @bind_args, $args{isbneanissn}, $args{isbneanissn}, $args{isbneanissn};
2402 if($args{title}) {
2403 push @bind_strs, " biblio.title LIKE CONCAT('%', ?, '%') ";
2404 push @bind_args, $args{title};
2406 if($args{author}) {
2407 push @bind_strs, " biblio.author LIKE CONCAT('%', ?, '%') ";
2408 push @bind_args, $args{author};
2410 if($args{publisher}) {
2411 push @bind_strs, " biblioitems.publishercode LIKE CONCAT('%', ?, '%') ";
2412 push @bind_args, $args{publisher};
2414 if($args{publicationyear}) {
2415 push @bind_strs, " ((biblioitems.publicationyear LIKE CONCAT('%', ?, '%')) OR (biblio.copyrightdate LIKE CONCAT('%', ?, '%'))) ";
2416 push @bind_args, $args{publicationyear}, $args{publicationyear};
2418 if($args{branchcode}) {
2419 push @bind_strs, " borrowers.branchcode = ? ";
2420 push @bind_args, $args{branchcode};
2422 if($args{message_id}) {
2423 push @bind_strs, " aqinvoices.message_id = ? ";
2424 push @bind_args, $args{message_id};
2427 $query .= " WHERE " . join(" AND ", @bind_strs) if @bind_strs;
2428 $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";
2430 if($args{order_by}) {
2431 my ($column, $direction) = split / /, $args{order_by};
2432 if(grep { $_ eq $column } @columns) {
2433 $direction ||= 'ASC';
2434 $query .= " ORDER BY $column $direction";
2438 my $sth = $dbh->prepare($query);
2439 $sth->execute(@bind_args);
2441 my $results = $sth->fetchall_arrayref({});
2442 return @$results;
2445 =head3 GetInvoice
2447 my $invoice = GetInvoice($invoiceid);
2449 Get informations about invoice with given $invoiceid
2451 Return a hash filled with aqinvoices.* fields
2453 =cut
2455 sub GetInvoice {
2456 my ($invoiceid) = @_;
2457 my $invoice;
2459 return unless $invoiceid;
2461 my $dbh = C4::Context->dbh;
2462 my $query = qq{
2463 SELECT *
2464 FROM aqinvoices
2465 WHERE invoiceid = ?
2467 my $sth = $dbh->prepare($query);
2468 $sth->execute($invoiceid);
2470 $invoice = $sth->fetchrow_hashref;
2471 return $invoice;
2474 =head3 GetInvoiceDetails
2476 my $invoice = GetInvoiceDetails($invoiceid)
2478 Return informations about an invoice + the list of related order lines
2480 Orders informations are in $invoice->{orders} (array ref)
2482 =cut
2484 sub GetInvoiceDetails {
2485 my ($invoiceid) = @_;
2487 if ( !defined $invoiceid ) {
2488 carp 'GetInvoiceDetails called without an invoiceid';
2489 return;
2492 my $dbh = C4::Context->dbh;
2493 my $query = q{
2494 SELECT aqinvoices.*, aqbooksellers.name AS suppliername
2495 FROM aqinvoices
2496 LEFT JOIN aqbooksellers ON aqinvoices.booksellerid = aqbooksellers.id
2497 WHERE invoiceid = ?
2499 my $sth = $dbh->prepare($query);
2500 $sth->execute($invoiceid);
2502 my $invoice = $sth->fetchrow_hashref;
2504 $query = q{
2505 SELECT aqorders.*,
2506 biblio.*,
2507 biblio.copyrightdate,
2508 biblioitems.isbn,
2509 biblioitems.publishercode,
2510 biblioitems.publicationyear,
2511 aqbasket.basketname,
2512 aqbasketgroups.id AS basketgroupid,
2513 aqbasketgroups.name AS basketgroupname
2514 FROM aqorders
2515 LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
2516 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
2517 LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2518 LEFT JOIN biblioitems ON aqorders.biblionumber = biblioitems.biblionumber
2519 WHERE invoiceid = ?
2521 $sth = $dbh->prepare($query);
2522 $sth->execute($invoiceid);
2523 $invoice->{orders} = $sth->fetchall_arrayref({});
2524 $invoice->{orders} ||= []; # force an empty arrayref if fetchall_arrayref fails
2526 return $invoice;
2529 =head3 AddInvoice
2531 my $invoiceid = AddInvoice(
2532 invoicenumber => $invoicenumber,
2533 booksellerid => $booksellerid,
2534 shipmentdate => $shipmentdate,
2535 billingdate => $billingdate,
2536 closedate => $closedate,
2537 shipmentcost => $shipmentcost,
2538 shipmentcost_budgetid => $shipmentcost_budgetid
2541 Create a new invoice and return its id or undef if it fails.
2543 =cut
2545 sub AddInvoice {
2546 my %invoice = @_;
2548 return unless(%invoice and $invoice{invoicenumber});
2550 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2551 closedate shipmentcost shipmentcost_budgetid message_id);
2553 my @set_strs;
2554 my @set_args;
2555 foreach my $key (keys %invoice) {
2556 if(0 < grep { $_ eq $key } @columns) {
2557 push @set_strs, "$key = ?";
2558 push @set_args, ($invoice{$key} || undef);
2562 my $rv;
2563 if(@set_args > 0) {
2564 my $dbh = C4::Context->dbh;
2565 my $query = "INSERT INTO aqinvoices SET ";
2566 $query .= join (",", @set_strs);
2567 my $sth = $dbh->prepare($query);
2568 $rv = $sth->execute(@set_args);
2569 if($rv) {
2570 $rv = $dbh->last_insert_id(undef, undef, 'aqinvoices', undef);
2573 return $rv;
2576 =head3 ModInvoice
2578 ModInvoice(
2579 invoiceid => $invoiceid, # Mandatory
2580 invoicenumber => $invoicenumber,
2581 booksellerid => $booksellerid,
2582 shipmentdate => $shipmentdate,
2583 billingdate => $billingdate,
2584 closedate => $closedate,
2585 shipmentcost => $shipmentcost,
2586 shipmentcost_budgetid => $shipmentcost_budgetid
2589 Modify an invoice, invoiceid is mandatory.
2591 Return undef if it fails.
2593 =cut
2595 sub ModInvoice {
2596 my %invoice = @_;
2598 return unless(%invoice and $invoice{invoiceid});
2600 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2601 closedate shipmentcost shipmentcost_budgetid);
2603 my @set_strs;
2604 my @set_args;
2605 foreach my $key (keys %invoice) {
2606 if(0 < grep { $_ eq $key } @columns) {
2607 push @set_strs, "$key = ?";
2608 push @set_args, ($invoice{$key} || undef);
2612 my $dbh = C4::Context->dbh;
2613 my $query = "UPDATE aqinvoices SET ";
2614 $query .= join(",", @set_strs);
2615 $query .= " WHERE invoiceid = ?";
2617 my $sth = $dbh->prepare($query);
2618 $sth->execute(@set_args, $invoice{invoiceid});
2621 =head3 CloseInvoice
2623 CloseInvoice($invoiceid);
2625 Close an invoice.
2627 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => undef);
2629 =cut
2631 sub CloseInvoice {
2632 my ($invoiceid) = @_;
2634 return unless $invoiceid;
2636 my $dbh = C4::Context->dbh;
2637 my $query = qq{
2638 UPDATE aqinvoices
2639 SET closedate = CAST(NOW() AS DATE)
2640 WHERE invoiceid = ?
2642 my $sth = $dbh->prepare($query);
2643 $sth->execute($invoiceid);
2646 =head3 ReopenInvoice
2648 ReopenInvoice($invoiceid);
2650 Reopen an invoice
2652 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => output_pref({ dt=>dt_from_string, dateonly=>1, otputpref=>'iso' }))
2654 =cut
2656 sub ReopenInvoice {
2657 my ($invoiceid) = @_;
2659 return unless $invoiceid;
2661 my $dbh = C4::Context->dbh;
2662 my $query = qq{
2663 UPDATE aqinvoices
2664 SET closedate = NULL
2665 WHERE invoiceid = ?
2667 my $sth = $dbh->prepare($query);
2668 $sth->execute($invoiceid);
2671 =head3 DelInvoice
2673 DelInvoice($invoiceid);
2675 Delete an invoice if there are no items attached to it.
2677 =cut
2679 sub DelInvoice {
2680 my ($invoiceid) = @_;
2682 return unless $invoiceid;
2684 my $dbh = C4::Context->dbh;
2685 my $query = qq{
2686 SELECT COUNT(*)
2687 FROM aqorders
2688 WHERE invoiceid = ?
2690 my $sth = $dbh->prepare($query);
2691 $sth->execute($invoiceid);
2692 my $res = $sth->fetchrow_arrayref;
2693 if ( $res && $res->[0] == 0 ) {
2694 $query = qq{
2695 DELETE FROM aqinvoices
2696 WHERE invoiceid = ?
2698 my $sth = $dbh->prepare($query);
2699 return ( $sth->execute($invoiceid) > 0 );
2701 return;
2704 =head3 MergeInvoices
2706 MergeInvoices($invoiceid, \@sourceids);
2708 Merge the invoices identified by the IDs in \@sourceids into
2709 the invoice identified by $invoiceid.
2711 =cut
2713 sub MergeInvoices {
2714 my ($invoiceid, $sourceids) = @_;
2716 return unless $invoiceid;
2717 foreach my $sourceid (@$sourceids) {
2718 next if $sourceid == $invoiceid;
2719 my $source = GetInvoiceDetails($sourceid);
2720 foreach my $order (@{$source->{'orders'}}) {
2721 $order->{'invoiceid'} = $invoiceid;
2722 ModOrder($order);
2724 DelInvoice($source->{'invoiceid'});
2726 return;
2729 =head3 GetBiblioCountByBasketno
2731 $biblio_count = &GetBiblioCountByBasketno($basketno);
2733 Looks up the biblio's count that has basketno value $basketno
2735 Returns a quantity
2737 =cut
2739 sub GetBiblioCountByBasketno {
2740 my ($basketno) = @_;
2741 my $dbh = C4::Context->dbh;
2742 my $query = "
2743 SELECT COUNT( DISTINCT( biblionumber ) )
2744 FROM aqorders
2745 WHERE basketno = ?
2746 AND datecancellationprinted IS NULL
2749 my $sth = $dbh->prepare($query);
2750 $sth->execute($basketno);
2751 return $sth->fetchrow;
2754 =head3 populate_order_with_prices
2756 $order = populate_order_with_prices({
2757 order => $order #a hashref with the order values
2758 booksellerid => $booksellerid #FIXME - should obtain from order basket
2759 receiving => 1 # boolean representing order stage, should pass only this or ordering
2760 ordering => 1 # boolean representing order stage
2764 Sets calculated values for an order - all values are stored with full precision
2765 regardless of rounding preference except for tax value which is calculated
2766 on rounded values if requested
2768 For ordering the values set are:
2769 rrp_tax_included
2770 rrp_tax_excluded
2771 ecost_tax_included
2772 ecost_tax_excluded
2773 tax_value_on_ordering
2774 For receiving the value set are:
2775 unitprice_tax_included
2776 unitprice_tax_excluded
2777 tax_value_on_receiving
2779 Note: When receiving, if the rounded value of the unitprice matches the rounded
2780 value of the ecost then then ecost (full precision) is used.
2782 Returns a hashref of the order
2784 FIXME: Move this to Koha::Acquisition::Order.pm
2786 =cut
2788 sub populate_order_with_prices {
2789 my ($params) = @_;
2791 my $order = $params->{order};
2792 my $booksellerid = $params->{booksellerid};
2793 return unless $booksellerid;
2795 my $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
2797 my $receiving = $params->{receiving};
2798 my $ordering = $params->{ordering};
2799 my $discount = $order->{discount};
2800 $discount /= 100 if $discount > 1;
2802 if ($ordering) {
2803 $order->{tax_rate_on_ordering} //= $order->{tax_rate};
2804 if ( $bookseller->listincgst ) {
2806 # The user entered the prices tax included
2807 $order->{unitprice} += 0;
2808 $order->{unitprice_tax_included} = $order->{unitprice};
2809 $order->{rrp_tax_included} = $order->{rrp};
2811 # price tax excluded = price tax included / ( 1 + tax rate )
2812 $order->{unitprice_tax_excluded} = $order->{unitprice_tax_included} / ( 1 + $order->{tax_rate_on_ordering} );
2813 $order->{rrp_tax_excluded} = $order->{rrp_tax_included} / ( 1 + $order->{tax_rate_on_ordering} );
2815 # ecost tax included = rrp tax included ( 1 - discount )
2816 $order->{ecost_tax_included} = $order->{rrp_tax_included} * ( 1 - $discount );
2818 # ecost tax excluded = rrp tax excluded * ( 1 - discount )
2819 $order->{ecost_tax_excluded} = $order->{rrp_tax_excluded} * ( 1 - $discount );
2821 # tax value = quantity * ecost tax excluded * tax rate
2822 # we should use the unitprice if included
2823 my $cost_tax_included = $order->{unitprice_tax_included} || $order->{ecost_tax_included};
2824 my $cost_tax_excluded = $order->{unitprice_tax_excluded} || $order->{ecost_tax_excluded};
2825 $order->{tax_value_on_ordering} = ( get_rounded_price($cost_tax_included) - get_rounded_price($cost_tax_excluded) ) * $order->{quantity};
2828 else {
2829 # The user entered the prices tax excluded
2830 $order->{unitprice_tax_excluded} = $order->{unitprice};
2831 $order->{rrp_tax_excluded} = $order->{rrp};
2833 # price tax included = price tax excluded * ( 1 - tax rate )
2834 $order->{unitprice_tax_included} = $order->{unitprice_tax_excluded} * ( 1 + $order->{tax_rate_on_ordering} );
2835 $order->{rrp_tax_included} = $order->{rrp_tax_excluded} * ( 1 + $order->{tax_rate_on_ordering} );
2837 # ecost tax excluded = rrp tax excluded * ( 1 - discount )
2838 $order->{ecost_tax_excluded} = $order->{rrp_tax_excluded} * ( 1 - $discount );
2840 # ecost tax included = rrp tax excluded * ( 1 + tax rate ) * ( 1 - discount ) = ecost tax excluded * ( 1 + tax rate )
2841 $order->{ecost_tax_included} = $order->{ecost_tax_excluded} * ( 1 + $order->{tax_rate_on_ordering} );
2843 # tax value = quantity * ecost tax included * tax rate
2844 # we should use the unitprice if included
2845 my $cost_tax_excluded = $order->{unitprice_tax_excluded} || $order->{ecost_tax_excluded};
2846 $order->{tax_value_on_ordering} = $order->{quantity} * get_rounded_price($cost_tax_excluded) * $order->{tax_rate_on_ordering};
2850 if ($receiving) {
2851 $order->{tax_rate_on_receiving} //= $order->{tax_rate};
2852 if ( $bookseller->invoiceincgst ) {
2853 # Trick for unitprice. If the unit price rounded value is the same as the ecost rounded value
2854 # we need to keep the exact ecost value
2855 if ( Koha::Number::Price->new( $order->{unitprice} )->round == Koha::Number::Price->new( $order->{ecost_tax_included} )->round ) {
2856 $order->{unitprice} = $order->{ecost_tax_included};
2859 # The user entered the unit price tax included
2860 $order->{unitprice_tax_included} = $order->{unitprice};
2862 # unit price tax excluded = unit price tax included / ( 1 + tax rate )
2863 $order->{unitprice_tax_excluded} = $order->{unitprice_tax_included} / ( 1 + $order->{tax_rate_on_receiving} );
2865 else {
2866 # Trick for unitprice. If the unit price rounded value is the same as the ecost rounded value
2867 # we need to keep the exact ecost value
2868 if ( Koha::Number::Price->new( $order->{unitprice} )->round == Koha::Number::Price->new( $order->{ecost_tax_excluded} )->round ) {
2869 $order->{unitprice} = $order->{ecost_tax_excluded};
2872 # The user entered the unit price tax excluded
2873 $order->{unitprice_tax_excluded} = $order->{unitprice};
2876 # unit price tax included = unit price tax included * ( 1 + tax rate )
2877 $order->{unitprice_tax_included} = $order->{unitprice_tax_excluded} * ( 1 + $order->{tax_rate_on_receiving} );
2880 # tax value = quantity * unit price tax excluded * tax rate
2881 $order->{tax_value_on_receiving} = $order->{quantity} * get_rounded_price($order->{unitprice_tax_excluded}) * $order->{tax_rate_on_receiving};
2884 return $order;
2887 =head3 GetOrderUsers
2889 $order_users_ids = &GetOrderUsers($ordernumber);
2891 Returns a list of all borrowernumbers that are in order users list
2893 =cut
2895 sub GetOrderUsers {
2896 my ($ordernumber) = @_;
2898 return unless $ordernumber;
2900 my $query = q|
2901 SELECT borrowernumber
2902 FROM aqorder_users
2903 WHERE ordernumber = ?
2905 my $dbh = C4::Context->dbh;
2906 my $sth = $dbh->prepare($query);
2907 $sth->execute($ordernumber);
2908 my $results = $sth->fetchall_arrayref( {} );
2910 my @borrowernumbers;
2911 foreach (@$results) {
2912 push @borrowernumbers, $_->{'borrowernumber'};
2915 return @borrowernumbers;
2918 =head3 ModOrderUsers
2920 my @order_users_ids = (1, 2, 3);
2921 &ModOrderUsers($ordernumber, @basketusers_ids);
2923 Delete all users from order users list, and add users in C<@order_users_ids>
2924 to this users list.
2926 =cut
2928 sub ModOrderUsers {
2929 my ( $ordernumber, @order_users_ids ) = @_;
2931 return unless $ordernumber;
2933 my $dbh = C4::Context->dbh;
2934 my $query = q|
2935 DELETE FROM aqorder_users
2936 WHERE ordernumber = ?
2938 my $sth = $dbh->prepare($query);
2939 $sth->execute($ordernumber);
2941 $query = q|
2942 INSERT INTO aqorder_users (ordernumber, borrowernumber)
2943 VALUES (?, ?)
2945 $sth = $dbh->prepare($query);
2946 foreach my $order_user_id (@order_users_ids) {
2947 $sth->execute( $ordernumber, $order_user_id );
2951 sub NotifyOrderUsers {
2952 my ($ordernumber) = @_;
2954 my @borrowernumbers = GetOrderUsers($ordernumber);
2955 return unless @borrowernumbers;
2957 my $order = GetOrder( $ordernumber );
2958 for my $borrowernumber (@borrowernumbers) {
2959 my $patron = Koha::Patrons->find( $borrowernumber );
2960 my $library = $patron->library->unblessed;
2961 my $biblio = Koha::Biblios->find( $order->{biblionumber} )->unblessed;
2962 my $letter = C4::Letters::GetPreparedLetter(
2963 module => 'acquisition',
2964 letter_code => 'ACQ_NOTIF_ON_RECEIV',
2965 branchcode => $library->{branchcode},
2966 lang => $patron->lang,
2967 tables => {
2968 'branches' => $library,
2969 'borrowers' => $patron->unblessed,
2970 'biblio' => $biblio,
2971 'aqorders' => $order,
2974 if ( $letter ) {
2975 C4::Letters::EnqueueLetter(
2977 letter => $letter,
2978 borrowernumber => $borrowernumber,
2979 LibraryName => C4::Context->preference("LibraryName"),
2980 message_transport_type => 'email',
2982 ) or warn "can't enqueue letter $letter";
2987 =head3 FillWithDefaultValues
2989 FillWithDefaultValues( $marc_record, $params );
2991 This will update the record with default value defined in the ACQ framework.
2992 For all existing fields, if a default value exists and there are no subfield, it will be created.
2993 If the field does not exist, it will be created too.
2995 If the parameter only_mandatory => 1 is passed via $params, only the mandatory
2996 defaults are being applied to the record.
2998 =cut
3000 sub FillWithDefaultValues {
3001 my ( $record, $params ) = @_;
3002 my $mandatory = $params->{only_mandatory};
3003 my $tagslib = C4::Biblio::GetMarcStructure( 1, 'ACQ', { unsafe => 1 } );
3004 if ($tagslib) {
3005 my ($itemfield) =
3006 C4::Biblio::GetMarcFromKohaField( 'items.itemnumber' );
3007 for my $tag ( sort keys %$tagslib ) {
3008 next unless $tag;
3009 next if $tag == $itemfield;
3010 for my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
3011 next if IsMarcStructureInternal($tagslib->{$tag}{$subfield});
3012 next if $mandatory && !$tagslib->{$tag}{$subfield}{mandatory};
3013 my $defaultvalue = $tagslib->{$tag}{$subfield}{defaultvalue};
3014 if ( defined $defaultvalue and $defaultvalue ne '' ) {
3015 my @fields = $record->field($tag);
3016 if (@fields) {
3017 for my $field (@fields) {
3018 if ( $field->is_control_field ) {
3019 $field->update($defaultvalue) if not defined $field->data;
3021 elsif ( not defined $field->subfield($subfield) ) {
3022 $field->add_subfields(
3023 $subfield => $defaultvalue );
3027 else {
3028 if ( $tag < 10 ) { # is_control_field
3029 $record->insert_fields_ordered(
3030 MARC::Field->new(
3031 $tag, $defaultvalue
3035 else {
3036 $record->insert_fields_ordered(
3037 MARC::Field->new(
3038 $tag, '', '', $subfield => $defaultvalue
3050 __END__
3052 =head1 AUTHOR
3054 Koha Development Team <http://koha-community.org/>
3056 =cut