Bug 7684: (follow-up) fix UTF-8 encoding problems in CSV export
[koha.git] / C4 / Acquisition.pm
index 4536902..aabc0ed 100644 (file)
@@ -28,7 +28,7 @@ use MARC::Record;
 use C4::Suggestions;
 use C4::Biblio;
 use C4::Debug;
-use C4::SQLHelper qw(InsertInTable);
+use C4::SQLHelper qw(InsertInTable UpdateInTable);
 use C4::Bookseller qw(GetBookSellerFromId);
 use C4::Templates qw(gettemplate);
 
@@ -43,11 +43,14 @@ BEGIN {
     require Exporter;
     @ISA    = qw(Exporter);
     @EXPORT = qw(
-        &GetBasket &NewBasket &CloseBasket &DelBasket &ModBasket
+        &GetBasket &NewBasket &CloseBasket &ReopenBasket &DelBasket &ModBasket
         &GetBasketAsCSV &GetBasketGroupAsCSV
         &GetBasketsByBookseller &GetBasketsByBasketgroup
         &GetBasketsInfosByBookseller
 
+        &GetBasketUsers &ModBasketUsers
+        &CanUserManageBasket
+
         &ModBasketHeader
 
         &ModBasketgroup &NewBasketgroup &DelBasketgroup &GetBasketgroup &CloseBasketgroup
@@ -72,10 +75,12 @@ BEGIN {
         &CloseInvoice
         &ReopenInvoice
         &DelInvoice
+        &MergeInvoices
 
         &GetItemnumbersFromOrder
 
         &AddClaim
+        &GetBiblioCountByBasketno
     );
 }
 
@@ -157,8 +162,7 @@ sub GetBasket {
     my $dbh        = C4::Context->dbh;
     my $query = "
         SELECT  aqbasket.*,
-                concat( b.firstname,' ',b.surname) AS authorisedbyname,
-                b.branchcode AS branch
+                concat( b.firstname,' ',b.surname) AS authorisedbyname
         FROM    aqbasket
         LEFT JOIN borrowers b ON aqbasket.authorisedby=b.borrowernumber
         WHERE basketno=?
@@ -215,7 +219,7 @@ sub NewBasket {
 
   &CloseBasket($basketno);
 
-close a basket (becomes unmodifiable,except for recieves)
+close a basket (becomes unmodifiable, except for receives)
 
 =cut
 
@@ -229,6 +233,48 @@ sub CloseBasket {
     ";
     my $sth = $dbh->prepare($query);
     $sth->execute($basketno);
+
+    my @orders = GetOrders($basketno);
+    foreach my $order (@orders) {
+        $query = qq{
+            UPDATE aqorders
+            SET orderstatus = 'ordered'
+            WHERE ordernumber = ?;
+        };
+        $sth = $dbh->prepare($query);
+        $sth->execute($order->{'ordernumber'});
+    }
+}
+
+=head3 ReopenBasket
+
+  &ReopenBasket($basketno);
+
+reopen a basket
+
+=cut
+
+sub ReopenBasket {
+    my ($basketno) = @_;
+    my $dbh        = C4::Context->dbh;
+    my $query = "
+        UPDATE aqbasket
+        SET    closedate=NULL
+        WHERE  basketno=?
+    ";
+    my $sth = $dbh->prepare($query);
+    $sth->execute($basketno);
+
+    my @orders = GetOrders($basketno);
+    foreach my $order (@orders) {
+        $query = qq{
+            UPDATE aqorders
+            SET orderstatus = 'new'
+            WHERE ordernumber = ?;
+        };
+        $sth = $dbh->prepare($query);
+        $sth->execute($order->{'ordernumber'});
+    }
 }
 
 #------------------------------------------------------------#
@@ -294,7 +340,7 @@ sub GetBasketAsCSV {
 
 =head3 GetBasketGroupAsCSV
 
-=over 4
+=over
 
 &GetBasketGroupAsCSV($basketgroupid);
 
@@ -468,6 +514,7 @@ sub ModBasket {
     my $dbh = C4::Context->dbh;
     my $sth = $dbh->prepare($query);
     $sth->execute(@params);
+
     $sth->finish;
 }
 
@@ -611,6 +658,149 @@ sub GetBasketsInfosByBookseller {
     return $sth->fetchall_arrayref({});
 }
 
+=head3 GetBasketUsers
+
+    $basketusers_ids = &GetBasketUsers($basketno);
+
+Returns a list of all borrowernumbers that are in basket users list
+
+=cut
+
+sub GetBasketUsers {
+    my $basketno = shift;
+
+    return unless $basketno;
+
+    my $query = qq{
+        SELECT borrowernumber
+        FROM aqbasketusers
+        WHERE basketno = ?
+    };
+    my $dbh = C4::Context->dbh;
+    my $sth = $dbh->prepare($query);
+    $sth->execute($basketno);
+    my $results = $sth->fetchall_arrayref( {} );
+    $sth->finish();
+
+    my @borrowernumbers;
+    foreach (@$results) {
+        push @borrowernumbers, $_->{'borrowernumber'};
+    }
+
+    return @borrowernumbers;
+}
+
+=head3 ModBasketUsers
+
+    my @basketusers_ids = (1, 2, 3);
+    &ModBasketUsers($basketno, @basketusers_ids);
+
+Delete all users from basket users list, and add users in C<@basketusers_ids>
+to this users list.
+
+=cut
+
+sub ModBasketUsers {
+    my ($basketno, @basketusers_ids) = @_;
+
+    return unless $basketno;
+
+    my $dbh = C4::Context->dbh;
+    my $query = qq{
+        DELETE FROM aqbasketusers
+        WHERE basketno = ?
+    };
+    my $sth = $dbh->prepare($query);
+    $sth->execute($basketno);
+    $sth->finish();
+
+    $query = qq{
+        INSERT INTO aqbasketusers (basketno, borrowernumber)
+        VALUES (?, ?)
+    };
+    $sth = $dbh->prepare($query);
+    foreach my $basketuser_id (@basketusers_ids) {
+        $sth->execute($basketno, $basketuser_id);
+    }
+}
+
+=head3 CanUserManageBasket
+
+    my $bool = CanUserManageBasket($borrower, $basket[, $userflags]);
+    my $bool = CanUserManageBasket($borrowernumber, $basketno[, $userflags]);
+
+Check if a borrower can manage a basket, according to system preference
+AcqViewBaskets, user permissions and basket properties (creator, users list,
+branch).
+
+First parameter can be either a borrowernumber or a hashref as returned by
+C4::Members::GetMember.
+
+Second parameter can be either a basketno or a hashref as returned by
+C4::Acquisition::GetBasket.
+
+The third parameter is optional. If given, it should be a hashref as returned
+by C4::Auth::getuserflags. If not, getuserflags is called.
+
+If user is authorised to manage basket, returns 1.
+Otherwise returns 0.
+
+=cut
+
+sub CanUserManageBasket {
+    my ($borrower, $basket, $userflags) = @_;
+
+    if (!ref $borrower) {
+        $borrower = C4::Members::GetMember(borrowernumber => $borrower);
+    }
+    if (!ref $basket) {
+        $basket = GetBasket($basket);
+    }
+
+    return 0 unless ($basket and $borrower);
+
+    my $borrowernumber = $borrower->{borrowernumber};
+    my $basketno = $basket->{basketno};
+
+    my $AcqViewBaskets = C4::Context->preference('AcqViewBaskets');
+
+    if (!defined $userflags) {
+        my $dbh = C4::Context->dbh;
+        my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE borrowernumber = ?");
+        $sth->execute($borrowernumber);
+        my ($flags) = $sth->fetchrow_array;
+        $sth->finish;
+
+        $userflags = C4::Auth::getuserflags($flags, $borrower->{userid}, $dbh);
+    }
+
+    unless ($userflags->{superlibrarian}
+    || (ref $userflags->{acquisition} && $userflags->{acquisition}->{order_manage_all})
+    || (!ref $userflags->{acquisition} && $userflags->{acquisition}))
+    {
+        if (not exists $userflags->{acquisition}) {
+            return 0;
+        }
+
+        if ( (ref $userflags->{acquisition} && !$userflags->{acquisition}->{order_manage})
+        || (!ref $userflags->{acquisition} && !$userflags->{acquisition}) ) {
+            return 0;
+        }
+
+        if ($AcqViewBaskets eq 'user'
+        && $basket->{authorisedby} != $borrowernumber
+        && grep($borrowernumber, GetBasketUsers($basketno)) == 0) {
+            return 0;
+        }
+
+        if ($AcqViewBaskets eq 'branch' && defined $basket->{branch}
+        && $basket->{branch} ne $borrower->{branchcode}) {
+            return 0;
+        }
+    }
+
+    return 1;
+}
 
 #------------------------------------------------------------#
 
@@ -921,17 +1111,43 @@ C<$order> are fields from the biblio, biblioitems, aqorders tables of the Koha d
 sub GetOrder {
     my ($ordernumber) = @_;
     my $dbh      = C4::Context->dbh;
-    my $query = "
-        SELECT biblioitems.*, biblio.*, aqorders.*
-        FROM   aqorders
-        LEFT JOIN biblio on           biblio.biblionumber=aqorders.biblionumber
-        LEFT JOIN biblioitems on       biblioitems.biblionumber=aqorders.biblionumber
-        WHERE aqorders.ordernumber=?
-
-    ";
+    my $query = qq{SELECT
+                aqorders.*,
+                biblio.title,
+                biblio.author,
+                aqbasket.basketname,
+                borrowers.branchcode,
+                biblioitems.publicationyear,
+                biblio.copyrightdate,
+                biblioitems.editionstatement,
+                biblioitems.isbn,
+                biblioitems.ean,
+                biblio.seriestitle,
+                biblioitems.publishercode,
+                aqorders.rrp              AS unitpricesupplier,
+                aqorders.ecost            AS unitpricelib,
+                aqorders.claims_count     AS claims_count,
+                aqorders.claimed_date     AS claimed_date,
+                aqbudgets.budget_name     AS budget,
+                aqbooksellers.name        AS supplier,
+                aqbooksellers.id          AS supplierid,
+                biblioitems.publishercode AS publisher,
+                ADDDATE(aqbasket.closedate, INTERVAL aqbooksellers.deliverytime DAY) AS estimateddeliverydate,
+                DATE(aqbasket.closedate)  AS orderdate,
+                aqorders.quantity - COALESCE(aqorders.quantityreceived,0)                 AS quantity_to_receive,
+                (aqorders.quantity - COALESCE(aqorders.quantityreceived,0)) * aqorders.rrp AS subtotal,
+                DATEDIFF(CURDATE( ),closedate) AS latesince
+                FROM aqorders LEFT JOIN biblio ON biblio.biblionumber = aqorders.biblionumber
+                LEFT JOIN biblioitems ON biblioitems.biblionumber = biblio.biblionumber
+                LEFT JOIN aqbudgets ON aqorders.budget_id = aqbudgets.budget_id,
+                aqbasket LEFT JOIN borrowers  ON aqbasket.authorisedby = borrowers.borrowernumber
+                LEFT JOIN aqbooksellers       ON aqbasket.booksellerid = aqbooksellers.id
+                WHERE aqorders.basketno = aqbasket.basketno
+                    AND ordernumber=?};
     my $sth= $dbh->prepare($query);
     $sth->execute($ordernumber);
     my $data = $sth->fetchrow_hashref;
+    $data->{orderdate} = format_date( $data->{orderdate} );
     $sth->finish;
     return $data;
 }
@@ -1031,7 +1247,7 @@ The following keys are used: "biblionumber", "title", "basketno", "quantity", "n
 
 sub NewOrder {
     my $orderinfo = shift;
-#### ------------------------------
+
     my $dbh = C4::Context->dbh;
     my @params;
 
@@ -1258,7 +1474,8 @@ sub ModReceiveOrder {
         # (entirely received)
         $sth=$dbh->prepare("
             UPDATE aqorders
-            SET quantity = ?
+            SET quantity = ?,
+                orderstatus = 'partial'
             WHERE ordernumber = ?
         ");
 
@@ -1267,6 +1484,7 @@ sub ModReceiveOrder {
         $sth->finish;
 
         delete $order->{'ordernumber'};
+        $order->{'budget_id'} = ( $budget_id || $order->{'budget_id'} );
         $order->{'quantity'} = $quantrec;
         $order->{'quantityreceived'} = $quantrec;
         $order->{'datereceived'} = $datereceived;
@@ -1274,7 +1492,7 @@ sub ModReceiveOrder {
         $order->{'unitprice'} = $cost;
         $order->{'rrp'} = $rrp;
         $order->{ecost} = $ecost;
-        $order->{'orderstatus'} = 3;    # totally received
+        $order->{'orderstatus'} = 'complete';
         my $basketno;
         ( $basketno, $new_ordernumber ) = NewOrder($order);
 
@@ -1286,9 +1504,9 @@ sub ModReceiveOrder {
     } else {
         $sth=$dbh->prepare("update aqorders
                             set quantityreceived=?,datereceived=?,invoiceid=?,
-                                unitprice=?,rrp=?,ecost=?
+                                unitprice=?,rrp=?,ecost=?,budget_id=?,orderstatus='complete'
                             where biblionumber=? and ordernumber=?");
-        $sth->execute($quantrec,$datereceived,$invoiceid,$cost,$rrp,$ecost,$biblionumber,$ordernumber);
+        $sth->execute($quantrec,$datereceived,$invoiceid,$cost,$rrp,$ecost,$budget_id,$biblionumber,$ordernumber);
         $sth->finish;
     }
     return ($datereceived, $new_ordernumber);
@@ -1336,7 +1554,8 @@ sub CancelReceipt {
             UPDATE aqorders
             SET quantityreceived = ?,
                 datereceived = ?,
-                invoiceid = ?
+                invoiceid = ?,
+                orderstatus = 'ordered'
             WHERE ordernumber = ?
         };
         $sth = $dbh->prepare($query);
@@ -1363,7 +1582,8 @@ sub CancelReceipt {
         }
         $query = qq{
             UPDATE aqorders
-            SET quantity = ?
+            SET quantity = ?,
+                orderstatus = 'ordered'
             WHERE ordernumber = ?
         };
         $sth = $dbh->prepare($query);
@@ -1543,7 +1763,7 @@ sub DelOrder {
     my $dbh = C4::Context->dbh;
     my $query = "
         UPDATE aqorders
-        SET    datecancellationprinted=now()
+        SET    datecancellationprinted=now(), orderstatus='cancelled'
         WHERE  biblionumber=? AND ordernumber=?
     ";
     my $sth = $dbh->prepare($query);
@@ -1894,6 +2114,7 @@ sub GetLateOrders {
         $from .= ' AND borrowers.branchcode LIKE ? ';
         push @query_params, C4::Context->userenv->{branch};
     }
+    $from .= " AND orderstatus <> 'cancelled' ";
     my $query = "$select $from $having\nORDER BY latesince, basketno, borrowers.branchcode, supplier";
     $debug and print STDERR "GetLateOrders query: $query\nGetLateOrders args: " . join(" ",@query_params);
     my $sth = $dbh->prepare($query);
@@ -1962,6 +2183,9 @@ sub GetHistory {
     my $basket = $params{basket};
     my $booksellerinvoicenumber = $params{booksellerinvoicenumber};
     my $basketgroupname = $params{basketgroupname};
+    my $budget = $params{budget};
+    my $orderstatus = $params{orderstatus};
+
     my @order_loop;
     my $total_qty         = 0;
     my $total_qtyreceived = 0;
@@ -1970,10 +2194,10 @@ sub GetHistory {
     my $dbh   = C4::Context->dbh;
     my $query ="
         SELECT
-            biblio.title,
-            biblio.author,
-           biblioitems.isbn,
-        biblioitems.ean,
+            COALESCE(biblio.title,     deletedbiblio.title)     AS title,
+            COALESCE(biblio.author,    deletedbiblio.author)    AS author,
+            COALESCE(biblioitems.isbn, deletedbiblioitems.isbn) AS isbn,
+            COALESCE(biblioitems.ean,  deletedbiblioitems.ean)  AS ean,
             aqorders.basketno,
             aqbasket.basketname,
             aqbasket.basketgroupid,
@@ -1988,19 +2212,32 @@ sub GetHistory {
             aqorders.invoiceid,
             aqinvoices.invoicenumber,
             aqbooksellers.id as id,
-            aqorders.biblionumber
+            aqorders.biblionumber,
+            aqorders.orderstatus,
+            aqorders.parent_ordernumber,
+            aqbudgets.budget_name
+            ";
+    $query .= ", aqbudgets.budget_id AS budget" if defined $budget;
+    $query .= "
         FROM aqorders
         LEFT JOIN aqbasket ON aqorders.basketno=aqbasket.basketno
         LEFT JOIN aqbasketgroups ON aqbasket.basketgroupid=aqbasketgroups.id
         LEFT JOIN aqbooksellers ON aqbasket.booksellerid=aqbooksellers.id
-       LEFT JOIN biblioitems ON biblioitems.biblionumber=aqorders.biblionumber
+        LEFT JOIN biblioitems ON biblioitems.biblionumber=aqorders.biblionumber
         LEFT JOIN biblio ON biblio.biblionumber=aqorders.biblionumber
-    LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid";
+        LEFT JOIN aqbudgets ON aqorders.budget_id=aqbudgets.budget_id
+        LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
+        LEFT JOIN deletedbiblio ON deletedbiblio.biblionumber=aqorders.biblionumber
+        LEFT JOIN deletedbiblioitems ON deletedbiblioitems.biblionumber=aqorders.biblionumber
+        ";
+
+    if ( C4::Context->preference("IndependentBranches") ) {
+        $query .= " LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber";
+    }
 
-    $query .= " LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber"
-    if ( C4::Context->preference("IndependentBranches") );
+    $query .= " WHERE 1 ";
 
-    $query .= " WHERE (datecancellationprinted is NULL or datecancellationprinted='0000-00-00') ";
+    $query .= " AND (datecancellationprinted is NULL or datecancellationprinted='0000-00-00') " if $orderstatus ne 'cancelled';
 
     my @query_params  = ();
 
@@ -2019,7 +2256,7 @@ sub GetHistory {
         $query .= " AND biblioitems.isbn LIKE ? ";
         push @query_params, "%$isbn%";
     }
-    if ( defined $ean and $ean ) {
+    if ( $ean ) {
         $query .= " AND biblioitems.ean = ? ";
         push @query_params, "$ean";
     }
@@ -2028,6 +2265,11 @@ sub GetHistory {
         push @query_params, "%$name%";
     }
 
+    if ( $budget ) {
+        $query .= " AND aqbudgets.budget_id = ? ";
+        push @query_params, "$budget";
+    }
+
     if ( $from_placed_on ) {
         $query .= " AND creationdate >= ? ";
         push @query_params, $from_placed_on;
@@ -2038,6 +2280,11 @@ sub GetHistory {
         push @query_params, $to_placed_on;
     }
 
+    if ( defined $orderstatus and $orderstatus ne '') {
+        $query .= " AND aqorders.orderstatus = ? ";
+        push @query_params, "$orderstatus";
+    }
+
     if ($basket) {
         if ($basket =~ m/^\d+$/) {
             $query .= " AND aqorders.basketno = ? ";
@@ -2073,9 +2320,9 @@ sub GetHistory {
         $line->{count} = $cnt++;
         $line->{toggle} = 1 if $cnt % 2;
         push @order_loop, $line;
-        $total_qty         += $line->{'quantity'};
-        $total_qtyreceived += $line->{'quantityreceived'};
-        $total_price       += $line->{'quantity'} * $line->{'ecost'};
+        $total_qty         += ( $line->{quantity} ) ? $line->{quantity} : 0;
+        $total_qtyreceived += ( $line->{quantityreceived} ) ? $line->{quantityreceived} : 0;
+        $total_price       += ( $line->{quantity} and $line->{ecost} ) ? $line->{quantity} * $line->{ecost} : 0;
     }
     return \@order_loop, $total_qty, $total_price, $total_qtyreceived;
 }
@@ -2176,7 +2423,7 @@ sub GetContract {
 
 =head3 AddClaim
 
-=over 4
+=over
 
 &AddClaim($ordernumber);
 
@@ -2185,6 +2432,7 @@ Add a claim for an order
 =back
 
 =cut
+
 sub AddClaim {
     my ($ordernumber) = @_;
     my $dbh          = C4::Context->dbh;
@@ -2372,7 +2620,7 @@ sub GetInvoiceDetails {
     }
 
     my $dbh = C4::Context->dbh;
-    my $query = qq{
+    my $query = q{
         SELECT aqinvoices.*, aqbooksellers.name AS suppliername
         FROM aqinvoices
           LEFT JOIN aqbooksellers ON aqinvoices.booksellerid = aqbooksellers.id
@@ -2383,9 +2631,8 @@ sub GetInvoiceDetails {
 
     my $invoice = $sth->fetchrow_hashref;
 
-    $query = qq{
-        SELECT aqorders.*, biblio.*,
-        aqbasket.basketname
+    $query = q{
+        SELECT aqorders.*, biblio.*, aqbasket.basketname
         FROM aqorders
           LEFT JOIN aqbasket ON aqorders.basketno = aqbasket.basketno
           LEFT JOIN biblio ON aqorders.biblionumber = biblio.biblionumber
@@ -2574,6 +2821,56 @@ sub DelInvoice {
     return;
 }
 
+=head3 MergeInvoices
+
+    MergeInvoices($invoiceid, \@sourceids);
+
+Merge the invoices identified by the IDs in \@sourceids into
+the invoice identified by $invoiceid.
+
+=cut
+
+sub MergeInvoices {
+    my ($invoiceid, $sourceids) = @_;
+
+    return unless $invoiceid;
+    foreach my $sourceid (@$sourceids) {
+        next if $sourceid == $invoiceid;
+        my $source = GetInvoiceDetails($sourceid);
+        foreach my $order (@{$source->{'orders'}}) {
+            $order->{'invoiceid'} = $invoiceid;
+            ModOrder($order);
+        }
+        DelInvoice($source->{'invoiceid'});
+    }
+    return;
+}
+
+=head3 GetBiblioCountByBasketno
+
+$biblio_count = &GetBiblioCountByBasketno($basketno);
+
+Looks up the biblio's count that has basketno value $basketno
+
+Returns a quantity
+
+=cut
+
+sub GetBiblioCountByBasketno {
+    my ($basketno) = @_;
+    my $dbh          = C4::Context->dbh;
+    my $query        = "
+        SELECT COUNT( DISTINCT( biblionumber ) )
+        FROM   aqorders
+        WHERE  basketno = ?
+            AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')
+        ";
+
+    my $sth = $dbh->prepare($query);
+    $sth->execute($basketno);
+    return $sth->fetchrow;
+}
+
 1;
 __END__