Bug 7684: multiple fixes for inventory
authorMatthias Meusburger <matthias.meusburger@biblibre.com>
Thu, 23 Aug 2012 14:02:52 +0000 (16:02 +0200)
committerGalen Charlton <gmc@esilibrary.com>
Fri, 1 Nov 2013 00:10:49 +0000 (00:10 +0000)
* when a file was uploaded and the comparison with catalogue range
 requested, the comparison was wrong: the logic was wrong
* items that were not supposed to be scanned (ie: supposed to be on another shelf)
  didn't had the author and title, it was hard to retrieve them on the shelved
* some useful fields were missing, like homebranch, location, status
* the CSV export contained all the item information. It should contain the same
   informations as the screen

Behaviour now:
   * scan a list of barcode & select a range of location
   * if a barcode has been scanned and should not be (misplaced item),
       the information is displayed
   * if you choose "compare barcodes list to result option", the
     resulting list contains all items that have been scanned and those
     that were supposed to be. Any item not in both list appears with a
     specific message on the last column

Signed-off-by: Leila <koha.aixmarseille@gmail.com>
Signed-off-by: Koha Team Amu <koha.aixmarseille@gmail.com>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Galen Charlton <gmc@esilibrary.com>

C4/Items.pm
C4/Templates.pm
koha-tmpl/intranet-tmpl/prog/en/columns.def
koha-tmpl/intranet-tmpl/prog/en/modules/tools/inventory.tt
tools/ajax-inventory.pl [new file with mode: 0755]
tools/inventory.pl

index 456c0a0..e4c041b 100644 (file)
@@ -979,9 +979,7 @@ sub GetLostItems {
 
 =head2 GetItemsForInventory
 
-  $itemlist = GetItemsForInventory($minlocation, $maxlocation, 
-                 $location, $itemtype $datelastseen, $branch, 
-                 $offset, $size, $statushash);
+($itemlist, $iTotalRecords)  = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $offset, $size, $statushash);
 
 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
 
@@ -994,6 +992,8 @@ the datelastseen can be used to specify that you want to see items not seen sinc
 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
 $statushash requires a hashref that has the authorized values fieldname (intems.notforloan, etc...) as keys, and an arrayref of statuscodes we are searching for as values.
 
+$iTotalRecords is the number of rows that would have been returned without the $offset, $size limit clause
+
 =cut
 
 sub GetItemsForInventory {
@@ -1002,7 +1002,7 @@ sub GetItemsForInventory {
     my ( @bind_params, @where_strings );
 
     my $query = <<'END_SQL';
-SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen
+SELECT SQL_CALC_FOUND_ROWS items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, biblio.frameworkcode, datelastseen, homebranch, location, notforloan, damaged, itemlost, stocknumber
 FROM items
   LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
   LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
@@ -1045,7 +1045,7 @@ END_SQL
         }
         push @bind_params, $branchcode;
     }
-    
+
     if ( $itemtype ) {
         push @where_strings, 'biblioitems.itemtype = ?';
         push @bind_params, $itemtype;
@@ -1061,20 +1061,33 @@ END_SQL
         $query .= join ' AND ', @where_strings;
     }
     $query .= ' ORDER BY items.cn_sort, itemcallnumber, title';
+    $query .= " LIMIT $offset, $size" if ($offset and $size);
     my $sth = $dbh->prepare($query);
     $sth->execute( @bind_params );
 
     my @results;
