Bug 19134: C4::SMS falils on long driver name
[koha.git] / C4 / Acquisition.pm
blobebbe44b1066737430e39cdc952e73f75b7019057
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( q{UPDATE aqorders SET orderstatus = 'ordered' WHERE basketno = ? AND orderstatus != 'complete'},
244 {}, $basketno);
245 return;
248 =head3 ReopenBasket
250 &ReopenBasket($basketno);
252 reopen a basket
254 =cut
256 sub ReopenBasket {
257 my ($basketno) = @_;
258 my $dbh = C4::Context->dbh;
259 $dbh->do( q{UPDATE aqbasket SET closedate=NULL WHERE basketno=?}, {}, $basketno );
261 $dbh->do( q{
262 UPDATE aqorders
263 SET orderstatus = 'new'
264 WHERE basketno = ?
265 AND orderstatus != 'complete'
266 }, {}, $basketno);
267 return;
270 #------------------------------------------------------------#
272 =head3 GetBasketAsCSV
274 &GetBasketAsCSV($basketno);
276 Export a basket as CSV
278 $cgi parameter is needed for column name translation
280 =cut
282 sub GetBasketAsCSV {
283 my ($basketno, $cgi, $csv_profile_id) = @_;
284 my $basket = GetBasket($basketno);
285 my @orders = GetOrders($basketno);
286 my $contract = GetContract({
287 contractnumber => $basket->{'contractnumber'}
290 my $template = C4::Templates::gettemplate("acqui/csv/basket.tt", "intranet", $cgi);
291 my @rows;
292 if ($csv_profile_id) {
293 my $csv_profile = Koha::CsvProfiles->find( $csv_profile_id );
294 die "There is no valid csv profile given" unless $csv_profile;
296 my $csv = Text::CSV_XS->new({'quote_char'=>'"','escape_char'=>'"','sep_char'=>$csv_profile->csv_separator,'binary'=>1});
297 my $csv_profile_content = $csv_profile->content;
298 my ( @headers, @fields );
299 while ( $csv_profile_content =~ /
300 ([^=\|]+) # header
302 ([^\|]*) # fieldname (table.row or row)
303 \|? /gxms
305 my $header = $1;
306 my $field = ($2 eq '') ? $1 : $2;
308 $header =~ s/^\s+|\s+$//g; # Trim whitespaces
309 push @headers, $header;
311 $field =~ s/[^\.]*\.{1}//; # Remove the table name if exists.
312 $field =~ s/^\s+|\s+$//g; # Trim whitespaces
313 push @fields, $field;
315 for my $order (@orders) {
316 my @row;
317 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
318 my $biblioitem = $biblio->biblioitem;
319 $order = { %$order, %{ $biblioitem->unblessed } };
320 if ($contract) {
321 $order = {%$order, %$contract};
323 $order = {%$order, %$basket, %{ $biblio->unblessed }};
324 for my $field (@fields) {
325 push @row, $order->{$field};
327 push @rows, \@row;
329 my $content = join( $csv_profile->csv_separator, @headers ) . "\n";
330 for my $row ( @rows ) {
331 $csv->combine(@$row);
332 my $string = $csv->string;
333 $content .= $string . "\n";
335 return $content;
337 else {
338 foreach my $order (@orders) {
339 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
340 my $biblioitem = $biblio->biblioitem;
341 my $row = {
342 contractname => $contract->{'contractname'},
343 ordernumber => $order->{'ordernumber'},
344 entrydate => $order->{'entrydate'},
345 isbn => $order->{'isbn'},
346 author => $biblio->author,
347 title => $biblio->title,
348 publicationyear => $biblioitem->publicationyear,
349 publishercode => $biblioitem->publishercode,
350 collectiontitle => $biblioitem->collectiontitle,
351 notes => $order->{'order_vendornote'},
352 quantity => $order->{'quantity'},
353 rrp => $order->{'rrp'},
355 for my $place ( qw( deliveryplace billingplace ) ) {
356 if ( my $library = Koha::Libraries->find( $row->{deliveryplace} ) ) {
357 $row->{$place} = $library->branchname
360 foreach(qw(
361 contractname author title publishercode collectiontitle notes
362 deliveryplace billingplace
363 ) ) {
364 # Double the quotes to not be interpreted as a field end
365 $row->{$_} =~ s/"/""/g if $row->{$_};
367 push @rows, $row;
370 @rows = sort {
371 if(defined $a->{publishercode} and defined $b->{publishercode}) {
372 $a->{publishercode} cmp $b->{publishercode};
374 } @rows;
376 $template->param(rows => \@rows);
378 return $template->output;
383 =head3 GetBasketGroupAsCSV
385 &GetBasketGroupAsCSV($basketgroupid);
387 Export a basket group as CSV
389 $cgi parameter is needed for column name translation
391 =cut
393 sub GetBasketGroupAsCSV {
394 my ($basketgroupid, $cgi) = @_;
395 my $baskets = GetBasketsByBasketgroup($basketgroupid);
397 my $template = C4::Templates::gettemplate('acqui/csv/basketgroup.tt', 'intranet', $cgi);
399 my @rows;
400 for my $basket (@$baskets) {
401 my @orders = GetOrders( $basket->{basketno} );
402 my $contract = GetContract({
403 contractnumber => $basket->{contractnumber}
405 my $bookseller = Koha::Acquisition::Booksellers->find( $basket->{booksellerid} );
406 my $basketgroup = GetBasketgroup( $$basket{basketgroupid} );
408 foreach my $order (@orders) {
409 my $biblio = Koha::Biblios->find( $order->{biblionumber} );
410 my $biblioitem = $biblio->biblioitem;
411 my $row = {
412 clientnumber => $bookseller->accountnumber,
413 basketname => $basket->{basketname},
414 ordernumber => $order->{ordernumber},
415 author => $biblio->author,
416 title => $biblio->title,
417 publishercode => $biblioitem->publishercode,
418 publicationyear => $biblioitem->publicationyear,
419 collectiontitle => $biblioitem->collectiontitle,
420 isbn => $order->{isbn},
421 quantity => $order->{quantity},
422 rrp_tax_included => $order->{rrp_tax_included},
423 rrp_tax_excluded => $order->{rrp_tax_excluded},
424 discount => $bookseller->discount,
425 ecost_tax_included => $order->{ecost_tax_included},
426 ecost_tax_excluded => $order->{ecost_tax_excluded},
427 notes => $order->{order_vendornote},
428 entrydate => $order->{entrydate},
429 booksellername => $bookseller->name,
430 bookselleraddress => $bookseller->address1,
431 booksellerpostal => $bookseller->postal,
432 contractnumber => $contract->{contractnumber},
433 contractname => $contract->{contractname},
435 my $temp = {
436 basketgroupdeliveryplace => $basketgroup->{deliveryplace},
437 basketgroupbillingplace => $basketgroup->{billingplace},
438 basketdeliveryplace => $basket->{deliveryplace},
439 basketbillingplace => $basket->{billingplace},
441 for my $place (qw( basketgroupdeliveryplace basketgroupbillingplace basketdeliveryplace basketbillingplace )) {
442 if ( my $library = Koha::Libraries->find( $temp->{$place} ) ) {
443 $row->{$place} = $library->branchname;
446 foreach(qw(
447 basketname author title publishercode collectiontitle notes
448 booksellername bookselleraddress booksellerpostal contractname
449 basketgroupdeliveryplace basketgroupbillingplace
450 basketdeliveryplace basketbillingplace
451 ) ) {
452 # Double the quotes to not be interpreted as a field end
453 $row->{$_} =~ s/"/""/g if $row->{$_};
455 push @rows, $row;
458 $template->param(rows => \@rows);
460 return $template->output;
464 =head3 CloseBasketgroup
466 &CloseBasketgroup($basketgroupno);
468 close a basketgroup
470 =cut
472 sub CloseBasketgroup {
473 my ($basketgroupno) = @_;
474 my $dbh = C4::Context->dbh;
475 my $sth = $dbh->prepare("
476 UPDATE aqbasketgroups
477 SET closed=1
478 WHERE id=?
480 $sth->execute($basketgroupno);
483 #------------------------------------------------------------#
485 =head3 ReOpenBaskergroup($basketgroupno)
487 &ReOpenBaskergroup($basketgroupno);
489 reopen a basketgroup
491 =cut
493 sub ReOpenBasketgroup {
494 my ($basketgroupno) = @_;
495 my $dbh = C4::Context->dbh;
496 my $sth = $dbh->prepare("
497 UPDATE aqbasketgroups
498 SET closed=0
499 WHERE id=?
501 $sth->execute($basketgroupno);
504 #------------------------------------------------------------#
507 =head3 DelBasket
509 &DelBasket($basketno);
511 Deletes the basket that has basketno field $basketno in the aqbasket table.
513 =over
515 =item C<$basketno> is the primary key of the basket in the aqbasket table.
517 =back
519 =cut
521 sub DelBasket {
522 my ( $basketno ) = @_;
523 my $query = "DELETE FROM aqbasket WHERE basketno=?";
524 my $dbh = C4::Context->dbh;
525 my $sth = $dbh->prepare($query);
526 $sth->execute($basketno);
527 return;
530 #------------------------------------------------------------#
532 =head3 ModBasket
534 &ModBasket($basketinfo);
536 Modifies a basket, using a hashref $basketinfo for the relevant information, only $basketinfo->{'basketno'} is required.
538 =over
540 =item C<$basketno> is the primary key of the basket in the aqbasket table.
542 =back
544 =cut
546 sub ModBasket {
547 my $basketinfo = shift;
548 my $query = "UPDATE aqbasket SET ";
549 my @params;
550 foreach my $key (keys %$basketinfo){
551 if ($key ne 'basketno'){
552 $query .= "$key=?, ";
553 push(@params, $basketinfo->{$key} || undef );
556 # get rid of the "," at the end of $query
557 if (substr($query, length($query)-2) eq ', '){
558 chop($query);
559 chop($query);
560 $query .= ' ';
562 $query .= "WHERE basketno=?";
563 push(@params, $basketinfo->{'basketno'});
564 my $dbh = C4::Context->dbh;
565 my $sth = $dbh->prepare($query);
566 $sth->execute(@params);
568 return;
571 #------------------------------------------------------------#
573 =head3 ModBasketHeader
575 &ModBasketHeader($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid);
577 Modifies a basket's header.
579 =over
581 =item C<$basketno> is the "basketno" field in the "aqbasket" table;
583 =item C<$basketname> is the "basketname" field in the "aqbasket" table;
585 =item C<$note> is the "note" field in the "aqbasket" table;
587 =item C<$booksellernote> is the "booksellernote" field in the "aqbasket" table;
589 =item C<$contractnumber> is the "contractnumber" (foreign) key in the "aqbasket" table.
591 =item C<$booksellerid> is the id (foreign) key in the "aqbooksellers" table for the vendor.
593 =item C<$deliveryplace> is the "deliveryplace" field in the aqbasket table.
595 =item C<$billingplace> is the "billingplace" field in the aqbasket table.
597 =item C<$is_standing> is the "is_standing" field in the aqbasket table.
599 =back
601 =cut
603 sub ModBasketHeader {
604 my ($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid, $deliveryplace, $billingplace, $is_standing) = @_;
605 my $query = qq{
606 UPDATE aqbasket
607 SET basketname=?, note=?, booksellernote=?, booksellerid=?, deliveryplace=?, billingplace=?, is_standing=?
608 WHERE basketno=?
611 my $dbh = C4::Context->dbh;
612 my $sth = $dbh->prepare($query);
613 $sth->execute($basketname, $note, $booksellernote, $booksellerid, $deliveryplace, $billingplace, $is_standing, $basketno);
615 if ( $contractnumber ) {
616 my $query2 ="UPDATE aqbasket SET contractnumber=? WHERE basketno=?";
617 my $sth2 = $dbh->prepare($query2);
618 $sth2->execute($contractnumber,$basketno);
620 return;
623 #------------------------------------------------------------#
625 =head3 GetBasketsByBookseller
627 @results = &GetBasketsByBookseller($booksellerid, $extra);
629 Returns a list of hashes of all the baskets that belong to bookseller 'booksellerid'.
631 =over
633 =item C<$booksellerid> is the 'id' field of the bookseller in the aqbooksellers table
635 =item C<$extra> is the extra sql parameters, can be
637 $extra->{groupby}: group baskets by column
638 ex. $extra->{groupby} = aqbasket.basketgroupid
639 $extra->{orderby}: order baskets by column
640 $extra->{limit}: limit number of results (can be helpful for pagination)
642 =back
644 =cut
646 sub GetBasketsByBookseller {
647 my ($booksellerid, $extra) = @_;
648 my $query = "SELECT * FROM aqbasket WHERE booksellerid=?";
649 if ($extra){
650 if ($extra->{groupby}) {
651 $query .= " GROUP by $extra->{groupby}";
653 if ($extra->{orderby}){
654 $query .= " ORDER by $extra->{orderby}";
656 if ($extra->{limit}){
657 $query .= " LIMIT $extra->{limit}";
660 my $dbh = C4::Context->dbh;
661 my $sth = $dbh->prepare($query);
662 $sth->execute($booksellerid);
663 return $sth->fetchall_arrayref({});
666 =head3 GetBasketsInfosByBookseller
668 my $baskets = GetBasketsInfosByBookseller($supplierid, $allbaskets);
670 The optional second parameter allbaskets is a boolean allowing you to
671 select all baskets from the supplier; by default only active baskets (open or
672 closed but still something to receive) are returned.
674 Returns in a arrayref of hashref all about booksellers baskets, plus:
675 total_biblios: Number of distinct biblios in basket
676 total_items: Number of items in basket
677 expected_items: Number of non-received items in basket
679 =cut
681 sub GetBasketsInfosByBookseller {
682 my ($supplierid, $allbaskets) = @_;
684 return unless $supplierid;
686 my $dbh = C4::Context->dbh;
687 my $query = q{
688 SELECT aqbasket.*,
689 SUM(aqorders.quantity) AS total_items,
690 SUM(
691 IF ( aqorders.orderstatus = 'cancelled', aqorders.quantity, 0 )
692 ) AS total_items_cancelled,
693 COUNT(DISTINCT aqorders.biblionumber) AS total_biblios,
694 SUM(
695 IF(aqorders.datereceived IS NULL
696 AND aqorders.datecancellationprinted IS NULL
697 , aqorders.quantity
698 , 0)
699 ) AS expected_items
700 FROM aqbasket
701 LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
702 WHERE booksellerid = ?};
704 unless ( $allbaskets ) {
705 $query.=" AND (closedate IS NULL OR (aqorders.quantity > aqorders.quantityreceived AND datecancellationprinted IS NULL))";
707 $query.=" GROUP BY aqbasket.basketno";
709 my $sth = $dbh->prepare($query);
710 $sth->execute($supplierid);
711 my $baskets = $sth->fetchall_arrayref({});
713 # Retrieve the number of biblios cancelled
714 my $cancelled_biblios = $dbh->selectall_hashref( q|
715 SELECT COUNT(DISTINCT(biblionumber)) AS total_biblios_cancelled, aqbasket.basketno
716 FROM aqbasket
717 LEFT JOIN aqorders ON aqorders.basketno = aqbasket.basketno
718 WHERE booksellerid = ?
719 AND aqorders.orderstatus = 'cancelled'
720 GROUP BY aqbasket.basketno
721 |, 'basketno', {}, $supplierid );
722 map {
723 $_->{total_biblios_cancelled} = $cancelled_biblios->{$_->{basketno}}{total_biblios_cancelled} || 0
724 } @$baskets;
726 return $baskets;
729 =head3 GetBasketUsers
731 $basketusers_ids = &GetBasketUsers($basketno);
733 Returns a list of all borrowernumbers that are in basket users list
735 =cut
737 sub GetBasketUsers {
738 my $basketno = shift;
740 return unless $basketno;
742 my $query = qq{
743 SELECT borrowernumber
744 FROM aqbasketusers
745 WHERE basketno = ?
747 my $dbh = C4::Context->dbh;
748 my $sth = $dbh->prepare($query);
749 $sth->execute($basketno);
750 my $results = $sth->fetchall_arrayref( {} );
752 my @borrowernumbers;
753 foreach (@$results) {
754 push @borrowernumbers, $_->{'borrowernumber'};
757 return @borrowernumbers;
760 =head3 ModBasketUsers
762 my @basketusers_ids = (1, 2, 3);
763 &ModBasketUsers($basketno, @basketusers_ids);
765 Delete all users from basket users list, and add users in C<@basketusers_ids>
766 to this users list.
768 =cut
770 sub ModBasketUsers {
771 my ($basketno, @basketusers_ids) = @_;
773 return unless $basketno;
775 my $dbh = C4::Context->dbh;
776 my $query = qq{
777 DELETE FROM aqbasketusers
778 WHERE basketno = ?
780 my $sth = $dbh->prepare($query);
781 $sth->execute($basketno);
783 $query = qq{
784 INSERT INTO aqbasketusers (basketno, borrowernumber)
785 VALUES (?, ?)
787 $sth = $dbh->prepare($query);
788 foreach my $basketuser_id (@basketusers_ids) {
789 $sth->execute($basketno, $basketuser_id);
791 return;
794 =head3 CanUserManageBasket
796 my $bool = CanUserManageBasket($borrower, $basket[, $userflags]);
797 my $bool = CanUserManageBasket($borrowernumber, $basketno[, $userflags]);
799 Check if a borrower can manage a basket, according to system preference
800 AcqViewBaskets, user permissions and basket properties (creator, users list,
801 branch).
803 First parameter can be either a borrowernumber or a hashref as returned by
804 Koha::Patron->unblessed
806 Second parameter can be either a basketno or a hashref as returned by
807 C4::Acquisition::GetBasket.
809 The third parameter is optional. If given, it should be a hashref as returned
810 by C4::Auth::getuserflags. If not, getuserflags is called.
812 If user is authorised to manage basket, returns 1.
813 Otherwise returns 0.
815 =cut
817 sub CanUserManageBasket {
818 my ($borrower, $basket, $userflags) = @_;
820 if (!ref $borrower) {
821 # FIXME This needs to be replaced
822 # We should not accept both scalar and array
823 # Tests need to be updated
824 $borrower = Koha::Patrons->find( $borrower )->unblessed;
826 if (!ref $basket) {
827 $basket = GetBasket($basket);
830 return 0 unless ($basket and $borrower);
832 my $borrowernumber = $borrower->{borrowernumber};
833 my $basketno = $basket->{basketno};
835 my $AcqViewBaskets = C4::Context->preference('AcqViewBaskets');
837 if (!defined $userflags) {
838 my $dbh = C4::Context->dbh;
839 my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE borrowernumber = ?");
840 $sth->execute($borrowernumber);
841 my ($flags) = $sth->fetchrow_array;
842 $sth->finish;
844 $userflags = C4::Auth::getuserflags($flags, $borrower->{userid}, $dbh);
847 unless ($userflags->{superlibrarian}
848 || (ref $userflags->{acquisition} && $userflags->{acquisition}->{order_manage_all})
849 || (!ref $userflags->{acquisition} && $userflags->{acquisition}))
851 if (not exists $userflags->{acquisition}) {
852 return 0;
855 if ( (ref $userflags->{acquisition} && !$userflags->{acquisition}->{order_manage})
856 || (!ref $userflags->{acquisition} && !$userflags->{acquisition}) ) {
857 return 0;
860 if ($AcqViewBaskets eq 'user'
861 && $basket->{authorisedby} != $borrowernumber
862 && ! grep { $borrowernumber eq $_ } GetBasketUsers($basketno)) {
863 return 0;
866 if ($AcqViewBaskets eq 'branch' && defined $basket->{branch}
867 && $basket->{branch} ne $borrower->{branchcode}) {
868 return 0;
872 return 1;
875 #------------------------------------------------------------#
877 =head3 GetBasketsByBasketgroup
879 $baskets = &GetBasketsByBasketgroup($basketgroupid);
881 Returns a reference to all baskets that belong to basketgroup $basketgroupid.
883 =cut
885 sub GetBasketsByBasketgroup {
886 my $basketgroupid = shift;
887 my $query = qq{
888 SELECT *, aqbasket.booksellerid as booksellerid
889 FROM aqbasket
890 LEFT JOIN aqcontract USING(contractnumber) WHERE basketgroupid=?
892 my $dbh = C4::Context->dbh;
893 my $sth = $dbh->prepare($query);
894 $sth->execute($basketgroupid);
895 return $sth->fetchall_arrayref({});
898 #------------------------------------------------------------#
900 =head3 NewBasketgroup
902 $basketgroupid = NewBasketgroup(\%hashref);
904 Adds a basketgroup to the aqbasketgroups table, and add the initial baskets to it.
906 $hashref->{'booksellerid'} is the 'id' field of the bookseller in the aqbooksellers table,
908 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
910 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
912 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
914 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
916 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
918 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
920 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
922 =cut
924 sub NewBasketgroup {
925 my $basketgroupinfo = shift;
926 die "booksellerid is required to create a basketgroup" unless $basketgroupinfo->{'booksellerid'};
927 my $query = "INSERT INTO aqbasketgroups (";
928 my @params;
929 foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
930 if ( defined $basketgroupinfo->{$field} ) {
931 $query .= "$field, ";
932 push(@params, $basketgroupinfo->{$field});
935 $query .= "booksellerid) VALUES (";
936 foreach (@params) {
937 $query .= "?, ";
939 $query .= "?)";
940 push(@params, $basketgroupinfo->{'booksellerid'});
941 my $dbh = C4::Context->dbh;
942 my $sth = $dbh->prepare($query);
943 $sth->execute(@params);
944 my $basketgroupid = $dbh->{'mysql_insertid'};
945 if( $basketgroupinfo->{'basketlist'} ) {
946 foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
947 my $query2 = "UPDATE aqbasket SET basketgroupid=? WHERE basketno=?";
948 my $sth2 = $dbh->prepare($query2);
949 $sth2->execute($basketgroupid, $basketno);
952 return $basketgroupid;
955 #------------------------------------------------------------#
957 =head3 ModBasketgroup
959 ModBasketgroup(\%hashref);
961 Modifies a basketgroup in the aqbasketgroups table, and add the baskets to it.
963 $hashref->{'id'} is the 'id' field of the basketgroup in the aqbasketgroup table, this parameter is mandatory,
965 $hashref->{'name'} is the 'name' field of the basketgroup in the aqbasketgroups table,
967 $hashref->{'basketlist'} is a list reference of the 'id's of the baskets that belong to this group,
969 $hashref->{'billingplace'} is the 'billingplace' field of the basketgroup in the aqbasketgroups table,
971 $hashref->{'deliveryplace'} is the 'deliveryplace' field of the basketgroup in the aqbasketgroups table,
973 $hashref->{'freedeliveryplace'} is the 'freedeliveryplace' field of the basketgroup in the aqbasketgroups table,
975 $hashref->{'deliverycomment'} is the 'deliverycomment' field of the basketgroup in the aqbasketgroups table,
977 $hashref->{'closed'} is the 'closed' field of the aqbasketgroups table, it is false if 0, true otherwise.
979 =cut
981 sub ModBasketgroup {
982 my $basketgroupinfo = shift;
983 die "basketgroup id is required to edit a basketgroup" unless $basketgroupinfo->{'id'};
984 my $dbh = C4::Context->dbh;
985 my $query = "UPDATE aqbasketgroups SET ";
986 my @params;
987 foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
988 if ( defined $basketgroupinfo->{$field} ) {
989 $query .= "$field=?, ";
990 push(@params, $basketgroupinfo->{$field});
993 chop($query);
994 chop($query);
995 $query .= " WHERE id=?";
996 push(@params, $basketgroupinfo->{'id'});
997 my $sth = $dbh->prepare($query);
998 $sth->execute(@params);
1000 $sth = $dbh->prepare('UPDATE aqbasket SET basketgroupid = NULL WHERE basketgroupid = ?');
1001 $sth->execute($basketgroupinfo->{'id'});
1003 if($basketgroupinfo->{'basketlist'} && @{$basketgroupinfo->{'basketlist'}}){
1004 $sth = $dbh->prepare("UPDATE aqbasket SET basketgroupid=? WHERE basketno=?");
1005 foreach my $basketno (@{$basketgroupinfo->{'basketlist'}}) {
1006 $sth->execute($basketgroupinfo->{'id'}, $basketno);
1009 return;
1012 #------------------------------------------------------------#
1014 =head3 DelBasketgroup
1016 DelBasketgroup($basketgroupid);
1018 Deletes a basketgroup in the aqbasketgroups table, and removes the reference to it from the baskets,
1020 =over
1022 =item C<$basketgroupid> is the 'id' field of the basket in the aqbasketgroup table
1024 =back
1026 =cut
1028 sub DelBasketgroup {
1029 my $basketgroupid = shift;
1030 die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
1031 my $query = "DELETE FROM aqbasketgroups WHERE id=?";
1032 my $dbh = C4::Context->dbh;
1033 my $sth = $dbh->prepare($query);
1034 $sth->execute($basketgroupid);
1035 return;
1038 #------------------------------------------------------------#
1041 =head2 FUNCTIONS ABOUT ORDERS
1043 =head3 GetBasketgroup
1045 $basketgroup = &GetBasketgroup($basketgroupid);
1047 Returns a reference to the hash containing all information about the basketgroup.
1049 =cut
1051 sub GetBasketgroup {
1052 my $basketgroupid = shift;
1053 die "basketgroup id is required to edit a basketgroup" unless $basketgroupid;
1054 my $dbh = C4::Context->dbh;
1055 my $result_set = $dbh->selectall_arrayref(
1056 'SELECT * FROM aqbasketgroups WHERE id=?',
1057 { Slice => {} },
1058 $basketgroupid
1060 return $result_set->[0]; # id is unique
1063 #------------------------------------------------------------#
1065 =head3 GetBasketgroups
1067 $basketgroups = &GetBasketgroups($booksellerid);
1069 Returns a reference to the array of all the basketgroups of bookseller $booksellerid.
1071 =cut
1073 sub GetBasketgroups {
1074 my $booksellerid = shift;
1075 die 'bookseller id is required to edit a basketgroup' unless $booksellerid;
1076 my $query = 'SELECT * FROM aqbasketgroups WHERE booksellerid=? ORDER BY id DESC';
1077 my $dbh = C4::Context->dbh;
1078 my $sth = $dbh->prepare($query);
1079 $sth->execute($booksellerid);
1080 return $sth->fetchall_arrayref({});
1083 #------------------------------------------------------------#
1085 =head2 FUNCTIONS ABOUT ORDERS
1087 =head3 GetOrders
1089 @orders = &GetOrders( $basketno, { orderby => 'biblio.title', cancelled => 0|1 } );
1091 Looks up the pending (non-cancelled) orders with the given basket
1092 number.
1094 If cancelled is set, only cancelled orders will be returned.
1096 =cut
1098 sub GetOrders {
1099 my ( $basketno, $params ) = @_;
1101 return () unless $basketno;
1103 my $orderby = $params->{orderby};
1104 my $cancelled = $params->{cancelled} || 0;
1106 my $dbh = C4::Context->dbh;
1107 my $query = q|
1108 SELECT biblio.*,biblioitems.*,
1109 aqorders.*,
1110 aqbudgets.*,
1112 $query .= $cancelled
1113 ? q|
1114 aqorders_transfers.ordernumber_to AS transferred_to,
1115 aqorders_transfers.timestamp AS transferred_to_timestamp
1117 : q|
1118 aqorders_transfers.ordernumber_from AS transferred_from,
1119 aqorders_transfers.timestamp AS transferred_from_timestamp
1121 $query .= q|
1122 FROM aqorders
1123 LEFT JOIN aqbudgets ON aqbudgets.budget_id = aqorders.budget_id
1124 LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1125 LEFT JOIN biblioitems ON biblioitems.biblionumber =biblio.biblionumber
1127 $query .= $cancelled
1128 ? q|
1129 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_from = aqorders.ordernumber
1131 : q|
1132 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1135 $query .= q|
1136 WHERE basketno=?
1139 if ($cancelled) {
1140 $orderby ||= q|biblioitems.publishercode, biblio.title|;
1141 $query .= q|
1142 AND (datecancellationprinted IS NOT NULL
1143 AND datecancellationprinted <> '0000-00-00')
1146 else {
1147 $orderby ||=
1148 q|aqorders.datecancellationprinted desc, aqorders.timestamp desc|;
1149 $query .= q|
1150 AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')
1154 $query .= " ORDER BY $orderby";
1155 my $orders =
1156 $dbh->selectall_arrayref( $query, { Slice => {} }, $basketno );
1157 return @{$orders};
1161 #------------------------------------------------------------#
1163 =head3 GetOrdersByBiblionumber
1165 @orders = &GetOrdersByBiblionumber($biblionumber);
1167 Looks up the orders with linked to a specific $biblionumber, including
1168 cancelled orders and received orders.
1170 return :
1171 C<@orders> is an array of references-to-hash, whose keys are the
1172 fields from the aqorders, biblio, and biblioitems tables in the Koha database.
1174 =cut
1176 sub GetOrdersByBiblionumber {
1177 my $biblionumber = shift;
1178 return unless $biblionumber;
1179 my $dbh = C4::Context->dbh;
1180 my $query ="
1181 SELECT biblio.*,biblioitems.*,
1182 aqorders.*,
1183 aqbudgets.*
1184 FROM aqorders
1185 LEFT JOIN aqbudgets ON aqbudgets.budget_id = aqorders.budget_id
1186 LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1187 LEFT JOIN biblioitems ON biblioitems.biblionumber =biblio.biblionumber
1188 WHERE aqorders.biblionumber=?
1190 my $result_set =
1191 $dbh->selectall_arrayref( $query, { Slice => {} }, $biblionumber );
1192 return @{$result_set};
1196 #------------------------------------------------------------#
1198 =head3 GetOrder
1200 $order = &GetOrder($ordernumber);
1202 Looks up an order by order number.
1204 Returns a reference-to-hash describing the order. The keys of
1205 C<$order> are fields from the biblio, biblioitems, aqorders tables of the Koha database.
1207 =cut
1209 sub GetOrder {
1210 my ($ordernumber) = @_;
1211 return unless $ordernumber;
1213 my $dbh = C4::Context->dbh;
1214 my $query = qq{SELECT
1215 aqorders.*,
1216 biblio.title,
1217 biblio.author,
1218 aqbasket.basketname,
1219 borrowers.branchcode,
1220 biblioitems.publicationyear,
1221 biblio.copyrightdate,
1222 biblioitems.editionstatement,
1223 biblioitems.isbn,
1224 biblioitems.ean,
1225 biblio.seriestitle,
1226 biblioitems.publishercode,
1227 aqorders.rrp AS unitpricesupplier,
1228 aqorders.ecost AS unitpricelib,
1229 aqorders.claims_count AS claims_count,
1230 aqorders.claimed_date AS claimed_date,
1231 aqbudgets.budget_name AS budget,
1232 aqbooksellers.name AS supplier,
1233 aqbooksellers.id AS supplierid,
1234 biblioitems.publishercode AS publisher,
1235 ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
1236 DATE(aqbasket.closedate) AS orderdate,
1237 aqorders.quantity - COALESCE(aqorders.quantityreceived,0) AS quantity_to_receive,
1238 (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
1239 DATEDIFF(CURDATE( ),closedate) AS latesince
1240 FROM aqorders LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
1241 LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
1242 LEFT JOIN aqbudgets ON aqorders.budget_id = aqbudgets.budget_id,
1243 aqbasket LEFT JOIN borrowers ON aqbasket.authorisedby = borrowers.borrowernumber
1244 LEFT JOIN aqbooksellers ON aqbasket.booksellerid = aqbooksellers.id
1245 WHERE aqorders.basketno = aqbasket.basketno
1246 AND ordernumber=?};
1247 my $result_set =
1248 $dbh->selectall_arrayref( $query, { Slice => {} }, $ordernumber );
1250 # result_set assumed to contain 1 match
1251 return $result_set->[0];
1254 =head3 GetLastOrderNotReceivedFromSubscriptionid
1256 $order = &GetLastOrderNotReceivedFromSubscriptionid($subscriptionid);
1258 Returns a reference-to-hash describing the last order not received for a subscription.
1260 =cut
1262 sub GetLastOrderNotReceivedFromSubscriptionid {
1263 my ( $subscriptionid ) = @_;
1264 my $dbh = C4::Context->dbh;
1265 my $query = qq|
1266 SELECT * FROM aqorders
1267 LEFT JOIN subscription
1268 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1269 WHERE aqorders.subscriptionid = ?
1270 AND aqorders.datereceived IS NULL
1271 LIMIT 1
1273 my $result_set =
1274 $dbh->selectall_arrayref( $query, { Slice => {} }, $subscriptionid );
1276 # result_set assumed to contain 1 match
1277 return $result_set->[0];
1280 =head3 GetLastOrderReceivedFromSubscriptionid
1282 $order = &GetLastOrderReceivedFromSubscriptionid($subscriptionid);
1284 Returns a reference-to-hash describing the last order received for a subscription.
1286 =cut
1288 sub GetLastOrderReceivedFromSubscriptionid {
1289 my ( $subscriptionid ) = @_;
1290 my $dbh = C4::Context->dbh;
1291 my $query = qq|
1292 SELECT * FROM aqorders
1293 LEFT JOIN subscription
1294 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1295 WHERE aqorders.subscriptionid = ?
1296 AND aqorders.datereceived =
1298 SELECT MAX( aqorders.datereceived )
1299 FROM aqorders
1300 LEFT JOIN subscription
1301 ON ( aqorders.subscriptionid = subscription.subscriptionid )
1302 WHERE aqorders.subscriptionid = ?
1303 AND aqorders.datereceived IS NOT NULL
1305 ORDER BY ordernumber DESC
1306 LIMIT 1
1308 my $result_set =
1309 $dbh->selectall_arrayref( $query, { Slice => {} }, $subscriptionid, $subscriptionid );
1311 # result_set assumed to contain 1 match
1312 return $result_set->[0];
1316 #------------------------------------------------------------#
1318 =head3 ModOrder
1320 &ModOrder(\%hashref);
1322 Modifies an existing order. Updates the order with order number
1323 $hashref->{'ordernumber'} and biblionumber $hashref->{'biblionumber'}. All
1324 other keys of the hash update the fields with the same name in the aqorders
1325 table of the Koha database.
1327 =cut
1329 sub ModOrder {
1330 my $orderinfo = shift;
1332 die "Ordernumber is required" if $orderinfo->{'ordernumber'} eq '';
1334 my $dbh = C4::Context->dbh;
1335 my @params;
1337 # update uncertainprice to an integer, just in case (under FF, checked boxes have the value "ON" by default)
1338 $orderinfo->{uncertainprice}=1 if $orderinfo->{uncertainprice};
1340 # delete($orderinfo->{'branchcode'});
1341 # the hash contains a lot of entries not in aqorders, so get the columns ...
1342 my $sth = $dbh->prepare("SELECT * FROM aqorders LIMIT 1;");
1343 $sth->execute;
1344 my $colnames = $sth->{NAME};
1345 #FIXME Be careful. If aqorders would have columns with diacritics,
1346 #you should need to decode what you get back from NAME.
1347 #See report 10110 and guided_reports.pl
1348 my $query = "UPDATE aqorders SET ";
1350 foreach my $orderinfokey (grep(!/ordernumber/, keys %$orderinfo)){
1351 # ... and skip hash entries that are not in the aqorders table
1352 # FIXME : probably not the best way to do it (would be better to have a correct hash)
1353 next unless grep(/^$orderinfokey$/, @$colnames);
1354 $query .= "$orderinfokey=?, ";
1355 push(@params, $orderinfo->{$orderinfokey});
1358 $query .= "timestamp=NOW() WHERE ordernumber=?";
1359 push(@params, $orderinfo->{'ordernumber'} );
1360 $sth = $dbh->prepare($query);
1361 $sth->execute(@params);
1362 return;
1365 #------------------------------------------------------------#
1367 =head3 ModItemOrder
1369 ModItemOrder($itemnumber, $ordernumber);
1371 Modifies the ordernumber of an item in aqorders_items.
1373 =cut
1375 sub ModItemOrder {
1376 my ($itemnumber, $ordernumber) = @_;
1378 return unless ($itemnumber and $ordernumber);
1380 my $dbh = C4::Context->dbh;
1381 my $query = qq{
1382 UPDATE aqorders_items
1383 SET ordernumber = ?
1384 WHERE itemnumber = ?
1386 my $sth = $dbh->prepare($query);
1387 return $sth->execute($ordernumber, $itemnumber);
1390 #------------------------------------------------------------#
1392 =head3 ModReceiveOrder
1394 my ( $date_received, $new_ordernumber ) = ModReceiveOrder(
1396 biblionumber => $biblionumber,
1397 order => $order,
1398 quantityreceived => $quantityreceived,
1399 user => $user,
1400 invoice => $invoice,
1401 budget_id => $budget_id,
1402 received_itemnumbers => \@received_itemnumbers,
1403 order_internalnote => $order_internalnote,
1407 Updates an order, to reflect the fact that it was received, at least
1408 in part.
1410 If a partial order is received, splits the order into two.
1412 Updates the order with biblionumber C<$biblionumber> and ordernumber
1413 C<$order->{ordernumber}>.
1415 =cut
1418 sub ModReceiveOrder {
1419 my ($params) = @_;
1420 my $biblionumber = $params->{biblionumber};
1421 my $order = { %{ $params->{order} } }; # Copy the order, we don't want to modify it
1422 my $invoice = $params->{invoice};
1423 my $quantrec = $params->{quantityreceived};
1424 my $user = $params->{user};
1425 my $budget_id = $params->{budget_id};
1426 my $received_items = $params->{received_items};
1428 my $dbh = C4::Context->dbh;
1429 my $datereceived = ( $invoice and $invoice->{datereceived} ) ? $invoice->{datereceived} : dt_from_string;
1430 my $suggestionid = GetSuggestionFromBiblionumber( $biblionumber );
1431 if ($suggestionid) {
1432 ModSuggestion( {suggestionid=>$suggestionid,
1433 STATUS=>'AVAILABLE',
1434 biblionumber=> $biblionumber}
1438 my $result_set = $dbh->selectrow_arrayref(
1439 q{SELECT aqbasket.is_standing
1440 FROM aqbasket
1441 WHERE basketno=?},{ Slice => {} }, $order->{basketno});
1442 my $is_standing = $result_set->[0]; # we assume we have a unique basket
1444 my $new_ordernumber = $order->{ordernumber};
1445 if ( $is_standing || $order->{quantity} > $quantrec ) {
1446 # Split order line in two parts: the first is the original order line
1447 # without received items (the quantity is decreased),
1448 # the second part is a new order line with quantity=quantityrec
1449 # (entirely received)
1450 my $query = q|
1451 UPDATE aqorders
1452 SET quantity = ?,
1453 orderstatus = 'partial'|;
1454 $query .= q|, order_internalnote = ?| if defined $order->{order_internalnote};
1455 $query .= q| WHERE ordernumber = ?|;
1456 my $sth = $dbh->prepare($query);
1458 $sth->execute(
1459 ( $is_standing ? 1 : ($order->{quantity} - $quantrec) ),
1460 ( defined $order->{order_internalnote} ? $order->{order_internalnote} : () ),
1461 $order->{ordernumber}
1464 # Recalculate tax_value
1465 $dbh->do(q|
1466 UPDATE aqorders
1468 tax_value_on_ordering = quantity * ecost_tax_excluded * tax_rate_on_ordering,
1469 tax_value_on_receiving = quantity * unitprice_tax_excluded * tax_rate_on_receiving
1470 WHERE ordernumber = ?
1471 |, undef, $order->{ordernumber});
1473 delete $order->{ordernumber};
1474 $order->{budget_id} = ( $budget_id || $order->{budget_id} );
1475 $order->{quantity} = $quantrec;
1476 $order->{quantityreceived} = $quantrec;
1477 $order->{ecost_tax_excluded} //= 0;
1478 $order->{tax_rate_on_ordering} //= 0;
1479 $order->{unitprice_tax_excluded} //= 0;
1480 $order->{tax_rate_on_receiving} //= 0;
1481 $order->{tax_value_on_ordering} = $order->{quantity} * $order->{ecost_tax_excluded} * $order->{tax_rate_on_ordering};
1482 $order->{tax_value_on_receiving} = $order->{quantity} * $order->{unitprice_tax_excluded} * $order->{tax_rate_on_receiving};
1483 $order->{datereceived} = $datereceived;
1484 $order->{invoiceid} = $invoice->{invoiceid};
1485 $order->{orderstatus} = 'complete';
1486 $new_ordernumber = Koha::Acquisition::Order->new($order)->insert->{ordernumber};
1488 if ($received_items) {
1489 foreach my $itemnumber (@$received_items) {
1490 ModItemOrder($itemnumber, $new_ordernumber);
1493 } else {
1494 my $query = q|
1495 UPDATE aqorders
1496 SET quantityreceived = ?,
1497 datereceived = ?,
1498 invoiceid = ?,
1499 budget_id = ?,
1500 orderstatus = 'complete'
1503 $query .= q|
1504 , unitprice = ?, unitprice_tax_included = ?, unitprice_tax_excluded = ?
1505 | if defined $order->{unitprice};
1507 $query .= q|
1508 ,tax_value_on_receiving = ?
1509 | if defined $order->{tax_value_on_receiving};
1511 $query .= q|
1512 ,tax_rate_on_receiving = ?
1513 | if defined $order->{tax_rate_on_receiving};
1515 $query .= q|
1516 , order_internalnote = ?
1517 | if defined $order->{order_internalnote};
1519 $query .= q| where biblionumber=? and ordernumber=?|;
1521 my $sth = $dbh->prepare( $query );
1522 my @params = ( $quantrec, $datereceived, $invoice->{invoiceid}, ( $budget_id ? $budget_id : $order->{budget_id} ) );
1524 if ( defined $order->{unitprice} ) {
1525 push @params, $order->{unitprice}, $order->{unitprice_tax_included}, $order->{unitprice_tax_excluded};
1528 if ( defined $order->{tax_value_on_receiving} ) {
1529 push @params, $order->{tax_value_on_receiving};
1532 if ( defined $order->{tax_rate_on_receiving} ) {
1533 push @params, $order->{tax_rate_on_receiving};
1536 if ( defined $order->{order_internalnote} ) {
1537 push @params, $order->{order_internalnote};
1540 push @params, ( $biblionumber, $order->{ordernumber} );
1542 $sth->execute( @params );
1544 # All items have been received, sent a notification to users
1545 NotifyOrderUsers( $order->{ordernumber} );
1548 return ($datereceived, $new_ordernumber);
1551 =head3 CancelReceipt
1553 my $parent_ordernumber = CancelReceipt($ordernumber);
1555 Cancel an order line receipt and update the parent order line, as if no
1556 receipt was made.
1557 If items are created at receipt (AcqCreateItem = receiving) then delete
1558 these items.
1560 =cut
1562 sub CancelReceipt {
1563 my $ordernumber = shift;
1565 return unless $ordernumber;
1567 my $dbh = C4::Context->dbh;
1568 my $query = qq{
1569 SELECT datereceived, parent_ordernumber, quantity
1570 FROM aqorders
1571 WHERE ordernumber = ?
1573 my $sth = $dbh->prepare($query);
1574 $sth->execute($ordernumber);
1575 my $order = $sth->fetchrow_hashref;
1576 unless($order) {
1577 warn "CancelReceipt: order $ordernumber does not exist";
1578 return;
1580 unless($order->{'datereceived'}) {
1581 warn "CancelReceipt: order $ordernumber is not received";
1582 return;
1585 my $parent_ordernumber = $order->{'parent_ordernumber'};
1587 my @itemnumbers = GetItemnumbersFromOrder( $ordernumber );
1589 if($parent_ordernumber == $ordernumber || not $parent_ordernumber) {
1590 # The order line has no parent, just mark it as not received
1591 $query = qq{
1592 UPDATE aqorders
1593 SET quantityreceived = ?,
1594 datereceived = ?,
1595 invoiceid = ?,
1596 orderstatus = 'ordered'
1597 WHERE ordernumber = ?
1599 $sth = $dbh->prepare($query);
1600 $sth->execute(0, undef, undef, $ordernumber);
1601 _cancel_items_receipt( $ordernumber );
1602 } else {
1603 # The order line has a parent, increase parent quantity and delete
1604 # the order line.
1605 $query = qq{
1606 SELECT quantity, datereceived
1607 FROM aqorders
1608 WHERE ordernumber = ?
1610 $sth = $dbh->prepare($query);
1611 $sth->execute($parent_ordernumber);
1612 my $parent_order = $sth->fetchrow_hashref;
1613 unless($parent_order) {
1614 warn "Parent order $parent_ordernumber does not exist.";
1615 return;
1617 if($parent_order->{'datereceived'}) {
1618 warn "CancelReceipt: parent order is received.".
1619 " Can't cancel receipt.";
1620 return;
1622 $query = qq{
1623 UPDATE aqorders
1624 SET quantity = ?,
1625 orderstatus = 'ordered'
1626 WHERE ordernumber = ?
1628 $sth = $dbh->prepare($query);
1629 my $rv = $sth->execute(
1630 $order->{'quantity'} + $parent_order->{'quantity'},
1631 $parent_ordernumber
1633 unless($rv) {
1634 warn "Cannot update parent order line, so do not cancel".
1635 " receipt";
1636 return;
1639 # Recalculate tax_value
1640 $dbh->do(q|
1641 UPDATE aqorders
1643 tax_value_on_ordering = quantity * ecost_tax_excluded * tax_rate_on_ordering,
1644 tax_value_on_receiving = quantity * unitprice_tax_excluded * tax_rate_on_receiving
1645 WHERE ordernumber = ?
1646 |, undef, $parent_ordernumber);
1648 _cancel_items_receipt( $ordernumber, $parent_ordernumber );
1649 # Delete order line
1650 $query = qq{
1651 DELETE FROM aqorders
1652 WHERE ordernumber = ?
1654 $sth = $dbh->prepare($query);
1655 $sth->execute($ordernumber);
1659 if(C4::Context->preference('AcqCreateItem') eq 'ordering') {
1660 my @affects = split q{\|}, C4::Context->preference("AcqItemSetSubfieldsWhenReceiptIsCancelled");
1661 if ( @affects ) {
1662 for my $in ( @itemnumbers ) {
1663 my $item = Koha::Items->find( $in );
1664 my $biblio = $item->biblio;
1665 my ( $itemfield ) = GetMarcFromKohaField( 'items.itemnumber', $biblio->frameworkcode );
1666 my $item_marc = C4::Items::GetMarcItem( $biblio->biblionumber, $in );
1667 for my $affect ( @affects ) {
1668 my ( $sf, $v ) = split q{=}, $affect, 2;
1669 foreach ( $item_marc->field($itemfield) ) {
1670 $_->update( $sf => $v );
1673 C4::Items::ModItemFromMarc( $item_marc, $biblio->biblionumber, $in );
1678 return $parent_ordernumber;
1681 sub _cancel_items_receipt {
1682 my ( $ordernumber, $parent_ordernumber ) = @_;
1683 $parent_ordernumber ||= $ordernumber;
1685 my @itemnumbers = GetItemnumbersFromOrder($ordernumber);
1686 if(C4::Context->preference('AcqCreateItem') eq 'receiving') {
1687 # Remove items that were created at receipt
1688 my $query = qq{
1689 DELETE FROM items, aqorders_items
1690 USING items, aqorders_items
1691 WHERE items.itemnumber = ? AND aqorders_items.itemnumber = ?
1693 my $dbh = C4::Context->dbh;
1694 my $sth = $dbh->prepare($query);
1695 foreach my $itemnumber (@itemnumbers) {
1696 $sth->execute($itemnumber, $itemnumber);
1698 } else {
1699 # Update items
1700 foreach my $itemnumber (@itemnumbers) {
1701 ModItemOrder($itemnumber, $parent_ordernumber);
1706 #------------------------------------------------------------#
1708 =head3 SearchOrders
1710 @results = &SearchOrders({
1711 ordernumber => $ordernumber,
1712 search => $search,
1713 ean => $ean,
1714 booksellerid => $booksellerid,
1715 basketno => $basketno,
1716 basketname => $basketname,
1717 basketgroupname => $basketgroupname,
1718 owner => $owner,
1719 pending => $pending
1720 ordered => $ordered
1721 biblionumber => $biblionumber,
1722 budget_id => $budget_id
1725 Searches for orders filtered by criteria.
1727 C<$ordernumber> Finds matching orders or transferred orders by ordernumber.
1728 C<$search> Finds orders matching %$search% in title, author, or isbn.
1729 C<$owner> Finds order for the logged in user.
1730 C<$pending> Finds pending orders. Ignores completed and cancelled orders.
1731 C<$ordered> Finds orders to receive only (status 'ordered' or 'partial').
1734 C<@results> is an array of references-to-hash with the keys are fields
1735 from aqorders, biblio, biblioitems and aqbasket tables.
1737 =cut
1739 sub SearchOrders {
1740 my ( $params ) = @_;
1741 my $ordernumber = $params->{ordernumber};
1742 my $search = $params->{search};
1743 my $ean = $params->{ean};
1744 my $booksellerid = $params->{booksellerid};
1745 my $basketno = $params->{basketno};
1746 my $basketname = $params->{basketname};
1747 my $basketgroupname = $params->{basketgroupname};
1748 my $owner = $params->{owner};
1749 my $pending = $params->{pending};
1750 my $ordered = $params->{ordered};
1751 my $biblionumber = $params->{biblionumber};
1752 my $budget_id = $params->{budget_id};
1754 my $dbh = C4::Context->dbh;
1755 my @args = ();
1756 my $query = q{
1757 SELECT aqbasket.basketno,
1758 borrowers.surname,
1759 borrowers.firstname,
1760 biblio.*,
1761 biblioitems.isbn,
1762 biblioitems.biblioitemnumber,
1763 biblioitems.publishercode,
1764 biblioitems.publicationyear,
1765 aqbasket.authorisedby,
1766 aqbasket.booksellerid,
1767 aqbasket.closedate,
1768 aqbasket.creationdate,
1769 aqbasket.basketname,
1770 aqbasketgroups.id as basketgroupid,
1771 aqbasketgroups.name as basketgroupname,
1772 aqorders.*
1773 FROM aqorders
1774 LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
1775 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
1776 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
1777 LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
1778 LEFT JOIN biblioitems ON biblioitems.biblionumber=biblio.biblionumber
1781 # If we search on ordernumber, we retrieve the transferred order if a transfer has been done.
1782 $query .= q{
1783 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1784 } if $ordernumber;
1786 $query .= q{
1787 WHERE (datecancellationprinted is NULL)
1790 if ( $pending or $ordered ) {
1791 $query .= q{
1792 AND (
1793 ( aqbasket.is_standing AND aqorders.orderstatus IN ( "new", "ordered", "partial" ) )
1794 OR (
1795 ( quantity > quantityreceived OR quantityreceived is NULL )
1798 if ( $ordered ) {
1799 $query .= q{ AND aqorders.orderstatus IN ( "ordered", "partial" )};
1801 $query .= q{
1807 my $userenv = C4::Context->userenv;
1808 if ( C4::Context->preference("IndependentBranches") ) {
1809 unless ( C4::Context->IsSuperLibrarian() ) {
1810 $query .= q{
1811 AND (
1812 borrowers.branchcode = ?
1813 OR borrowers.branchcode = ''
1816 push @args, $userenv->{branch};
1820 if ( $ordernumber ) {
1821 $query .= ' AND ( aqorders.ordernumber = ? OR aqorders_transfers.ordernumber_from = ? ) ';
1822 push @args, ( $ordernumber, $ordernumber );
1824 if ( $biblionumber ) {
1825 $query .= 'AND aqorders.biblionumber = ?';
1826 push @args, $biblionumber;
1828 if( $search ) {
1829 $query .= ' AND (biblio.title LIKE ? OR biblio.author LIKE ? OR biblioitems.isbn LIKE ?)';
1830 push @args, ("%$search%","%$search%","%$search%");
1832 if ( $ean ) {
1833 $query .= ' AND biblioitems.ean = ?';
1834 push @args, $ean;
1836 if ( $booksellerid ) {
1837 $query .= 'AND aqbasket.booksellerid = ?';
1838 push @args, $booksellerid;
1840 if( $basketno ) {
1841 $query .= 'AND aqbasket.basketno = ?';
1842 push @args, $basketno;
1844 if( $basketname ) {
1845 $query .= 'AND aqbasket.basketname LIKE ?';
1846 push @args, "%$basketname%";
1848 if( $basketgroupname ) {
1849 $query .= ' AND aqbasketgroups.name LIKE ?';
1850 push @args, "%$basketgroupname%";
1853 if ( $owner ) {
1854 $query .= ' AND aqbasket.authorisedby=? ';
1855 push @args, $userenv->{'number'};
1858 if ( $budget_id ) {
1859 $query .= ' AND aqorders.budget_id = ?';
1860 push @args, $budget_id;
1863 $query .= ' ORDER BY aqbasket.basketno';
1865 my $sth = $dbh->prepare($query);
1866 $sth->execute(@args);
1867 return $sth->fetchall_arrayref({});
1870 #------------------------------------------------------------#
1872 =head3 DelOrder
1874 &DelOrder($biblionumber, $ordernumber);
1876 Cancel the order with the given order and biblio numbers. It does not
1877 delete any entries in the aqorders table, it merely marks them as
1878 cancelled.
1880 =cut
1882 sub DelOrder {
1883 my ( $bibnum, $ordernumber, $delete_biblio, $reason ) = @_;
1885 my $error;
1886 my $dbh = C4::Context->dbh;
1887 my $query = "
1888 UPDATE aqorders
1889 SET datecancellationprinted=now(), orderstatus='cancelled'
1891 if($reason) {
1892 $query .= ", cancellationreason = ? ";
1894 $query .= "
1895 WHERE biblionumber=? AND ordernumber=?
1897 my $sth = $dbh->prepare($query);
1898 if($reason) {
1899 $sth->execute($reason, $bibnum, $ordernumber);
1900 } else {
1901 $sth->execute( $bibnum, $ordernumber );
1903 $sth->finish;
1905 my @itemnumbers = GetItemnumbersFromOrder( $ordernumber );
1906 foreach my $itemnumber (@itemnumbers){
1907 my $delcheck = C4::Items::DelItemCheck( $bibnum, $itemnumber );
1909 if($delcheck != 1) {
1910 $error->{'delitem'} = 1;
1914 if($delete_biblio) {
1915 # We get the number of remaining items
1916 my $biblio = Koha::Biblios->find( $bibnum );
1917 my $itemcount = $biblio->items->count;
1919 # If there are no items left,
1920 if ( $itemcount == 0 ) {
1921 # We delete the record
1922 my $delcheck = DelBiblio($bibnum);
1924 if($delcheck) {
1925 $error->{'delbiblio'} = 1;
1930 return $error;
1933 =head3 TransferOrder
1935 my $newordernumber = TransferOrder($ordernumber, $basketno);
1937 Transfer an order line to a basket.
1938 Mark $ordernumber as cancelled with an internal note 'Cancelled and transferred
1939 to BOOKSELLER on DATE' and create new order with internal note
1940 'Transferred from BOOKSELLER on DATE'.
1941 Move all attached items to the new order.
1942 Received orders cannot be transferred.
1943 Return the ordernumber of created order.
1945 =cut
1947 sub TransferOrder {
1948 my ($ordernumber, $basketno) = @_;
1950 return unless ($ordernumber and $basketno);
1952 my $order = GetOrder( $ordernumber );
1953 return if $order->{datereceived};
1954 my $basket = GetBasket($basketno);
1955 return unless $basket;
1957 my $dbh = C4::Context->dbh;
1958 my ($query, $sth, $rv);
1960 $query = q{
1961 UPDATE aqorders
1962 SET datecancellationprinted = CAST(NOW() AS date), orderstatus = ?
1963 WHERE ordernumber = ?
1965 $sth = $dbh->prepare($query);
1966 $rv = $sth->execute('cancelled', $ordernumber);
1968 delete $order->{'ordernumber'};
1969 delete $order->{parent_ordernumber};
1970 $order->{'basketno'} = $basketno;
1972 my $newordernumber = Koha::Acquisition::Order->new($order)->insert->{ordernumber};
1974 $query = q{
1975 UPDATE aqorders_items
1976 SET ordernumber = ?
1977 WHERE ordernumber = ?
1979 $sth = $dbh->prepare($query);
1980 $sth->execute($newordernumber, $ordernumber);
1982 $query = q{
1983 INSERT INTO aqorders_transfers (ordernumber_from, ordernumber_to)
1984 VALUES (?, ?)
1986 $sth = $dbh->prepare($query);
1987 $sth->execute($ordernumber, $newordernumber);
1989 return $newordernumber;
1992 =head2 FUNCTIONS ABOUT PARCELS
1994 =head3 GetParcels
1996 $results = &GetParcels($bookseller, $order, $code, $datefrom, $dateto);
1998 get a lists of parcels.
2000 * Input arg :
2002 =over
2004 =item $bookseller
2005 is the bookseller this function has to get parcels.
2007 =item $order
2008 To know on what criteria the results list has to be ordered.
2010 =item $code
2011 is the booksellerinvoicenumber.
2013 =item $datefrom & $dateto
2014 to know on what date this function has to filter its search.
2016 =back
2018 * return:
2019 a pointer on a hash list containing parcel informations as such :
2021 =over
2023 =item Creation date
2025 =item Last operation
2027 =item Number of biblio
2029 =item Number of items
2031 =back
2033 =cut
2035 sub GetParcels {
2036 my ($bookseller,$order, $code, $datefrom, $dateto) = @_;
2037 my $dbh = C4::Context->dbh;
2038 my @query_params = ();
2039 my $strsth ="
2040 SELECT aqinvoices.invoicenumber,
2041 datereceived,purchaseordernumber,
2042 count(DISTINCT biblionumber) AS biblio,
2043 sum(quantity) AS itemsexpected,
2044 sum(quantityreceived) AS itemsreceived
2045 FROM aqorders LEFT JOIN aqbasket ON aqbasket.basketno = aqorders.basketno
2046 LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
2047 WHERE aqbasket.booksellerid = ? and datereceived IS NOT NULL
2049 push @query_params, $bookseller;
2051 if ( defined $code ) {
2052 $strsth .= ' and aqinvoices.invoicenumber like ? ';
2053 # add a % to the end of the code to allow stemming.
2054 push @query_params, "$code%";
2057 if ( defined $datefrom ) {
2058 $strsth .= ' and datereceived >= ? ';
2059 push @query_params, $datefrom;
2062 if ( defined $dateto ) {
2063 $strsth .= 'and datereceived <= ? ';
2064 push @query_params, $dateto;
2067 $strsth .= "group by aqinvoices.invoicenumber,datereceived ";
2069 # can't use a placeholder to place this column name.
2070 # but, we could probably be checking to make sure it is a column that will be fetched.
2071 $strsth .= "order by $order " if ($order);
2073 my $sth = $dbh->prepare($strsth);
2075 $sth->execute( @query_params );
2076 my $results = $sth->fetchall_arrayref({});
2077 return @{$results};
2080 #------------------------------------------------------------#
2082 =head3 GetLateOrders
2084 @results = &GetLateOrders;
2086 Searches for bookseller with late orders.
2088 return:
2089 the table of supplier with late issues. This table is full of hashref.
2091 =cut
2093 sub GetLateOrders {
2094 my $delay = shift;
2095 my $supplierid = shift;
2096 my $branch = shift;
2097 my $estimateddeliverydatefrom = shift;
2098 my $estimateddeliverydateto = shift;
2100 my $dbh = C4::Context->dbh;
2102 #BEWARE, order of parenthesis and LEFT JOIN is important for speed
2103 my $dbdriver = C4::Context->config("db_scheme") || "mysql";
2105 my @query_params = ();
2106 my $select = "
2107 SELECT aqbasket.basketno,
2108 aqorders.ordernumber,
2109 DATE(aqbasket.closedate) AS orderdate,
2110 aqbasket.basketname AS basketname,
2111 aqbasket.basketgroupid AS basketgroupid,
2112 aqbasketgroups.name AS basketgroupname,
2113 aqorders.rrp AS unitpricesupplier,
2114 aqorders.ecost AS unitpricelib,
2115 aqorders.claims_count AS claims_count,
2116 aqorders.claimed_date AS claimed_date,
2117 aqbudgets.budget_name AS budget,
2118 borrowers.branchcode AS branch,
2119 aqbooksellers.name AS supplier,
2120 aqbooksellers.id AS supplierid,
2121 biblio.author, biblio.title,
2122 biblioitems.publishercode AS publisher,
2123 biblioitems.publicationyear,
2124 ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
2126 my $from = "
2127 FROM
2128 aqorders LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
2129 LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
2130 LEFT JOIN aqbudgets ON aqorders.budget_id = aqbudgets.budget_id,
2131 aqbasket LEFT JOIN borrowers ON aqbasket.authorisedby = borrowers.borrowernumber
2132 LEFT JOIN aqbooksellers ON aqbasket.booksellerid = aqbooksellers.id
2133 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
2134 WHERE aqorders.basketno = aqbasket.basketno
2135 AND ( datereceived = ''
2136 OR datereceived IS NULL
2137 OR aqorders.quantityreceived < aqorders.quantity
2139 AND aqbasket.closedate IS NOT NULL
2140 AND (aqorders.datecancellationprinted IS NULL OR aqorders.datecancellationprinted='0000-00-00')
2142 my $having = "";
2143 if ($dbdriver eq "mysql") {
2144 $select .= "
2145 aqorders.quantity - COALESCE(aqorders.quantityreceived,0) AS quantity,
2146 (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
2147 DATEDIFF(CAST(now() AS date),closedate) AS latesince
2149 if ( defined $delay ) {
2150 $from .= " AND (closedate <= DATE_SUB(CAST(now() AS date),INTERVAL ? DAY)) " ;
2151 push @query_params, $delay;
2153 $having = "HAVING quantity <> 0";
2154 } else {
2155 # FIXME: account for IFNULL as above
2156 $select .= "
2157 aqorders.quantity AS quantity,
2158 aqorders.quantity * aqorders.rrp AS subtotal,
2159 (CAST(now() AS date) - closedate) AS latesince
2161 if ( defined $delay ) {
2162 $from .= " AND (closedate <= (CAST(now() AS date) -(INTERVAL ? DAY)) ";
2163 push @query_params, $delay;
2166 if (defined $supplierid) {
2167 $from .= ' AND aqbasket.booksellerid = ? ';
2168 push @query_params, $supplierid;
2170 if (defined $branch) {
2171 $from .= ' AND borrowers.branchcode LIKE ? ';
2172 push @query_params, $branch;
2175 if ( defined $estimateddeliverydatefrom or defined $estimateddeliverydateto ) {
2176 $from .= ' AND aqbooksellers.deliverytime IS NOT NULL ';
2178 if ( defined $estimateddeliverydatefrom ) {
2179 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) >= ?';
2180 push @query_params, $estimateddeliverydatefrom;
2182 if ( defined $estimateddeliverydateto ) {
2183 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) <= ?';
2184 push @query_params, $estimateddeliverydateto;
2186 if ( defined $estimateddeliverydatefrom and not defined $estimateddeliverydateto ) {
2187 $from .= ' AND ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) <= CAST(now() AS date)';
2189 if (C4::Context->preference("IndependentBranches")
2190 && !C4::Context->IsSuperLibrarian() ) {
2191 $from .= ' AND borrowers.branchcode LIKE ? ';
2192 push @query_params, C4::Context->userenv->{branch};
2194 $from .= " AND orderstatus <> 'cancelled' ";
2195 my $query = "$select $from $having\nORDER BY latesince, basketno, borrowers.branchcode, supplier";
2196 $debug and print STDERR "GetLateOrders query: $query\nGetLateOrders args: " . join(" ",@query_params);
2197 my $sth = $dbh->prepare($query);
2198 $sth->execute(@query_params);
2199 my @results;
2200 while (my $data = $sth->fetchrow_hashref) {
2201 push @results, $data;
2203 return @results;
2206 #------------------------------------------------------------#
2208 =head3 GetHistory
2210 \@order_loop = GetHistory( %params );
2212 Retreives some acquisition history information
2214 params:
2215 title
2216 author
2217 name
2218 isbn
2220 from_placed_on
2221 to_placed_on
2222 basket - search both basket name and number
2223 booksellerinvoicenumber
2224 basketgroupname
2225 budget
2226 orderstatus (note that orderstatus '' will retrieve orders
2227 of any status except cancelled)
2228 biblionumber
2229 get_canceled_order (if set to a true value, cancelled orders will
2230 be included)
2232 returns:
2233 $order_loop is a list of hashrefs that each look like this:
2235 'author' => 'Twain, Mark',
2236 'basketno' => '1',
2237 'biblionumber' => '215',
2238 'count' => 1,
2239 'creationdate' => 'MM/DD/YYYY',
2240 'datereceived' => undef,
2241 'ecost' => '1.00',
2242 'id' => '1',
2243 'invoicenumber' => undef,
2244 'name' => '',
2245 'ordernumber' => '1',
2246 'quantity' => 1,
2247 'quantityreceived' => undef,
2248 'title' => 'The Adventures of Huckleberry Finn'
2251 =cut
2253 sub GetHistory {
2254 # don't run the query if there are no parameters (list would be too long for sure !)
2255 croak "No search params" unless @_;
2256 my %params = @_;
2257 my $title = $params{title};
2258 my $author = $params{author};
2259 my $isbn = $params{isbn};
2260 my $ean = $params{ean};
2261 my $name = $params{name};
2262 my $from_placed_on = $params{from_placed_on};
2263 my $to_placed_on = $params{to_placed_on};
2264 my $basket = $params{basket};
2265 my $booksellerinvoicenumber = $params{booksellerinvoicenumber};
2266 my $basketgroupname = $params{basketgroupname};
2267 my $budget = $params{budget};
2268 my $orderstatus = $params{orderstatus};
2269 my $biblionumber = $params{biblionumber};
2270 my $get_canceled_order = $params{get_canceled_order} || 0;
2271 my $ordernumber = $params{ordernumber};
2272 my $search_children_too = $params{search_children_too} || 0;
2273 my $created_by = $params{created_by} || [];
2275 my @order_loop;
2276 my $total_qty = 0;
2277 my $total_qtyreceived = 0;
2278 my $total_price = 0;
2280 my $dbh = C4::Context->dbh;
2281 my $query ="
2282 SELECT
2283 COALESCE(biblio.title, deletedbiblio.title) AS title,
2284 COALESCE(biblio.author, deletedbiblio.author) AS author,
2285 COALESCE(biblioitems.isbn, deletedbiblioitems.isbn) AS isbn,
2286 COALESCE(biblioitems.ean, deletedbiblioitems.ean) AS ean,
2287 aqorders.basketno,
2288 aqbasket.basketname,
2289 aqbasket.basketgroupid,
2290 aqbasket.authorisedby,
2291 concat( borrowers.firstname,' ',borrowers.surname) AS authorisedbyname,
2292 aqbasketgroups.name as groupname,
2293 aqbooksellers.name,
2294 aqbasket.creationdate,
2295 aqorders.datereceived,
2296 aqorders.quantity,
2297 aqorders.quantityreceived,
2298 aqorders.ecost,
2299 aqorders.ordernumber,
2300 aqorders.invoiceid,
2301 aqinvoices.invoicenumber,
2302 aqbooksellers.id as id,
2303 aqorders.biblionumber,
2304 aqorders.orderstatus,
2305 aqorders.parent_ordernumber,
2306 aqbudgets.budget_name
2308 $query .= ", aqbudgets.budget_id AS budget" if defined $budget;
2309 $query .= "
2310 FROM aqorders
2311 LEFT JOIN aqbasket ON aqorders.basketno=aqbasket.basketno
2312 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid=aqbasketgroups.id
2313 LEFT JOIN aqbooksellers ON aqbasket.booksellerid=aqbooksellers.id
2314 LEFT JOIN biblioitems ON biblioitems.biblionumber=aqorders.biblionumber
2315 LEFT JOIN biblio ON biblio.biblionumber=aqorders.biblionumber
2316 LEFT JOIN aqbudgets ON aqorders.budget_id=aqbudgets.budget_id
2317 LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
2318 LEFT JOIN deletedbiblio ON deletedbiblio.biblionumber=aqorders.biblionumber
2319 LEFT JOIN deletedbiblioitems ON deletedbiblioitems.biblionumber=aqorders.biblionumber
2320 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
2323 $query .= " WHERE 1 ";
2325 unless ($get_canceled_order or (defined $orderstatus and $orderstatus eq 'cancelled')) {
2326 $query .= " AND (datecancellationprinted is NULL or datecancellationprinted='0000-00-00') ";
2329 my @query_params = ();
2331 if ( $biblionumber ) {
2332 $query .= " AND biblio.biblionumber = ?";
2333 push @query_params, $biblionumber;
2336 if ( $title ) {
2337 $query .= " AND biblio.title LIKE ? ";
2338 $title =~ s/\s+/%/g;
2339 push @query_params, "%$title%";
2342 if ( $author ) {
2343 $query .= " AND biblio.author LIKE ? ";
2344 push @query_params, "%$author%";
2347 if ( $isbn ) {
2348 $query .= " AND biblioitems.isbn LIKE ? ";
2349 push @query_params, "%$isbn%";
2351 if ( $ean ) {
2352 $query .= " AND biblioitems.ean = ? ";
2353 push @query_params, "$ean";
2355 if ( $name ) {
2356 $query .= " AND aqbooksellers.name LIKE ? ";
2357 push @query_params, "%$name%";
2360 if ( $budget ) {
2361 $query .= " AND aqbudgets.budget_id = ? ";
2362 push @query_params, "$budget";
2365 if ( $from_placed_on ) {
2366 $query .= " AND creationdate >= ? ";
2367 push @query_params, $from_placed_on;
2370 if ( $to_placed_on ) {
2371 $query .= " AND creationdate <= ? ";
2372 push @query_params, $to_placed_on;
2375 if ( defined $orderstatus and $orderstatus ne '') {
2376 $query .= " AND aqorders.orderstatus = ? ";
2377 push @query_params, "$orderstatus";
2380 if ($basket) {
2381 if ($basket =~ m/^\d+$/) {
2382 $query .= " AND aqorders.basketno = ? ";
2383 push @query_params, $basket;
2384 } else {
2385 $query .= " AND aqbasket.basketname LIKE ? ";
2386 push @query_params, "%$basket%";
2390 if ($booksellerinvoicenumber) {
2391 $query .= " AND aqinvoices.invoicenumber LIKE ? ";
2392 push @query_params, "%$booksellerinvoicenumber%";
2395 if ($basketgroupname) {
2396 $query .= " AND aqbasketgroups.name LIKE ? ";
2397 push @query_params, "%$basketgroupname%";
2400 if ($ordernumber) {
2401 $query .= " AND (aqorders.ordernumber = ? ";
2402 push @query_params, $ordernumber;
2403 if ($search_children_too) {
2404 $query .= " OR aqorders.parent_ordernumber = ? ";
2405 push @query_params, $ordernumber;
2407 $query .= ") ";
2410 if ( @$created_by ) {
2411 $query .= ' AND aqbasket.authorisedby IN ( ' . join( ',', ('?') x @$created_by ) . ')';
2412 push @query_params, @$created_by;
2416 if ( C4::Context->preference("IndependentBranches") ) {
2417 unless ( C4::Context->IsSuperLibrarian() ) {
2418 $query .= " AND (borrowers.branchcode = ? OR borrowers.branchcode ='' ) ";
2419 push @query_params, C4::Context->userenv->{branch};
2422 $query .= " ORDER BY id";
2424 return $dbh->selectall_arrayref( $query, { Slice => {} }, @query_params );
2427 =head2 GetRecentAcqui
2429 $results = GetRecentAcqui($days);
2431 C<$results> is a ref to a table which containts hashref
2433 =cut
2435 sub GetRecentAcqui {
2436 my $limit = shift;
2437 my $dbh = C4::Context->dbh;
2438 my $query = "
2439 SELECT *
2440 FROM biblio
2441 ORDER BY timestamp DESC
2442 LIMIT 0,".$limit;
2444 my $sth = $dbh->prepare($query);
2445 $sth->execute;
2446 my $results = $sth->fetchall_arrayref({});
2447 return $results;
2450 #------------------------------------------------------------#
2452 =head3 AddClaim
2454 &AddClaim($ordernumber);
2456 Add a claim for an order
2458 =cut
2460 sub AddClaim {
2461 my ($ordernumber) = @_;
2462 my $dbh = C4::Context->dbh;
2463 my $query = "
2464 UPDATE aqorders SET
2465 claims_count = claims_count + 1,
2466 claimed_date = CURDATE()
2467 WHERE ordernumber = ?
2469 my $sth = $dbh->prepare($query);
2470 $sth->execute($ordernumber);
2473 =head3 GetInvoices
2475 my @invoices = GetInvoices(
2476 invoicenumber => $invoicenumber,
2477 supplierid => $supplierid,
2478 suppliername => $suppliername,
2479 shipmentdatefrom => $shipmentdatefrom, # ISO format
2480 shipmentdateto => $shipmentdateto, # ISO format
2481 billingdatefrom => $billingdatefrom, # ISO format
2482 billingdateto => $billingdateto, # ISO format
2483 isbneanissn => $isbn_or_ean_or_issn,
2484 title => $title,
2485 author => $author,
2486 publisher => $publisher,
2487 publicationyear => $publicationyear,
2488 branchcode => $branchcode,
2489 order_by => $order_by
2492 Return a list of invoices that match all given criteria.
2494 $order_by is "column_name (asc|desc)", where column_name is any of
2495 'invoicenumber', 'booksellerid', 'shipmentdate', 'billingdate', 'closedate',
2496 'shipmentcost', 'shipmentcost_budgetid'.
2498 asc is the default if omitted
2500 =cut
2502 sub GetInvoices {
2503 my %args = @_;
2505 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2506 closedate shipmentcost shipmentcost_budgetid);
2508 my $dbh = C4::Context->dbh;
2509 my $query = qq{
2510 SELECT aqinvoices.*, aqbooksellers.name AS suppliername,
2511 COUNT(
2512 DISTINCT IF(
2513 aqorders.datereceived IS NOT NULL,
2514 aqorders.biblionumber,
2515 NULL
2517 ) AS receivedbiblios,
2518 COUNT(
2519 DISTINCT IF(
2520 aqorders.subscriptionid IS NOT NULL,
2521 aqorders.subscriptionid,
2522 NULL
2524 ) AS is_linked_to_subscriptions,
2525 SUM(aqorders.quantityreceived) AS receiveditems
2526 FROM aqinvoices
2527 LEFT JOIN aqbooksellers ON aqbooksellers.id = aqinvoices.booksellerid
2528 LEFT JOIN aqorders ON aqorders.invoiceid = aqinvoices.invoiceid
2529 LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
2530 LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
2531 LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2532 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
2533 LEFT JOIN subscription ON biblio.biblionumber = subscription.biblionumber
2536 my @bind_args;
2537 my @bind_strs;
2538 if($args{supplierid}) {
2539 push @bind_strs, " aqinvoices.booksellerid = ? ";
2540 push @bind_args, $args{supplierid};
2542 if($args{invoicenumber}) {
2543 push @bind_strs, " aqinvoices.invoicenumber LIKE ? ";
2544 push @bind_args, "%$args{invoicenumber}%";
2546 if($args{suppliername}) {
2547 push @bind_strs, " aqbooksellers.name LIKE ? ";
2548 push @bind_args, "%$args{suppliername}%";
2550 if($args{shipmentdatefrom}) {
2551 push @bind_strs, " aqinvoices.shipmentdate >= ? ";
2552 push @bind_args, $args{shipmentdatefrom};
2554 if($args{shipmentdateto}) {
2555 push @bind_strs, " aqinvoices.shipmentdate <= ? ";
2556 push @bind_args, $args{shipmentdateto};
2558 if($args{billingdatefrom}) {
2559 push @bind_strs, " aqinvoices.billingdate >= ? ";
2560 push @bind_args, $args{billingdatefrom};
2562 if($args{billingdateto}) {
2563 push @bind_strs, " aqinvoices.billingdate <= ? ";
2564 push @bind_args, $args{billingdateto};
2566 if($args{isbneanissn}) {
2567 push @bind_strs, " (biblioitems.isbn LIKE CONCAT('%', ?, '%') OR biblioitems.ean LIKE CONCAT('%', ?, '%') OR biblioitems.issn LIKE CONCAT('%', ?, '%') ) ";
2568 push @bind_args, $args{isbneanissn}, $args{isbneanissn}, $args{isbneanissn};
2570 if($args{title}) {
2571 push @bind_strs, " biblio.title LIKE CONCAT('%', ?, '%') ";
2572 push @bind_args, $args{title};
2574 if($args{author}) {
2575 push @bind_strs, " biblio.author LIKE CONCAT('%', ?, '%') ";
2576 push @bind_args, $args{author};
2578 if($args{publisher}) {
2579 push @bind_strs, " biblioitems.publishercode LIKE CONCAT('%', ?, '%') ";
2580 push @bind_args, $args{publisher};
2582 if($args{publicationyear}) {
2583 push @bind_strs, " ((biblioitems.publicationyear LIKE CONCAT('%', ?, '%')) OR (biblio.copyrightdate LIKE CONCAT('%', ?, '%'))) ";
2584 push @bind_args, $args{publicationyear}, $args{publicationyear};
2586 if($args{branchcode}) {
2587 push @bind_strs, " borrowers.branchcode = ? ";
2588 push @bind_args, $args{branchcode};
2590 if($args{message_id}) {
2591 push @bind_strs, " aqinvoices.message_id = ? ";
2592 push @bind_args, $args{message_id};
2595 $query .= " WHERE " . join(" AND ", @bind_strs) if @bind_strs;
2596 $query .= " GROUP BY aqinvoices.invoiceid ";
2598 if($args{order_by}) {
2599 my ($column, $direction) = split / /, $args{order_by};
2600 if(grep /^$column$/, @columns) {
2601 $direction ||= 'ASC';
2602 $query .= " ORDER BY $column $direction";
2606 my $sth = $dbh->prepare($query);
2607 $sth->execute(@bind_args);
2609 my $results = $sth->fetchall_arrayref({});
2610 return @$results;
2613 =head3 GetInvoice
2615 my $invoice = GetInvoice($invoiceid);
2617 Get informations about invoice with given $invoiceid
2619 Return a hash filled with aqinvoices.* fields
2621 =cut
2623 sub GetInvoice {
2624 my ($invoiceid) = @_;
2625 my $invoice;
2627 return unless $invoiceid;
2629 my $dbh = C4::Context->dbh;
2630 my $query = qq{
2631 SELECT *
2632 FROM aqinvoices
2633 WHERE invoiceid = ?
2635 my $sth = $dbh->prepare($query);
2636 $sth->execute($invoiceid);
2638 $invoice = $sth->fetchrow_hashref;
2639 return $invoice;
2642 =head3 GetInvoiceDetails
2644 my $invoice = GetInvoiceDetails($invoiceid)
2646 Return informations about an invoice + the list of related order lines
2648 Orders informations are in $invoice->{orders} (array ref)
2650 =cut
2652 sub GetInvoiceDetails {
2653 my ($invoiceid) = @_;
2655 if ( !defined $invoiceid ) {
2656 carp 'GetInvoiceDetails called without an invoiceid';
2657 return;
2660 my $dbh = C4::Context->dbh;
2661 my $query = q{
2662 SELECT aqinvoices.*, aqbooksellers.name AS suppliername
2663 FROM aqinvoices
2664 LEFT JOIN aqbooksellers ON aqinvoices.booksellerid = aqbooksellers.id
2665 WHERE invoiceid = ?
2667 my $sth = $dbh->prepare($query);
2668 $sth->execute($invoiceid);
2670 my $invoice = $sth->fetchrow_hashref;
2672 $query = q{
2673 SELECT aqorders.*,
2674 biblio.*,
2675 biblio.copyrightdate,
2676 biblioitems.isbn,
2677 biblioitems.publishercode,
2678 biblioitems.publicationyear,
2679 aqbasket.basketname,
2680 aqbasketgroups.id AS basketgroupid,
2681 aqbasketgroups.name AS basketgroupname
2682 FROM aqorders
2683 LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
2684 LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid = aqbasketgroups.id
2685 LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
2686 LEFT JOIN biblioitems ON aqorders.biblionumber = biblioitems.biblionumber
2687 WHERE invoiceid = ?
2689 $sth = $dbh->prepare($query);
2690 $sth->execute($invoiceid);
2691 $invoice->{orders} = $sth->fetchall_arrayref({});
2692 $invoice->{orders} ||= []; # force an empty arrayref if fetchall_arrayref fails
2694 return $invoice;
2697 =head3 AddInvoice
2699 my $invoiceid = AddInvoice(
2700 invoicenumber => $invoicenumber,
2701 booksellerid => $booksellerid,
2702 shipmentdate => $shipmentdate,
2703 billingdate => $billingdate,
2704 closedate => $closedate,
2705 shipmentcost => $shipmentcost,
2706 shipmentcost_budgetid => $shipmentcost_budgetid
2709 Create a new invoice and return its id or undef if it fails.
2711 =cut
2713 sub AddInvoice {
2714 my %invoice = @_;
2716 return unless(%invoice and $invoice{invoicenumber});
2718 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2719 closedate shipmentcost shipmentcost_budgetid message_id);
2721 my @set_strs;
2722 my @set_args;
2723 foreach my $key (keys %invoice) {
2724 if(0 < grep(/^$key$/, @columns)) {
2725 push @set_strs, "$key = ?";
2726 push @set_args, ($invoice{$key} || undef);
2730 my $rv;
2731 if(@set_args > 0) {
2732 my $dbh = C4::Context->dbh;
2733 my $query = "INSERT INTO aqinvoices SET ";
2734 $query .= join (",", @set_strs);
2735 my $sth = $dbh->prepare($query);
2736 $rv = $sth->execute(@set_args);
2737 if($rv) {
2738 $rv = $dbh->last_insert_id(undef, undef, 'aqinvoices', undef);
2741 return $rv;
2744 =head3 ModInvoice
2746 ModInvoice(
2747 invoiceid => $invoiceid, # Mandatory
2748 invoicenumber => $invoicenumber,
2749 booksellerid => $booksellerid,
2750 shipmentdate => $shipmentdate,
2751 billingdate => $billingdate,
2752 closedate => $closedate,
2753 shipmentcost => $shipmentcost,
2754 shipmentcost_budgetid => $shipmentcost_budgetid
2757 Modify an invoice, invoiceid is mandatory.
2759 Return undef if it fails.
2761 =cut
2763 sub ModInvoice {
2764 my %invoice = @_;
2766 return unless(%invoice and $invoice{invoiceid});
2768 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2769 closedate shipmentcost shipmentcost_budgetid);
2771 my @set_strs;
2772 my @set_args;
2773 foreach my $key (keys %invoice) {
2774 if(0 < grep(/^$key$/, @columns)) {
2775 push @set_strs, "$key = ?";
2776 push @set_args, ($invoice{$key} || undef);
2780 my $dbh = C4::Context->dbh;
2781 my $query = "UPDATE aqinvoices SET ";
2782 $query .= join(",", @set_strs);
2783 $query .= " WHERE invoiceid = ?";
2785 my $sth = $dbh->prepare($query);
2786 $sth->execute(@set_args, $invoice{invoiceid});
2789 =head3 CloseInvoice
2791 CloseInvoice($invoiceid);
2793 Close an invoice.
2795 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => undef);
2797 =cut
2799 sub CloseInvoice {
2800 my ($invoiceid) = @_;
2802 return unless $invoiceid;
2804 my $dbh = C4::Context->dbh;
2805 my $query = qq{
2806 UPDATE aqinvoices
2807 SET closedate = CAST(NOW() AS DATE)
2808 WHERE invoiceid = ?
2810 my $sth = $dbh->prepare($query);
2811 $sth->execute($invoiceid);
2814 =head3 ReopenInvoice
2816 ReopenInvoice($invoiceid);
2818 Reopen an invoice
2820 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => output_pref({ dt=>dt_from_string, dateonly=>1, otputpref=>'iso' }))
2822 =cut
2824 sub ReopenInvoice {
2825 my ($invoiceid) = @_;
2827 return unless $invoiceid;
2829 my $dbh = C4::Context->dbh;
2830 my $query = qq{
2831 UPDATE aqinvoices
2832 SET closedate = NULL
2833 WHERE invoiceid = ?
2835 my $sth = $dbh->prepare($query);
2836 $sth->execute($invoiceid);
2839 =head3 DelInvoice
2841 DelInvoice($invoiceid);
2843 Delete an invoice if there are no items attached to it.
2845 =cut
2847 sub DelInvoice {
2848 my ($invoiceid) = @_;
2850 return unless $invoiceid;
2852 my $dbh = C4::Context->dbh;
2853 my $query = qq{
2854 SELECT COUNT(*)
2855 FROM aqorders
2856 WHERE invoiceid = ?
2858 my $sth = $dbh->prepare($query);
2859 $sth->execute($invoiceid);
2860 my $res = $sth->fetchrow_arrayref;
2861 if ( $res && $res->[0] == 0 ) {
2862 $query = qq{
2863 DELETE FROM aqinvoices
2864 WHERE invoiceid = ?
2866 my $sth = $dbh->prepare($query);
2867 return ( $sth->execute($invoiceid) > 0 );
2869 return;
2872 =head3 MergeInvoices
2874 MergeInvoices($invoiceid, \@sourceids);
2876 Merge the invoices identified by the IDs in \@sourceids into
2877 the invoice identified by $invoiceid.
2879 =cut
2881 sub MergeInvoices {
2882 my ($invoiceid, $sourceids) = @_;
2884 return unless $invoiceid;
2885 foreach my $sourceid (@$sourceids) {
2886 next if $sourceid == $invoiceid;
2887 my $source = GetInvoiceDetails($sourceid);
2888 foreach my $order (@{$source->{'orders'}}) {
2889 $order->{'invoiceid'} = $invoiceid;
2890 ModOrder($order);
2892 DelInvoice($source->{'invoiceid'});
2894 return;
2897 =head3 GetBiblioCountByBasketno
2899 $biblio_count = &GetBiblioCountByBasketno($basketno);
2901 Looks up the biblio's count that has basketno value $basketno
2903 Returns a quantity
2905 =cut
2907 sub GetBiblioCountByBasketno {
2908 my ($basketno) = @_;
2909 my $dbh = C4::Context->dbh;
2910 my $query = "
2911 SELECT COUNT( DISTINCT( biblionumber ) )
2912 FROM aqorders
2913 WHERE basketno = ?
2914 AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')
2917 my $sth = $dbh->prepare($query);
2918 $sth->execute($basketno);
2919 return $sth->fetchrow;
2922 # Note this subroutine should be moved to Koha::Acquisition::Order
2923 # Will do when a DBIC decision will be taken.
2924 sub populate_order_with_prices {
2925 my ($params) = @_;
2927 my $order = $params->{order};
2928 my $booksellerid = $params->{booksellerid};
2929 return unless $booksellerid;
2931 my $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
2933 my $receiving = $params->{receiving};
2934 my $ordering = $params->{ordering};
2935 my $discount = $order->{discount};
2936 $discount /= 100 if $discount > 1;
2938 if ($ordering) {
2939 $order->{tax_rate_on_ordering} //= $order->{tax_rate};
2940 if ( $bookseller->listincgst ) {
2941 # The user entered the rrp tax included
2942 $order->{rrp_tax_included} = $order->{rrp};
2944 # rrp tax excluded = rrp tax included / ( 1 + tax rate )
2945 $order->{rrp_tax_excluded} = $order->{rrp_tax_included} / ( 1 + $order->{tax_rate_on_ordering} );
2947 # ecost tax excluded = rrp tax excluded * ( 1 - discount )
2948 $order->{ecost_tax_excluded} = $order->{rrp_tax_excluded} * ( 1 - $discount );
2950 # ecost tax included = rrp tax included ( 1 - discount )
2951 $order->{ecost_tax_included} = $order->{rrp_tax_included} * ( 1 - $discount );
2953 else {
2954 # The user entered the rrp tax excluded
2955 $order->{rrp_tax_excluded} = $order->{rrp};
2957 # rrp tax included = rrp tax excluded * ( 1 - tax rate )
2958 $order->{rrp_tax_included} = $order->{rrp_tax_excluded} * ( 1 + $order->{tax_rate_on_ordering} );
2960 # ecost tax excluded = rrp tax excluded * ( 1 - discount )
2961 $order->{ecost_tax_excluded} = $order->{rrp_tax_excluded} * ( 1 - $discount );
2963 # ecost tax included = rrp tax excluded * ( 1 - tax rate ) * ( 1 - discount )
2964 $order->{ecost_tax_included} =
2965 $order->{rrp_tax_excluded} *
2966 ( 1 + $order->{tax_rate_on_ordering} ) *
2967 ( 1 - $discount );
2970 # tax value = quantity * ecost tax excluded * tax rate
2971 $order->{tax_value_on_ordering} =
2972 $order->{quantity} * $order->{ecost_tax_excluded} * $order->{tax_rate_on_ordering};
2975 if ($receiving) {
2976 $order->{tax_rate_on_receiving} //= $order->{tax_rate};
2977 if ( $bookseller->invoiceincgst ) {
2978 # Trick for unitprice. If the unit price rounded value is the same as the ecost rounded value
2979 # we need to keep the exact ecost value
2980 if ( Koha::Number::Price->new( $order->{unitprice} )->round == Koha::Number::Price->new( $order->{ecost_tax_included} )->round ) {
2981 $order->{unitprice} = $order->{ecost_tax_included};
2984 # The user entered the unit price tax included
2985 $order->{unitprice_tax_included} = $order->{unitprice};
2987 # unit price tax excluded = unit price tax included / ( 1 + tax rate )
2988 $order->{unitprice_tax_excluded} = $order->{unitprice_tax_included} / ( 1 + $order->{tax_rate_on_receiving} );
2990 else {
2991 # Trick for unitprice. If the unit price rounded value is the same as the ecost rounded value
2992 # we need to keep the exact ecost value
2993 if ( Koha::Number::Price->new( $order->{unitprice} )->round == Koha::Number::Price->new( $order->{ecost_tax_excluded} )->round ) {
2994 $order->{unitprice} = $order->{ecost_tax_excluded};
2997 # The user entered the unit price tax excluded
2998 $order->{unitprice_tax_excluded} = $order->{unitprice};
3001 # unit price tax included = unit price tax included * ( 1 + tax rate )
3002 $order->{unitprice_tax_included} = $order->{unitprice_tax_excluded} * ( 1 + $order->{tax_rate_on_receiving} );
3005 # tax value = quantity * unit price tax excluded * tax rate
3006 $order->{tax_value_on_receiving} = $order->{quantity} * $order->{unitprice_tax_excluded} * $order->{tax_rate_on_receiving};
3009 return $order;
3012 =head3 GetOrderUsers
3014 $order_users_ids = &GetOrderUsers($ordernumber);
3016 Returns a list of all borrowernumbers that are in order users list
3018 =cut
3020 sub GetOrderUsers {
3021 my ($ordernumber) = @_;
3023 return unless $ordernumber;
3025 my $query = q|
3026 SELECT borrowernumber
3027 FROM aqorder_users
3028 WHERE ordernumber = ?
3030 my $dbh = C4::Context->dbh;
3031 my $sth = $dbh->prepare($query);
3032 $sth->execute($ordernumber);
3033 my $results = $sth->fetchall_arrayref( {} );
3035 my @borrowernumbers;
3036 foreach (@$results) {
3037 push @borrowernumbers, $_->{'borrowernumber'};
3040 return @borrowernumbers;
3043 =head3 ModOrderUsers
3045 my @order_users_ids = (1, 2, 3);
3046 &ModOrderUsers($ordernumber, @basketusers_ids);
3048 Delete all users from order users list, and add users in C<@order_users_ids>
3049 to this users list.
3051 =cut
3053 sub ModOrderUsers {
3054 my ( $ordernumber, @order_users_ids ) = @_;
3056 return unless $ordernumber;
3058 my $dbh = C4::Context->dbh;
3059 my $query = q|
3060 DELETE FROM aqorder_users
3061 WHERE ordernumber = ?
3063 my $sth = $dbh->prepare($query);
3064 $sth->execute($ordernumber);
3066 $query = q|
3067 INSERT INTO aqorder_users (ordernumber, borrowernumber)
3068 VALUES (?, ?)
3070 $sth = $dbh->prepare($query);
3071 foreach my $order_user_id (@order_users_ids) {
3072 $sth->execute( $ordernumber, $order_user_id );
3076 sub NotifyOrderUsers {
3077 my ($ordernumber) = @_;
3079 my @borrowernumbers = GetOrderUsers($ordernumber);
3080 return unless @borrowernumbers;
3082 my $order = GetOrder( $ordernumber );
3083 for my $borrowernumber (@borrowernumbers) {
3084 my $patron = Koha::Patrons->find( $borrowernumber );
3085 my $library = $patron->library->unblessed;
3086 my $biblio = Koha::Biblios->find( $order->{biblionumber} )->unblessed;
3087 my $letter = C4::Letters::GetPreparedLetter(
3088 module => 'acquisition',
3089 letter_code => 'ACQ_NOTIF_ON_RECEIV',
3090 branchcode => $library->{branchcode},
3091 lang => $patron->lang,
3092 tables => {
3093 'branches' => $library,
3094 'borrowers' => $patron->unblessed,
3095 'biblio' => $biblio,
3096 'aqorders' => $order,
3099 if ( $letter ) {
3100 C4::Letters::EnqueueLetter(
3102 letter => $letter,
3103 borrowernumber => $borrowernumber,
3104 LibraryName => C4::Context->preference("LibraryName"),
3105 message_transport_type => 'email',
3107 ) or warn "can't enqueue letter $letter";
3112 =head3 FillWithDefaultValues
3114 FillWithDefaultValues( $marc_record );
3116 This will update the record with default value defined in the ACQ framework.
3117 For all existing fields, if a default value exists and there are no subfield, it will be created.
3118 If the field does not exist, it will be created too.
3120 =cut
3122 sub FillWithDefaultValues {
3123 my ($record) = @_;
3124 my $tagslib = C4::Biblio::GetMarcStructure( 1, 'ACQ', { unsafe => 1 } );
3125 if ($tagslib) {
3126 my ($itemfield) =
3127 C4::Biblio::GetMarcFromKohaField( 'items.itemnumber', '' );
3128 for my $tag ( sort keys %$tagslib ) {
3129 next unless $tag;
3130 next if $tag == $itemfield;
3131 for my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
3132 next if IsMarcStructureInternal($tagslib->{$tag}{$subfield});
3133 my $defaultvalue = $tagslib->{$tag}{$subfield}{defaultvalue};
3134 if ( defined $defaultvalue and $defaultvalue ne '' ) {
3135 my @fields = $record->field($tag);
3136 if (@fields) {
3137 for my $field (@fields) {
3138 unless ( defined $field->subfield($subfield) ) {
3139 $field->add_subfields(
3140 $subfield => $defaultvalue );
3144 else {
3145 $record->insert_fields_ordered(
3146 MARC::Field->new(
3147 $tag, '', '', $subfield => $defaultvalue
3158 __END__
3160 =head1 AUTHOR
3162 Koha Development Team <http://koha-community.org/>
3164 =cut