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>.
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
;
38 use Koha
::Number
::Price
;
40 use Koha
::CsvProfiles
;
50 use vars
qw(@ISA @EXPORT);
56 &GetBasket &NewBasket &CloseBasket &ReopenBasket &ModBasket
57 &GetBasketAsCSV &GetBasketGroupAsCSV
58 &GetBasketsByBookseller &GetBasketsByBasketgroup
59 &GetBasketsInfosByBookseller
61 &GetBasketUsers &ModBasketUsers
66 &ModBasketgroup &NewBasketgroup &DelBasketgroup &GetBasketgroup &CloseBasketgroup
67 &GetBasketgroups &ReOpenBasketgroup
69 &ModOrder &GetOrder &GetOrders &GetOrdersByBiblionumber
70 &GetOrderFromItemnumber
71 &SearchOrders &GetHistory &GetRecentAcqui
72 &ModReceiveOrder &CancelReceipt
89 &GetBiblioCountByBasketno
95 &FillWithDefaultValues
106 sub GetOrderFromItemnumber
{
107 my ($itemnumber) = @_;
108 my $dbh = C4
::Context
->dbh;
111 SELECT
* from aqorders LEFT JOIN aqorders_items
112 ON
( aqorders
.ordernumber
= aqorders_items
.ordernumber
)
113 WHERE itemnumber
= ?
|;
115 my $sth = $dbh->prepare($query);
119 $sth->execute($itemnumber);
121 my $order = $sth->fetchrow_hashref;
128 C4::Acquisition - Koha functions for dealing with orders and acquisitions
136 The functions in this module deal with acquisitions, managing book
137 orders, basket and parcels.
141 =head2 FUNCTIONS ABOUT BASKETS
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.
155 my $dbh = C4
::Context
->dbh;
158 concat( b.firstname,' ',b.surname) AS authorisedbyname
160 LEFT JOIN borrowers b ON aqbasket.authorisedby=b.borrowernumber
163 my $sth=$dbh->prepare($query);
164 $sth->execute($basketno);
165 my $basket = $sth->fetchrow_hashref;
169 #------------------------------------------------------------#
173 $basket = &NewBasket( $booksellerid, $authorizedby, $basketname,
174 $basketnote, $basketbooksellernote, $basketcontractnumber, $deliveryplace, $billingplace, $is_standing, $create_items );
176 Create a new basket in aqbasket table
180 =item C<$booksellerid> is a foreign key in the aqbasket table
182 =item C<$authorizedby> is the username of who created the basket
186 The other parameters are optional, see ModBasketHeader for more info on them.
191 my ( $booksellerid, $authorisedby, $basketname, $basketnote,
192 $basketbooksellernote, $basketcontractnumber, $deliveryplace,
193 $billingplace, $is_standing, $create_items ) = @_;
194 my $dbh = C4
::Context
->dbh;
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
203 $basketbooksellernote ||= q{};
204 ModBasketHeader
( $basket, $basketname, $basketnote, $basketbooksellernote,
205 $basketcontractnumber, $booksellerid, $deliveryplace, $billingplace, $is_standing, $create_items );
209 #------------------------------------------------------------#
213 &CloseBasket($basketno);
215 close a basket (becomes unmodifiable, except for receives)
221 my $dbh = C4
::Context
->dbh;
222 $dbh->do('UPDATE aqbasket SET closedate=now() WHERE basketno=?', {}, $basketno );
225 q{UPDATE aqorders SET orderstatus = 'ordered' WHERE basketno = ? AND orderstatus NOT IN ( 'complete', 'cancelled')},
233 &ReopenBasket($basketno);
241 my $dbh = C4
::Context
->dbh;
242 $dbh->do( q{UPDATE aqbasket SET closedate=NULL WHERE basketno=?}, {}, $basketno );
246 SET orderstatus = 'new'
248 AND orderstatus NOT IN ( 'complete', 'cancelled' )
253 #------------------------------------------------------------#
255 =head3 GetBasketAsCSV
257 &GetBasketAsCSV($basketno);
259 Export a basket as CSV
261 $cgi parameter is needed for column name translation
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);
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 =~ /
285 ([^\
|]*) # fieldname (table.row or row)
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) {
300 my $biblio = Koha
::Biblios
->find( $order->{biblionumber
} );
301 my $biblioitem = $biblio->biblioitem;
302 $order = { %$order, %{ $biblioitem->unblessed } };
304 $order = {%$order, %$contract};
306 $order = {%$order, %$basket, %{ $biblio->unblessed }};
307 for my $field (@fields) {
308 push @row, $order->{$field};
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";
321 foreach my $order (@orders) {
322 my $biblio = Koha
::Biblios
->find( $order->{biblionumber
} );
323 my $biblioitem = $biblio->biblioitem;
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
344 contractname author title publishercode collectiontitle notes
345 deliveryplace billingplace
347 # Double the quotes to not be interpreted as a field end
348 $row->{$_} =~ s/"/""/g if $row->{$_};
354 if(defined $a->{publishercode
} and defined $b->{publishercode
}) {
355 $a->{publishercode
} cmp $b->{publishercode
};
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
376 sub GetBasketGroupAsCSV
{
377 my ($basketgroupid, $cgi) = @_;
378 my $baskets = GetBasketsByBasketgroup
($basketgroupid);
380 my $template = C4
::Templates
::gettemplate
('acqui/csv/basketgroup.tt', 'intranet', $cgi);
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;
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
},
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;
430 basketname author title publishercode collectiontitle notes
431 booksellername bookselleraddress booksellerpostal contractname
432 basketgroupdeliveryplace basketgroupbillingplace
433 basketdeliveryplace basketbillingplace
435 # Double the quotes to not be interpreted as a field end
436 $row->{$_} =~ s/"/""/g if $row->{$_};
441 $template->param(rows
=> \
@rows);
443 return $template->output;
447 =head3 CloseBasketgroup
449 &CloseBasketgroup($basketgroupno);
455 sub CloseBasketgroup
{
456 my ($basketgroupno) = @_;
457 my $dbh = C4
::Context
->dbh;
458 my $sth = $dbh->prepare("
459 UPDATE aqbasketgroups
463 $sth->execute($basketgroupno);
466 #------------------------------------------------------------#
468 =head3 ReOpenBaskergroup($basketgroupno)
470 &ReOpenBaskergroup($basketgroupno);
476 sub ReOpenBasketgroup
{
477 my ($basketgroupno) = @_;
478 my $dbh = C4
::Context
->dbh;
479 my $sth = $dbh->prepare("
480 UPDATE aqbasketgroups
484 $sth->execute($basketgroupno);
487 #------------------------------------------------------------#
491 &ModBasket($basketinfo);
493 Modifies a basket, using a hashref $basketinfo for the relevant information, only $basketinfo->{'basketno'} is required.
497 =item C<$basketno> is the primary key of the basket in the aqbasket table.
504 my $basketinfo = shift;
505 my $query = "UPDATE aqbasket SET ";
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 ', '){
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);
528 #------------------------------------------------------------#
530 =head3 ModBasketHeader
532 &ModBasketHeader($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid);
534 Modifies a basket's header.
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).
563 sub ModBasketHeader
{
564 my ($basketno, $basketname, $note, $booksellernote, $contractnumber, $booksellerid, $deliveryplace, $billingplace, $is_standing, $create_items) = @_;
569 SET basketname
=?
, note
=?
, booksellernote
=?
, booksellerid
=?
, deliveryplace
=?
, billingplace
=?
, is_standing
=?
, create_items
=?
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);
585 #------------------------------------------------------------#
587 =head3 GetBasketsByBookseller
589 @results = &GetBasketsByBookseller($booksellerid, $extra);
591 Returns a list of hashes of all the baskets that belong to bookseller 'booksellerid'.
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)
608 sub GetBasketsByBookseller
{
609 my ($booksellerid, $extra) = @_;
610 my $query = "SELECT * FROM aqbasket WHERE booksellerid=?";
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
643 sub GetBasketsInfosByBookseller
{
644 my ($supplierid, $allbaskets) = @_;
646 return unless $supplierid;
648 my $dbh = C4
::Context
->dbh;
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,
653 IF ( aqorders.orderstatus = 'cancelled', aqorders.quantity, 0 )
654 ) AS total_items_cancelled,
655 COUNT(DISTINCT aqorders.biblionumber) AS total_biblios,
657 IF(aqorders.datereceived IS NULL
658 AND aqorders.datecancellationprinted IS NULL
662 SUM( aqorders.uncertainprice ) AS uncertainprices
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 (
673 IF(aqorders.datereceived IS NULL
674 AND aqorders.datecancellationprinted IS NULL
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
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 );
694 $_->{total_biblios_cancelled
} = $cancelled_biblios->{$_->{basketno
}}{total_biblios_cancelled
} || 0
700 =head3 GetBasketUsers
702 $basketusers_ids = &GetBasketUsers($basketno);
704 Returns a list of all borrowernumbers that are in basket users list
709 my $basketno = shift;
711 return unless $basketno;
714 SELECT borrowernumber
718 my $dbh = C4
::Context
->dbh;
719 my $sth = $dbh->prepare($query);
720 $sth->execute($basketno);
721 my $results = $sth->fetchall_arrayref( {} );
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>
742 my ($basketno, @basketusers_ids) = @_;
744 return unless $basketno;
746 my $dbh = C4
::Context
->dbh;
748 DELETE FROM aqbasketusers
751 my $sth = $dbh->prepare($query);
752 $sth->execute($basketno);
755 INSERT INTO aqbasketusers
(basketno
, borrowernumber
)
758 $sth = $dbh->prepare($query);
759 foreach my $basketuser_id (@basketusers_ids) {
760 $sth->execute($basketno, $basketuser_id);
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,
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.
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;
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;
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
}) {
826 if ( (ref $userflags->{acquisition
} && !$userflags->{acquisition
}->{order_manage
})
827 || (!ref $userflags->{acquisition
} && !$userflags->{acquisition
}) ) {
831 if ($AcqViewBaskets eq 'user'
832 && $basket->{authorisedby
} != $borrowernumber
833 && ! grep { $borrowernumber eq $_ } GetBasketUsers
($basketno)) {
837 if ($AcqViewBaskets eq 'branch' && defined $basket->{branch
}
838 && $basket->{branch
} ne $borrower->{branchcode
}) {
846 #------------------------------------------------------------#
848 =head3 GetBasketsByBasketgroup
850 $baskets = &GetBasketsByBasketgroup($basketgroupid);
852 Returns a reference to all baskets that belong to basketgroup $basketgroupid.
856 sub GetBasketsByBasketgroup
{
857 my $basketgroupid = shift;
859 SELECT
*, aqbasket
.booksellerid as booksellerid
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.
896 my $basketgroupinfo = shift;
897 die "booksellerid is required to create a basketgroup" unless $basketgroupinfo->{'booksellerid'};
898 my $query = "INSERT INTO aqbasketgroups (";
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 (";
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.
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 ";
958 foreach my $field (qw(name billingplace deliveryplace freedeliveryplace deliverycomment closed)) {
959 if ( defined $basketgroupinfo->{$field} ) {
960 $query .= "$field=?, ";
961 push(@params, $basketgroupinfo->{$field});
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);
983 #------------------------------------------------------------#
985 =head3 DelBasketgroup
987 DelBasketgroup($basketgroupid);
989 Deletes a basketgroup in the aqbasketgroups table, and removes the reference to it from the baskets,
993 =item C<$basketgroupid> is the 'id' field of the basket in the aqbasketgroup table
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);
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.
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=?',
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.
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
1060 @orders = &GetOrders( $basketno, { orderby => 'biblio.title', cancelled => 0|1 } );
1062 Looks up the pending (non-cancelled) orders with the given basket
1065 If cancelled is set, only cancelled orders will be returned.
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;
1079 SELECT biblio
.*,biblioitems
.*,
1083 $query .= $cancelled
1085 aqorders_transfers
.ordernumber_to AS transferred_to
,
1086 aqorders_transfers
.timestamp AS transferred_to_timestamp
1089 aqorders_transfers
.ordernumber_from AS transferred_from
,
1090 aqorders_transfers
.timestamp AS transferred_from_timestamp
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
1100 LEFT JOIN aqorders_transfers ON aqorders_transfers
.ordernumber_from
= aqorders
.ordernumber
1103 LEFT JOIN aqorders_transfers ON aqorders_transfers
.ordernumber_to
= aqorders
.ordernumber
1111 $orderby ||= q
|biblioitems
.publishercode
, biblio
.title
|;
1113 AND datecancellationprinted IS NOT NULL
1118 q
|aqorders
.datecancellationprinted desc
, aqorders
.timestamp desc
|;
1120 AND datecancellationprinted IS NULL
1124 $query .= " ORDER BY $orderby";
1126 $dbh->selectall_arrayref( $query, { Slice
=> {} }, $basketno );
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.
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.
1146 sub GetOrdersByBiblionumber
{
1147 my $biblionumber = shift;
1148 return unless $biblionumber;
1149 my $dbh = C4
::Context
->dbh;
1151 SELECT biblio.*,biblioitems.*,
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=?
1161 $dbh->selectall_arrayref( $query, { Slice
=> {} }, $biblionumber );
1162 return @
{$result_set};
1166 #------------------------------------------------------------#
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.
1180 my ($ordernumber) = @_;
1181 return unless $ordernumber;
1183 my $dbh = C4
::Context
->dbh;
1184 my $query = qq{SELECT
1188 aqbasket
.basketname
,
1189 borrowers
.branchcode
,
1190 biblioitems
.publicationyear
,
1191 biblio
.copyrightdate
,
1192 biblioitems
.editionstatement
,
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
1216 $dbh->selectall_arrayref( $query, { Slice
=> {} }, $ordernumber );
1218 # result_set assumed to contain 1 match
1219 return $result_set->[0];
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.
1234 my $orderinfo = shift;
1236 die "Ordernumber is required" if $orderinfo->{'ordernumber'} eq '';
1238 my $dbh = C4
::Context
->dbh;
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;");
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);
1269 #------------------------------------------------------------#
1273 ModItemOrder($itemnumber, $ordernumber);
1275 Modifies the ordernumber of an item in aqorders_items.
1280 my ($itemnumber, $ordernumber) = @_;
1282 return unless ($itemnumber and $ordernumber);
1284 my $dbh = C4
::Context
->dbh;
1286 UPDATE aqorders_items
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,
1302 quantityreceived => $quantityreceived,
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
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}>.
1322 sub ModReceiveOrder
{
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',
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
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)
1365 orderstatus
= 'partial'|;
1366 $query .= q
| WHERE ordernumber
= ?
|;
1367 my $sth = $dbh->prepare($query);
1370 ( $is_standing ?
1 : ($order->{quantity
} - $quantrec) ),
1371 $order->{ordernumber
}
1374 if ( not $order->{subscriptionid
} && defined $order->{order_internalnote
} ) {
1377 SET order_internalnote
= ?
1378 WHERE ordernumber
= ?
|, {},
1379 $order->{order_internalnote
}, $order->{ordernumber
}
1383 # Recalculate tax_value
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);
1415 SET quantityreceived
= ?
,
1419 orderstatus
= 'complete'
1423 , replacementprice
= ?
1424 | if defined $order->{replacementprice
};
1427 , unitprice
= ?
, unitprice_tax_included
= ?
, unitprice_tax_excluded
= ?
1428 | if defined $order->{unitprice
};
1431 ,tax_value_on_receiving
= ?
1432 | if defined $order->{tax_value_on_receiving
};
1435 ,tax_rate_on_receiving
= ?
1436 | if defined $order->{tax_rate_on_receiving
};
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
1484 If items are created at receipt (AcqCreateItem = receiving) then delete
1490 my $ordernumber = shift;
1492 return unless $ordernumber;
1494 my $dbh = C4
::Context
->dbh;
1496 SELECT datereceived
, parent_ordernumber
, quantity
1498 WHERE ordernumber
= ?
1500 my $sth = $dbh->prepare($query);
1501 $sth->execute($ordernumber);
1502 my $order = $sth->fetchrow_hashref;
1504 warn "CancelReceipt: order $ordernumber does not exist";
1507 unless($order->{'datereceived'}) {
1508 warn "CancelReceipt: order $ordernumber is not received";
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
1521 SET quantityreceived
= ?
,
1524 orderstatus
= 'ordered'
1525 WHERE ordernumber
= ?
1527 $sth = $dbh->prepare($query);
1528 $sth->execute(0, undef, undef, $ordernumber);
1529 _cancel_items_receipt
( $order_obj );
1531 # The order line has a parent, increase parent quantity and delete
1533 unless ( $order_obj->basket->is_standing ) {
1535 SELECT quantity
, datereceived
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.";
1546 if($parent_order->{'datereceived'}) {
1547 warn "CancelReceipt: parent order is received.".
1548 " Can't cancel receipt.";
1554 orderstatus
= 'ordered'
1555 WHERE ordernumber
= ?
1557 $sth = $dbh->prepare($query);
1558 my $rv = $sth->execute(
1559 $order->{'quantity'} + $parent_order->{'quantity'},
1563 warn "Cannot update parent order line, so do not cancel".
1568 # Recalculate tax_value
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 );
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");
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
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);
1630 while ( my $item = $items->next ) {
1631 ModItemOrder
($item->itemnumber, $parent_ordernumber);
1636 #------------------------------------------------------------#
1640 @results = &SearchOrders({
1641 ordernumber => $ordernumber,
1644 booksellerid => $booksellerid,
1645 basketno => $basketno,
1646 basketname => $basketname,
1647 basketgroupname => $basketgroupname,
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.
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;
1687 SELECT aqbasket.basketno,
1689 borrowers.firstname,
1692 biblioitems.biblioitemnumber,
1693 biblioitems.publishercode,
1694 biblioitems.publicationyear,
1695 aqbasket.authorisedby,
1696 aqbasket.booksellerid,
1698 aqbasket.creationdate,
1699 aqbasket.basketname,
1700 aqbasketgroups.id as basketgroupid,
1701 aqbasketgroups.name as basketgroupname,
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.
1713 LEFT JOIN aqorders_transfers ON aqorders_transfers.ordernumber_to = aqorders.ordernumber
1717 WHERE (datecancellationprinted is NULL)
1720 if ( $pending or $ordered ) {
1723 ( aqbasket.is_standing AND aqorders.orderstatus IN ( "new", "ordered", "partial" ) )
1725 ( quantity > quantityreceived OR quantityreceived is NULL )
1729 $query .= q{ AND aqorders.orderstatus IN ( "ordered", "partial" )};
1737 my $userenv = C4
::Context
->userenv;
1738 if ( C4
::Context
->preference("IndependentBranches") ) {
1739 unless ( C4
::Context
->IsSuperLibrarian() ) {
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;
1759 $query .= ' AND (biblio.title LIKE ? OR biblio.author LIKE ? OR biblioitems.isbn LIKE ?)';
1760 push @args, ("%$search%","%$search%","%$search%");
1763 $query .= ' AND biblioitems.ean = ?';
1766 if ( $booksellerid ) {
1767 $query .= 'AND aqbasket.booksellerid = ?';
1768 push @args, $booksellerid;
1771 $query .= 'AND aqbasket.basketno = ?';
1772 push @args, $basketno;
1775 $query .= 'AND aqbasket.basketname LIKE ?';
1776 push @args, "%$basketname%";
1778 if( $basketgroupname ) {
1779 $query .= ' AND aqbasketgroups.name LIKE ?';
1780 push @args, "%$basketgroupname%";
1784 $query .= ' AND aqbasket.authorisedby=? ';
1785 push @args, $userenv->{'number'};
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.
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);
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;
1847 UPDATE aqorders_items
1849 WHERE ordernumber = ?
1851 $sth = $dbh->prepare($query);
1852 $sth->execute($newordernumber, $ordernumber);
1855 INSERT INTO aqorders_transfers (ordernumber_from, ordernumber_to)
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.
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.
1889 sub get_rounded_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();
1899 =head2 FUNCTIONS ABOUT PARCELS
1903 $results = &GetParcels($bookseller, $order, $code, $datefrom, $dateto);
1905 get a lists of parcels.
1912 is the bookseller this function has to get parcels.
1915 To know on what criteria the results list has to be ordered.
1918 is the booksellerinvoicenumber.
1920 =item $datefrom & $dateto
1921 to know on what date this function has to filter its search.
1926 a pointer on a hash list containing parcel informations as such :
1932 =item Last operation
1934 =item Number of biblio
1936 =item Number of items
1943 my ($bookseller,$order, $code, $datefrom, $dateto) = @_;
1944 my $dbh = C4
::Context
->dbh;
1945 my @query_params = ();
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({});
1987 #------------------------------------------------------------#
1991 \@order_loop = GetHistory( %params );
1993 Retreives some acquisition history information
2003 basket - search both basket name and number
2004 booksellerinvoicenumber
2007 orderstatus (note that orderstatus '' will retrieve orders
2008 of any status except cancelled)
2012 get_canceled_order (if set to a true value, cancelled orders will
2016 $order_loop is a list of hashrefs that each look like this:
2018 'author' => 'Twain, Mark',
2020 'biblionumber' => '215',
2022 'creationdate' => 'MM/DD/YYYY',
2023 'datereceived' => undef,
2026 'invoicenumber' => undef,
2028 'ordernumber' => '1',
2030 'quantityreceived' => undef,
2031 'title' => 'The Adventures of Huckleberry Finn',
2032 'managing_library' => 'CPL'
2033 'is_standing' => '1'
2039 # don't run the query if there are no parameters (list would be too long for sure !)
2040 croak
"No search params" unless @_;
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
} // [];
2065 my $total_qtyreceived = 0;
2066 my $total_price = 0;
2068 #get variation of isbn
2072 if ( C4
::Context
->preference("SearchWithISBNVariations") ){
2073 @isbns = C4
::Koha
::GetVariationsOfISBN
( $isbn );
2074 foreach my $isb (@isbns){
2075 push @isbn_params, '?';
2080 push @isbn_params, '?';
2084 my $dbh = C4
::Context
->dbh;
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,
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,
2100 aqbasket.creationdate,
2101 aqorders.datereceived,
2103 aqorders.quantityreceived,
2105 aqorders.ordernumber,
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;
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;
2143 $query .= " AND biblio.title LIKE ? ";
2144 $title =~ s/\s+/%/g;
2145 push @query_params, "%$title%";
2149 $query .= " AND biblio.author LIKE ? ";
2150 push @query_params, "%$author%";
2154 $query .= " AND ( biblioitems.isbn LIKE " . join (" OR biblioitems.isbn LIKE ", @isbn_params ) . ")";
2155 foreach my $isb (@isbns){
2156 push @query_params, "%$isb%";
2161 $query .= " AND biblioitems.ean = ? ";
2162 push @query_params, "$ean";
2165 $query .= " AND aqbooksellers.name LIKE ? ";
2166 push @query_params, "%$name%";
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;
2195 if ($basket =~ m/^\d+$/) {
2196 $query .= " AND aqorders.basketno = ? ";
2197 push @query_params, $basket;
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%";
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;
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
2266 sub GetRecentAcqui
{
2268 my $dbh = C4
::Context
->dbh;
2272 ORDER BY timestamp DESC
2275 my $sth = $dbh->prepare($query);
2277 my $results = $sth->fetchall_arrayref({});
2281 #------------------------------------------------------------#
2285 &AddClaim($ordernumber);
2287 Add a claim for an order
2292 my ($ordernumber) = @_;
2293 my $dbh = C4
::Context
->dbh;
2296 claims_count = claims_count + 1,
2297 claimed_date = CURDATE()
2298 WHERE ordernumber = ?
2300 my $sth = $dbh->prepare($query);
2301 $sth->execute($ordernumber);
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,
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
2336 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2337 closedate shipmentcost shipmentcost_budgetid);
2339 my $dbh = C4
::Context
->dbh;
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
,
2345 aqorders
.datereceived IS NOT NULL
,
2346 aqorders
.biblionumber
,
2349 ) AS receivedbiblios
,
2352 aqorders
.subscriptionid IS NOT NULL
,
2353 aqorders
.subscriptionid
,
2356 ) AS is_linked_to_subscriptions
,
2357 SUM
(aqorders
.quantityreceived
) AS receiveditems
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
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
};
2403 push @bind_strs, " biblio.title LIKE CONCAT('%', ?, '%') ";
2404 push @bind_args, $args{title
};
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({});
2447 my $invoice = GetInvoice($invoiceid);
2449 Get informations about invoice with given $invoiceid
2451 Return a hash filled with aqinvoices.* fields
2456 my ($invoiceid) = @_;
2459 return unless $invoiceid;
2461 my $dbh = C4
::Context
->dbh;
2467 my $sth = $dbh->prepare($query);
2468 $sth->execute($invoiceid);
2470 $invoice = $sth->fetchrow_hashref;
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)
2484 sub GetInvoiceDetails
{
2485 my ($invoiceid) = @_;
2487 if ( !defined $invoiceid ) {
2488 carp
'GetInvoiceDetails called without an invoiceid';
2492 my $dbh = C4
::Context
->dbh;
2494 SELECT aqinvoices.*, aqbooksellers.name AS suppliername
2496 LEFT JOIN aqbooksellers ON aqinvoices.booksellerid = aqbooksellers.id
2499 my $sth = $dbh->prepare($query);
2500 $sth->execute($invoiceid);
2502 my $invoice = $sth->fetchrow_hashref;
2507 biblio.copyrightdate,
2509 biblioitems.publishercode,
2510 biblioitems.publicationyear,
2511 aqbasket.basketname,
2512 aqbasketgroups.id AS basketgroupid,
2513 aqbasketgroups.name AS basketgroupname
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
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
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.
2548 return unless(%invoice and $invoice{invoicenumber
});
2550 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2551 closedate shipmentcost shipmentcost_budgetid message_id);
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);
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);
2570 $rv = $dbh->last_insert_id(undef, undef, 'aqinvoices', undef);
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.
2598 return unless(%invoice and $invoice{invoiceid
});
2600 my @columns = qw(invoicenumber booksellerid shipmentdate billingdate
2601 closedate shipmentcost shipmentcost_budgetid);
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
});
2623 CloseInvoice($invoiceid);
2627 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => undef);
2632 my ($invoiceid) = @_;
2634 return unless $invoiceid;
2636 my $dbh = C4
::Context
->dbh;
2639 SET closedate
= CAST
(NOW
() AS DATE
)
2642 my $sth = $dbh->prepare($query);
2643 $sth->execute($invoiceid);
2646 =head3 ReopenInvoice
2648 ReopenInvoice($invoiceid);
2652 Equivalent to ModInvoice(invoiceid => $invoiceid, closedate => output_pref({ dt=>dt_from_string, dateonly=>1, otputpref=>'iso' }))
2657 my ($invoiceid) = @_;
2659 return unless $invoiceid;
2661 my $dbh = C4
::Context
->dbh;
2664 SET closedate
= NULL
2667 my $sth = $dbh->prepare($query);
2668 $sth->execute($invoiceid);
2673 DelInvoice($invoiceid);
2675 Delete an invoice if there are no items attached to it.
2680 my ($invoiceid) = @_;
2682 return unless $invoiceid;
2684 my $dbh = C4
::Context
->dbh;
2690 my $sth = $dbh->prepare($query);
2691 $sth->execute($invoiceid);
2692 my $res = $sth->fetchrow_arrayref;
2693 if ( $res && $res->[0] == 0 ) {
2695 DELETE FROM aqinvoices
2698 my $sth = $dbh->prepare($query);
2699 return ( $sth->execute($invoiceid) > 0 );
2704 =head3 MergeInvoices
2706 MergeInvoices($invoiceid, \@sourceids);
2708 Merge the invoices identified by the IDs in \@sourceids into
2709 the invoice identified by $invoiceid.
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;
2724 DelInvoice
($source->{'invoiceid'});
2729 =head3 GetBiblioCountByBasketno
2731 $biblio_count = &GetBiblioCountByBasketno($basketno);
2733 Looks up the biblio's count that has basketno value $basketno
2739 sub GetBiblioCountByBasketno
{
2740 my ($basketno) = @_;
2741 my $dbh = C4
::Context
->dbh;
2743 SELECT COUNT( DISTINCT( biblionumber ) )
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:
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
2788 sub populate_order_with_prices
{
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;
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
};
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
};
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
} );
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
};
2887 =head3 GetOrderUsers
2889 $order_users_ids = &GetOrderUsers($ordernumber);
2891 Returns a list of all borrowernumbers that are in order users list
2896 my ($ordernumber) = @_;
2898 return unless $ordernumber;
2901 SELECT borrowernumber
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>
2929 my ( $ordernumber, @order_users_ids ) = @_;
2931 return unless $ordernumber;
2933 my $dbh = C4
::Context
->dbh;
2935 DELETE FROM aqorder_users
2936 WHERE ordernumber
= ?
2938 my $sth = $dbh->prepare($query);
2939 $sth->execute($ordernumber);
2942 INSERT INTO aqorder_users
(ordernumber
, borrowernumber
)
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,
2968 'branches' => $library,
2969 'borrowers' => $patron->unblessed,
2970 'biblio' => $biblio,
2971 'aqorders' => $order,
2975 C4
::Letters
::EnqueueLetter
(
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.
3000 sub FillWithDefaultValues
{
3001 my ( $record, $params ) = @_;
3002 my $mandatory = $params->{only_mandatory
};
3003 my $tagslib = C4
::Biblio
::GetMarcStructure
( 1, 'ACQ', { unsafe
=> 1 } );
3006 C4
::Biblio
::GetMarcFromKohaField
( 'items.itemnumber' );
3007 for my $tag ( sort keys %$tagslib ) {
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);
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 );
3028 if ( $tag < 10 ) { # is_control_field
3029 $record->insert_fields_ordered(
3036 $record->insert_fields_ordered(
3038 $tag, '', '', $subfield => $defaultvalue
3054 Koha Development Team <http://koha-community.org/>