-    $size--;
-    while ( my $row = $sth->fetchrow_hashref ) {
-        $offset-- if ($offset);
-        $row->{datelastseen}=format_date($row->{datelastseen});
-        if ( ( !$offset ) && $size ) {
-            push @results, $row;
-            $size--;
+    my $tmpresults = $sth->fetchall_arrayref({});
+    $sth = $dbh->prepare("SELECT FOUND_ROWS()");
+    $sth->execute();
+    my ($iTotalRecords) = $sth->fetchrow_array();
+
+    foreach my $row (@$tmpresults) {
+        $row->{datelastseen} = format_date( $row->{datelastseen} );
+
+        # Auth values
+        foreach (keys %$row) {
+            # If the koha field is mapped to a marc field
+            my ($f, $sf) = GetMarcFromKohaField("items.$_", $row->{'frameworkcode'});
+            if ($f and $sf) {
+                # We replace the code with it's description
+                my $authvals = C4::Koha::GetKohaAuthorisedValuesFromField($f, $sf, $row->{'frameworkcode'});
+                $row->{$_} = $authvals->{$row->{$_}} if defined $authvals->{$row->{$_}};
+            }
         }
+        push @results, $row;
     }
-    return \@results;
+
+    return (\@results, $iTotalRecords);
 }
 
 =head2 GetItemsCount
index 0b93971..2d2304f 100644 (file)
@@ -360,7 +360,7 @@ sub getlanguage {
     }
 
     # cookie
-    if ( $query->cookie('KohaOpacLanguage') ) {
+    if ($query and $query->cookie('KohaOpacLanguage') ) {
         $lang = $query->cookie('KohaOpacLanguage');
         $lang =~ s/[^a-zA-Z_-]*//; # sanitize cookie
     }
index c48fa48..dc863e4 100644 (file)
@@ -138,3 +138,4 @@ biblioitems.place   Place of publication
 biblioitems.lccn       LCCN
 biblioitems.marcxml    MARC blob
 biblioitems.url        URL
+biblioitems.title      Title
index 424928f..1052714 100644 (file)
@@ -1,30 +1,72 @@
 [% INCLUDE 'doc-head-open.inc' %]
 <title>Koha &rsaquo; Tools &rsaquo; Inventory</title>
 [% INCLUDE 'doc-head-close.inc' %]
+<link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" />
+<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.dataTables.min.js"></script>
+<script type="text/javascript" src="[% themelang %]/js/datatables.js"></script>
 [% INCLUDE 'calendar.inc' %]
 <script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
 <script type="text/javascript">
 //<![CDATA[
 $(document).ready(function(){
-       $(".checkall").click(function(){
-               $(".checkboxed").checkCheckboxes();
-               return false;
-               });
-       $(".clearall").click(function(){
-               $(".checkboxed").unCheckCheckboxes();
-               return false;
-           });
+
+        inventorydt = $('#inventoryt').dataTable($.extend(true, {}, dataTablesDefaults, {
+            'sPaginationType': 'full_numbers',
+            "aoColumnDefs": [ { "bSortable": false, "aTargets": [ 0 ] } ]
+        } ));
+
+
+    $("#continuewithoutmarkingbutton").click(function(){
+                inventorydt.fnPageChange( 'next' );
+            return false;
+        });
+
+        $("#markseenandcontinuebutton").click(function(){
+            var param = '';
+            $("input:checked").each(function() {
+                param += "|" + $(this).attr('name');
+            });
+            $.post('/cgi-bin/koha/tools/ajax-inventory.pl', { seen: param });
+            inventorydt.fnPageChange( 'next' );
+            return false;
+        });
+
+        $("#markseenandquit").click(function(){
+            var param = '';
+            $("input:checked").each(function() {
+                param += "|" + $(this).attr('name');
+            });
+            $.ajax({
+              type: 'POST',
+              url: '/cgi-bin/koha/tools/ajax-inventory.pl',
+              data: { seen: param},
+              async: false
+            });
+            document.location.href = '/cgi-bin/koha/tools/inventory.pl';
+            return false;
+        });
+
+
+
+    $(".checkall").click(function(){
+            $(".checkboxed").checkCheckboxes();
+            return false;
+        });
+    $(".clearall").click(function(){
+            $(".checkboxed").unCheckCheckboxes();
+            return false;
+        });
 [% IF ( offset ) %]$("#markseen").before("<input type=\"submit\" value=\"&lt;&lt; " + _("Mark seen and continue") + "\" id=\"markback\" /> ");[% END %]
 [% IF ( nextoffset ) %]$("#markseen").after(" <input type=\"submit\" id=\"marknext\" value=\"" + _("Mark seen and continue") + " &gt;&gt;\" />");[% END %]
-       $("#markback").click(function(){
-               $(".checkboxed").find("input").filter("[name=offset]").attr("value","[% prevoffset %]");
-               return true;
-       });
-       $("#marknext").click(function(){
-               $(".checkboxed").find("input").filter("[name=offset]").attr("value","[% nextoffset %]");
-               return true;
-       });
-       });
+    $("#markback").click(function(){
+        $(".checkboxed").find("input").filter("[name=offset]").attr("value","[% prevoffset %]");
+        return true;
+    });
+    $("#marknext").click(function(){
+        $(".checkboxed").find("input").filter("[name=offset]").attr("value","[% nextoffset %]");
+        return true;
+    });
+    });
 //]]>
 </script>
 </head>
@@ -32,34 +74,33 @@ $(document).ready(function(){
 [% INCLUDE 'header.inc' %]
 [% INCLUDE 'cat-search.inc' %]
 
-<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> &rsaquo; [% IF ( loop ) %]<a href="/cgi-bin/koha/tools/inventory.pl">Inventory</a> &rsaquo; Results[% ELSE %]Inventory[% END %]</div>
+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> &rsaquo; [% IF (loop) %]<a href="/cgi-bin/koha/tools/inventory.pl">Inventory</a> &rsaquo; Results[% ELSE %]Inventory[% END %]</div>
 
 <div id="doc3" class="yui-t2">
-   
+
    <div id="bd">
-       <div id="yui-main">
-       <div class="yui-b">
+    <div id="yui-main">
+    <div class="yui-b">
     <h1>Inventory/Stocktaking</h1>
-    [% IF ( Number ) %]<div class="dialog message">[% Number %] items modified : datelastseen set to [% date %]</div>[% END %]
-    [% IF ( errorfile ) %]<div class="dialog alert">[% errorfile %] can't be opened</div>[% END %]
-    [% FOREACH errorloo IN errorloop %]
+    [% IF (moddatecount) %]<div class="dialog message">[% moddatecount %] items modified : datelastseen set to [% date %]</div>[% END %]
+    [% IF (errorfile) %]<div class="dialog alert">[% errorfile %] can't be opened</div>[% END %]
+    [% FOREACH error IN errorloop %]
         <div class="dialog alert">
-            [% errorloo.barcode %]
-            [% IF ( errorloo.ERR_BARCODE ) %]: barcode not found[% END %]
-            [% IF ( errorloo.ERR_WTHDRAWN ) %]: item withdrawn[% END %]
-            [% IF ( errorloo.ERR_ONLOAN_RET ) %]: item was on loan. It was returned before marked as seen[% END %]
-            [% IF ( errorloo.ERR_ONLOAN_NOT_RET ) %]: item was on loan. couldn't be returned.[% END %]
+            [% error.barcode %]
+            [% IF (error.ERR_BARCODE) %]: barcode not found[% END %]
+            [% IF (error.ERR_WTHDRAWN) %]: item withdrawn[% END %]
+            [% IF (error.ERR_ONLOAN_RET) %]: item was on loan. It was returned before marked as seen[% END %]
+            [% IF (error.ERR_ONLOAN_NOT_RET) %]: item was on loan. couldn't be returned.[% END %]
         </div>
     [% END %]
-       [% UNLESS ( loop ) %]
-       <div class="yui-g">
+       [% UNLESS op %]
+    <div class="yui-g">
     <form method="post" action="/cgi-bin/koha/tools/inventory.pl" enctype="multipart/form-data">
         <fieldset class="rows">
             <legend>Use a barcode file</legend>
-        <ol>
+     <ol>
             <li><label for="uploadbarcodes">Barcode file: </label> <input type="file" id="uploadbarcodes" name="uploadbarcodes" /></li>
-            <li><label for="setdate">Set inventory date to:</label> <input type="text" id="setdate" name="setdate" value="[% today %]" class="datepicker" />
-                       <div class="hint">[% INCLUDE 'date-format.inc' %]</div>
+            <li><label for="setdate">Set inventory date to:</label> <input type="text" id="setdate" name="setdate" value="[% today %]" class="datepickerfrom" />
             </li>
           </ol>
         </fieldset>
@@ -81,43 +122,43 @@ $(document).ready(function(){
         [% END %]
         </select>
         </li>
-        [% IF ( authorised_values ) %]
+        [% IF (authorised_values) %]
         <li>
             <label for="locationloop">Shelving location (items.location) is: </label>
         <select id="locationloop" name="location">
                 <option value="">Filter location</option>
-        [% FOREACH authorised_value IN authorised_values %]
-            [% IF ( authorised_value.selected ) %]
-                <option value="[% authorised_value.authorised_value %]" selected="selected">[% authorised_value.lib %]</option>
+        [% FOREACH value IN authorised_values %]
+            [% IF (value.selected) %]
+                <option value="[% value.authorised_value %]" selected="selected">[% value.lib %]</option>
             [% ELSE %]
-                <option value="[% authorised_value.authorised_value %]">[% authorised_value.lib %]</option>
+                <option value="[% value.authorised_value %]">[% value.lib %]</option>
             [% END %]
         [% END %]
         </select>        </li>
         [% END %]
         <li>
-            <label for="minlocation">Item call number between: </label>
+            <label for="minlocation">Item callnumber between: </label>
                 <input type="text" name="minlocation" id="minlocation" value="[% minlocation %]" /> (items.itemcallnumber)  </li>
            <li><label for="maxlocation">...and: </label>
                 <input type="text" name="maxlocation" id="maxlocation" value="[% maxlocation %]" />
         </li>
-        [% IF ( statuses ) %]
-       </ol>
-       </fieldset>
+        [% IF (statuses) %]
+    </ol>
+    </fieldset>
             <fieldset class="rows">
             <legend>Item statuses</legend>
             <div name="statuses" style="display: block;">
-                  [% FOREACH statuse IN statuses %]
-                      [% IF ( statuse.values ) %]
+                  [% FOREACH status IN statuses %]
+                      [% IF (status.values) %]
                           <fieldset style="float: left; padding: 5px; margin: 5px;text-align:right">
-                              <legend>[% statuse.fieldname %]</legend>
-                              <ul id="statuses-[% statuse.fieldname %]" style="display: inline;">
-                              [% FOREACH value IN statuse.values %]
-                                  [% IF ( value.lib ) %]<li>
+                              <legend>[% status.fieldname %]</legend>
+                              <ul id="statuses-[% fieldname %]" style="display: inline;">
+                              [% FOREACH value IN status.values %]
+                                  [% IF (value.lib) %]<li>
                                     <label for="[% value.id %]">
                                       [% value.lib %]
                                     </label>
-                                    <input type="checkbox" name="status-[% value.fieldname %]-[% value.id %]" id="[% value.id %]" />
+                                    <input type="checkbox" name="status-[% status.fieldname %]-[% value.authorised_value %]" id="[% value.authorised_value %]" />
                                   </li>[% END %]
                               [% END %]
                               </ul>
@@ -126,25 +167,19 @@ $(document).ready(function(){
                   [% END %]
                 </div>
             </fieldset>
-               <fieldset class="rows">
+        <fieldset class="rows">
           <ol>
         [% END %]
 
         <li><label for="datelastseen">Inventory date:</label>
-            <input type="text" id="datelastseen" name="datelastseen" value="[% datelastseen %]" class="datepicker"/>
-                       <div class="hint">[% INCLUDE 'date-format.inc' %]</div>
+            <input type="text" id="datelastseen" name="datelastseen" value="[% datelastseen %]" class="datepickerfrom" />
         </li>
         <li><label for="ignoreissued">Skip copies on loan: </label>
-            [% IF ( ignoreissued ) %]
+            [% IF (ignoreissued) %]
             <input type="checkbox" id="ignoreissued" name="ignoreissued" checked="checked" /></li>
             [% ELSE %]
             <input type="checkbox" id="ignoreissued" name="ignoreissued" /></li>
             [% END %]
-        <li><label for="pagesize">Show: </label>
-            <input type="text" id="pagesize" name="pagesize" value="[% pagesize %]" maxlength="5" size="5" /> items</li>
-          <li><label for="offset">Beginning at offset: </label>
-            <input type="text" id="offset" name="offset" value="[% offset %]" size="5" maxlength="5" />
-        </li>
         <li>
            <label for="CSVexport">Export to CSV file: </label>
            <input type="checkbox" name="CSVexport" id="CSVexport" />
@@ -158,10 +193,10 @@ $(document).ready(function(){
             <input type="hidden" name="op" value="do_it" />
             <fieldset class="action"><input type="submit" value="Submit" class="button" /></fieldset>
     </form>
-       </div>
-       </div>
-       [% END %]
-    [% IF ( loop ) %]
+    </div>
+    </div>
+    [% END %]
+    [% IF (op) %]
     <form method="post" action="/cgi-bin/koha/tools/inventory.pl" class="checkboxed">
     <input type="hidden" name="markseen" value="1" />
     <input type="hidden" name="minlocation" value="[% minlocation %]" />
@@ -171,74 +206,65 @@ $(document).ready(function(){
     <input type="hidden" name="datelastseen" value="[% datelastseen %]" />
     <input type="hidden" name="pagesize" value="[% pagesize %]" />
     <input type="hidden" name="offset" value="[% offset %]" />
-    <div style="padding : .3em 0"><a href="#" class="checkall">[Select all]</a> <a href="#" class="clearall">[Clear all]</a></div>
-    <table>
+    <div><a href="#" class="checkall">[Select All]</a> <a href="#" class="clearall">[Clear All]</a></div>
+    <table id="inventoryt">
+    <thead>
         <tr>
             <th>Seen</th>
             <th>Barcode</th>
+            <th>Location</th>
             <th>Title</th>
+            <th>Status</th>
+            <th>Lost</th>
+            <th>Damaged</th>
             <th>Unseen since</th>
             <th>Problems</th>
         </tr>
-    [% FOREACH loo IN loop %]
+    </thead>
+    <tbody>
+    [% FOREACH result IN loop %]
         <tr>
             <td>
-                <input type="checkbox" name="SEEN-[% loo.itemnumber %]" value="1" />
+            <input type="checkbox" name="SEEN-[% result.itemnumber %]" value="1" />
             </td>
             <td>
-                [% loo.barcode %]
+            [% result.barcode | html %]
             </td>
             <td>
-                <p><b>[% loo.itemcallnumber %]</b> - <a href="#" onclick="window.open('/cgi-bin/koha/catalogue/MARCdetail.pl?biblionumber=[% loo.biblionumber %]','marcview','width=800,height=600,toolbar=0,scrollbars=1');">[% loo.title |html %]</a></p>
-                <p>[% loo.author %]</p>
+            [% result.homebranch | html %] [% result.location | html %] [[% result.itemcallnumber | html %]]
             </td>
             <td>
-                <p>[% loo.datelastseen %]</p>
+            <p><a href="#" onclick="window.open('/cgi-bin/koha/catalogue/MARCdetail.pl?biblionumber=[% result.biblionumber %]','marcview','width=800,height=600,toolbar=0,scrollbars=1');">[% result.title | html %]</a></p><p>[% result.author | html %]</p>
             </td>
             <td>
-                 [% IF ( loo.notfoundbarcode ) %]
-                     <p style="background: red;">Not found among barcodes in barcodes file.</p>
-                 [% ELSIF ( loo.notfoundkoha ) %]
-                     <p style="background: red;">Not found in Koha.</p>
-                 [% ELSE %]
-                     <p style="background: green;">None</p>
-                 [% END %]
+            [% result.notforloan | html %]
+            </td>
+            <td>
+            [% result.itemlost | html %]
+            </td>
+            <td>
+            [% result.damaged | html %]
+            </td>
+            <td>
+            [% result.datelastseen | html %]
+            </td>
+            <td>
+            [% IF (result.wrongplace) %]<p>Item should not have been scanned</p>[% ELSIF (result.missingitem) %]<p>Item missing</p>[% ELSIF (result.changestatus) %]<p>Change item status</p>[% END %]
             </td>
         </tr>
     [% END %]
+    </tbody>
     </table>
-    <div style="padding : .3em 0"><a href="#" class="checkall">[Select all]</a> <a href="#" class="clearall">[Clear all]</a></div>
-     <input type="submit" id="markseen" value="Mark seen" />
-    </form>
-    [% IF ( offset ) %]
-    <form method="post" action="/cgi-bin/koha/tools/inventory.pl">
-        <input type="hidden" name="branch" value="[% branch %]" />
-        <input type="hidden" name="minlocation" value="[% minlocation %]" />
-        <input type="hidden" name="maxlocation" value="[% maxlocation %]" />
-        <input type="hidden" name="location" value="[% location %]" />
-        <input type="hidden" name="branchcode" value="[% branchcode %]" />
-        <input type="hidden" name="datelastseen" value="[% datelastseen %]" />
-        <input type="hidden" name="pagesize" value="[% pagesize %]" />
-        <input type="hidden" name="offset" value="[% prevoffset %]" />
-        <input type="hidden" name="op" value="do_it" />
-        <input type="submit" value="&lt;&lt; Continue without marking" class="submit" />
-    </form>
-    [% END %]
-    [% IF ( nextoffset ) %]
-    <form method="post">
-        <input type="hidden" name="branch" value="[% branch %]" />
-        <input type="hidden" name="location" value="[% location %]" />
-        <input type="hidden" name="branchcode" value="[% branchcode %]" />
-        <input type="hidden" name="minlocation" value="[% minlocation %]" />
-        <input type="hidden" name="maxlocation" value="[% maxlocation %]" />
-        <input type="hidden" name="datelastseen" value="[% datelastseen %]" />
-        <input type="hidden" name="pagesize" value="[% pagesize %]" />
-        <input type="hidden" name="offset" value="[% nextoffset %]" />
-        <input type="hidden" name="op" value="do_it" />
-        <input type="submit" value="Continue without marking &gt;&gt;" class="submit" />
+    <div class="spacer"></div>
+      <p style="display:block;"><span class="exportSelected"></span></p>
+    <div style="padding : .3em 0"><a href="#" class="checkall">[Select All]</a> <a href="#" class="clearall">[Clear All]</a></div>
+     <input type="submit" id="markseenandquit" value="Mark seen and quit" />
+     <input type="submit" value="Mark Seen and Continue &gt;&gt;" id="markseenandcontinuebutton" />
+     <input type="submit" value="Continue without Marking &gt;&gt;" id="continuewithoutmarkingbutton" class="submit" />
     </form>
+
     </div>
-    [% END %]
+
     [% END %]
 </div>
 <div class="yui-b">
diff --git a/tools/ajax-inventory.pl b/tools/ajax-inventory.pl
new file mode 100755 (executable)
index 0000000..24739b9
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+#
+
+use Modern::Perl;
+
+use CGI;
+use JSON;
+
+use C4::Auth;
+use C4::Circulation qw/CanBookBeRenewed/;
+use C4::Context;
+use C4::Koha qw/getitemtypeimagelocation/;
+use C4::Reserves qw/CheckReserves/;
+use C4::Utils::DataTables;
+use C4::Output;
+use C4::Biblio;
+use C4::Items;
+use C4::Dates qw/format_date format_date_in_iso/;
+use C4::Koha;
+use C4::Branch;    # GetBranches
+use C4::Reports::Guided;    #_get_column_defs
+use C4::Charset;
+use List::MoreUtils qw /none/;
+
+
+my $input = new CGI;
+
+# Authentication
+my ($status, $cookie, $sessionId) = C4::Auth::check_api_auth($input, { tools => 'inventory' });
+exit unless ($status eq "ok");
+
+
+my $seen = $input->param('seen');
+my @seent = split(/\|/, $seen);
+
+# mark seen if applicable (ie: coming form mark seen checkboxes)
+foreach ( @seent ) {
+    /SEEN-(.+)/ and &ModDateLastSeen($1);
+}
+
+
+print $input->header('application/json');
+
index 824906b..9a80017 100755 (executable)
@@ -35,6 +35,10 @@ use C4::Dates qw/format_date format_date_in_iso/;
 use C4::Koha;
 use C4::Branch; # GetBranches
 use C4::Circulation;
+use C4::Reports::Guided;    #_get_column_defs
+use C4::Charset;
+use List::MoreUtils qw/none/;
+
 
 my $minlocation=$input->param('minlocation') || '';
 my $maxlocation=$input->param('maxlocation');
@@ -50,26 +54,27 @@ my $pagesize = $input->param('pagesize');
 $pagesize=50 unless $pagesize;
 my $branchcode = $input->param('branchcode') || '';
 my $branch     = $input->param('branch');
-my $op = $input->param('op');
-my $res;    #contains the results loop
-# warn "uploadbarcodes : ".$uploadbarcodes;
-# use Data::Dumper; warn Dumper($input);
-
-my ($template, $borrowernumber, $cookie)
-    = get_template_and_user({template_name => "tools/inventory.tmpl",
-                query => $input,
-                type => "intranet",
-                authnotrequired => 0,
-                flagsrequired => {tools => 'inventory'},
-                debug => 1,
-                });
+my $op         = $input->param('op');
+my $compareinv2barcd = $input->param('compareinv2barcd');
+my $res;                                            #contains the results loop
+
+my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
+    {   template_name   => "tools/inventory.tmpl",
+        query           => $input,
+        type            => "intranet",
+        authnotrequired => 0,
+        flagsrequired   => { tools => 'inventory' },
+        debug           => 1,
+    }
+);
+
 
 my $branches = GetBranches();
 my @branch_loop;
 for my $branch_hash (keys %$branches) {
-       push @branch_loop, {value => "$branch_hash",
-                          branchname => $branches->{$branch_hash}->{'branchname'}, 
-                          selected => ($branch_hash eq $branchcode?1:0)};      
+    push @branch_loop, {value => "$branch_hash",
+                       branchname => $branches->{$branch_hash}->{'branchname'},
+                       selected => ($branch_hash eq $branchcode?1:0)};
 }
 
 @branch_loop = sort {$a->{branchname} cmp $b->{branchname}} @branch_loop;
@@ -87,7 +92,7 @@ for my $fwk (keys %$frameworks){
       my $data=GetAuthorisedValues($authcode);
       foreach my $value (@$data){
         $value->{selected}=1 if ($value->{authorised_value} eq ($location));
-      }      
+      }
       push @authorised_value_list,@$data;
     }
 }
@@ -103,58 +108,76 @@ for my $statfield (qw/items.notforloan items.itemlost items.withdrawn items.dama
         push @$statuses, $hash;
     }
 }
+
+
 $template->param( statuses => $statuses );
-my $staton = {};                                                               #authorized values that are ticked
+my $staton = {};                                #authorized values that are ticked
 for my $authvfield (@$statuses) {
     $staton->{$authvfield->{fieldname}} = [];
     for my $authval (@{$authvfield->{values}}){
-        if ( defined $input->param('status-' . $authvfield->{fieldname} . '-' . $authval->{id}) && $input->param('status-' . $authvfield->{fieldname} . '-' . $authval->{id}) eq 'on' ){
-            push @{$staton->{$authvfield->{fieldname}}}, $authval->{id};
+        if ( defined $input->param('status-' . $authvfield->{fieldname} . '-' . $authval->{authorised_value}) && $input->param('status-' . $authvfield->{fieldname} . '-' . $authval->{authorised_value}) eq 'on' ){
+            push @{$staton->{$authvfield->{fieldname}}}, $authval->{authorised_value};
         }
     }
 }
 
+my $notforloanlist;
 my $statussth = '';
 for my $authvfield (@$statuses) {
     if ( scalar @{$staton->{$authvfield->{fieldname}}} > 0 ){
         my $joinedvals = join ',', @{$staton->{$authvfield->{fieldname}}};
         $statussth .= "$authvfield->{fieldname} in ($joinedvals) and ";
+        $notforloanlist = $joinedvals if ($authvfield->{fieldname} eq "items.notforloan");
     }
 }
 $statussth =~ s, and $,,g;
-$template->param(branchloop => \@branch_loop,
-                authorised_values=>\@authorised_value_list,   
-                today                  =>      C4::Dates->today(),
-                minlocation => $minlocation,
-                maxlocation => $maxlocation,
-                location=>$location,
-                ignoreissued=>$ignoreissued,
-                branchcode=>$branchcode,      
-                branch    => $branch,
-                offset => $offset,
-                pagesize => $pagesize,
-                datelastseen => $datelastseen,
-                );
+$template->param(
+    branchloop               => \@branch_loop,
+    authorised_values        => \@authorised_value_list,
+    today                    => C4::Dates->today(),
+    minlocation              => $minlocation,
+    maxlocation              => $maxlocation,
+    location                 => $location,
+    ignoreissued             => $ignoreissued,
+    branchcode               => $branchcode,
+    branch                   => $branch,
+    offset                   => $offset,
+    pagesize                 => $pagesize,
+    datelastseen             => $datelastseen,
+    compareinv2barcd         => $compareinv2barcd,
+    notforloanlist           => $notforloanlist
+);
+
+my @notforloans;
+if (defined $notforloanlist) {
+    @notforloans = split(/,/, $notforloanlist);
+}
+
+
+
 my @brcditems;
-if ($uploadbarcodes && length($uploadbarcodes)>0){
-    my $dbh=C4::Context->dbh;
-    my $date = format_date_in_iso($input->param('setdate')) || C4::Dates->today('iso');
-#      warn "$date";
-    my $strsth="select * from issues, items where items.itemnumber=issues.itemnumber and items.barcode =?";
+my $barcodelist;
+my @errorloop;
+if ( $uploadbarcodes && length($uploadbarcodes) > 0 ) {
+    my $dbh = C4::Context->dbh;
+    my $date = format_date_in_iso( $input->param('setdate') ) || C4::Dates->today('iso');
+
+    my $strsth  = "select * from issues, items where items.itemnumber=issues.itemnumber and items.barcode =?";
     my $qonloan = $dbh->prepare($strsth);
     $strsth="select * from items where items.barcode =? and items.withdrawn = 1";
     my $qwithdrawn = $dbh->prepare($strsth);
-    my @errorloop;
-    my $count=0;
+
+    my $count = 0;
+
     while (my $barcode=<$uploadbarcodes>){
         $barcode =~ s/\r?\n$//;
-        if ($qwithdrawn->execute($barcode) &&$qwithdrawn->rows){
-            push @errorloop, {'barcode'=>$barcode,'ERR_WTHDRAWN'=>1};
-        }else{
-            my $item = GetItem('', $barcode);
-            if (defined $item && $item->{'itemnumber'}){
-                ModItem({ datelastseen => $date }, undef, $item->{'itemnumber'});
+        $barcodelist .= ($barcodelist) ? '|' . $barcode : $barcode;
+        if ( $qwithdrawn->execute($barcode) && $qwithdrawn->rows ) {
+            push @errorloop, { 'barcode' => $barcode, 'ERR_WTHDRAWN' => 1 };
+        } else {
+            my $item = GetItem( '', $barcode );
+            if ( defined $item && $item->{'itemnumber'} ) {
+                ModItem( { datelastseen => $date }, undef, $item->{'itemnumber'} );
                 push @brcditems, $item;
                 $count++;
                 $qonloan->execute($barcode);
@@ -171,48 +194,101 @@ if ($uploadbarcodes && length($uploadbarcodes)>0){
                 push @errorloop, {'barcode'=>$barcode,'ERR_BARCODE'=>1};
             }
         }
+
     }
     $qonloan->finish;
     $qwithdrawn->finish;
-    $template->param(date=>format_date($date),Number=>$count);
-#      $template->param(errorfile=>$errorfile) if ($errorfile);
-    $template->param(errorloop=>\@errorloop) if (@errorloop);
+    $template->param( date => format_date($date), Number => $count );
+    $template->param( errorloop => \@errorloop ) if (@errorloop);
+
 }
-#if we want to compare the results to a list of barcodes, or we have no barcode file
-if ( ! ($uploadbarcodes && length($uploadbarcodes)>0 ) || ( $input->param('compareinv2barcd') eq 'on' && length($uploadbarcodes)>0) ) {
-    if ($markseen) {
-        foreach ($input->param) {
-            /SEEN-(.+)/ and &ModDateLastSeen($1);
-        }
+$template->param(barcodelist => $barcodelist);
+
+# now build the result list: inventoried items if requested, and mis-placed items -always-
+my $inventorylist;
+if ( $markseen or $op ) {
+    # retrieve all items in this range.
+    my $totalrecords;
+    ($inventorylist, $totalrecords) = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype, $ignoreissued, '', $branchcode, $branch, 0, undef , $staton);
+
+    # Real copy
+    my @res_copy;
+    foreach (@$inventorylist) {
+        push @res_copy, $_;
     }
-    if ($markseen or $op) {
-        $res = GetItemsForInventory( $minlocation, $maxlocation, $location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $branch, $offset, $pagesize, $staton );
-        $template->param(loop =>$res,
-                        nextoffset => ($offset+$pagesize),
-                        prevoffset => ($offset?$offset-$pagesize:0),
-                        );
-    }
-    if ( defined $input->param('compareinv2barcd') && ( ( $input->param('compareinv2barcd') eq 'on' ) && ( scalar @brcditems != scalar @$res ) ) && length($uploadbarcodes) > 0 ){
-        if ( scalar @brcditems > scalar @$res ){
-            for my $brcditem (@brcditems) {
-                if (! grep( $_->{barcode} =~ /$brcditem->{barcode}/ , @$res) ){
-                    $brcditem->{notfoundkoha} = 1;
-                    push @$res, $brcditem;
-                }
-            }
-        } else {
-            my @notfound;
-            for my $item (@$res) {
-                if ( ! grep( $_->{barcode} =~ /$item->{barcode}/ , @brcditems) ){
-                    $item->{notfoundbarcode} = 1;
-                    push @notfound, $item;
-                }
+    $res = \@res_copy;
+}
+
+# set "missing" flags for all items with a datelastseen before the choosen datelastseen
+foreach (@$res) { $_->{missingitem}=1 if C4::Dates->new($_->{datelastseen})->output('iso') lt C4::Dates->new($datelastseen)->output('iso'); }
+
+# removing missing items from loop if "Compare barcodes list to results" has not been checked
+@$res = grep {!$_->{missingitem} == 1 } @$res if (!$input->param('compareinv2barcd'));
+
+# insert "wrongplace" to all scanned items that are not supposed to be in this range
+# note this list is always displayed, whatever the librarian has choosen for comparison
+foreach my $temp (@brcditems) {
+
+  # Saving notforloan code before it's replaced by it's authorised value for later comparison
+  $temp->{'notforloancode'} = $temp->{'notforloan'};
+
+  # Populating with authorised values
+  foreach (keys %$temp) {
+        # If the koha field is mapped to a marc field
+        my $fc = $temp->{'frameworkcode'} || '';
+        my ($f, $sf) = GetMarcFromKohaField("items.$_", $fc);
+        if ($f and $sf) {
+            # We replace the code with it's description
+            my $authvals = C4::Koha::GetKohaAuthorisedValuesFromField($f, $sf, $fc);
+            if ($authvals and defined $temp->{$_} and defined $authvals->{$temp->{$_}}) {
+              $temp->{$_} = $authvals->{$temp->{$_}};
             }
-            $res = [@$res, @notfound];
         }
     }
+
+    next if $temp->{onloan}; # skip checked out items
+
+    # If we have scanned items with a non-matching notforloan value
+    if (none { $temp->{'notforloancode'} eq $_ } @notforloans) {
+        $temp->{'changestatus'} = 1;
+        my $biblio = C4::Biblio::GetBiblioData($temp->{biblionumber});
+        $temp->{title} = $biblio->{title};
+        $temp->{author} = $biblio->{author};
+        $temp->{datelastseen} = format_date($temp->{datelastseen});
+        push @$res, $temp;
+
+    }
+    if (none { $temp->{barcode} eq $_->{barcode} && !$_->{'onloan'} } @$inventorylist) {
+        my $temp2 = { %$temp };
+        $temp2->{wrongplace}=1;
+        my $biblio = C4::Biblio::GetBiblioData($temp->{biblionumber});
+        $temp2->{title} = $biblio->{title};
+        $temp2->{author} = $biblio->{author};
+        $temp2->{datelastseen} = format_date($temp->{datelastseen});
+        push @$res, $temp2;
+    }
+}
+
+# Finally, modifying datelastseen for remaining items
+my $moddatecount = 0;
+foreach (@$res) {
+    unless ($_->{'missingitem'}) {
+        ModDateLastSeen($_->{'itemnumber'});
+        $moddatecount++;
+    }
 }
 
+# Removing items that don't have any problems from loop
+@$res = grep { $_->{missingitem} || $_->{wrongplace} || $_->{changestatus} } @$res;
+
+$template->param(
+    moddatecount => $moddatecount,
+    loop       => $res,
+    nextoffset => ( $offset + $pagesize ),
+    prevoffset => ( $offset ? $offset - $pagesize : 0 ),
+    op         => $op
+);
+
 if (defined $input->param('CSVexport') && $input->param('CSVexport') eq 'on'){
     eval {use Text::CSV};
     my $csv = Text::CSV->new or
@@ -221,14 +297,55 @@ if (defined $input->param('CSVexport') && $input->param('CSVexport') eq 'on'){
         -type       => 'text/csv',
         -attachment => 'inventory.csv',
     );
-    for my $re (@$res){
+
+    my $columns_def_hashref = C4::Reports::Guided::_get_column_defs();
+    foreach my $key ( keys %$columns_def_hashref ) {
+        my $initkey = $key;
+        $key =~ s/[^\.]*\.//;
+        $columns_def_hashref->{$initkey}=NormalizeString($columns_def_hashref->{$initkey});
+        $columns_def_hashref->{$key} = $columns_def_hashref->{$initkey};
+    }
+
+    my @translated_keys;
+    for my $key (qw / biblioitems.title    biblio.author
+                      items.barcode        items.itemnumber
+                      items.homebranch     items.location
+                      items.itemcallnumber items.notforloan
+                      items.itemlost       items.damaged
+                      items.stocknumber
+                      / ) {
+       push @translated_keys, $columns_def_hashref->{$key};
+    }
+
+    $csv->combine(@translated_keys);
+    print $csv->string, "\n";
+
+    my @keys = qw / title author barcode itemnumber homebranch location itemcallnumber notforloan lost damaged stocknumber /;
+    for my $re (@$res) {
         my @line;
-        for my $key (keys %$re) {
+        for my $key (@keys) {
             push @line, $re->{$key};
         }
+        if ($re->{wrongplace}) {
+            push @line, "wrong place";
+        } elsif ($re->{missingitem}) {
+            push @line, "missing item";
+        } elsif ($re->{changestatus}) {
+            push @line, "change item status";
+        }
+        $csv->combine(@line);
+        print $csv->string, "\n";
+    }
+    # Adding not found barcodes
+    foreach my $error (@errorloop) {
+    my @line;
+    if ($error->{'ERR_BARCODE'}) {
+        push @line, map { $_ eq 'barcode' ? $error->{'barcode'} : ''} @keys;
+        push @line, "barcode not found";
         $csv->combine(@line);
         print $csv->string, "\n";
     }
+    }
     exit;
 }