webstaff: Transit List
authorJason Etheridge <jason@esilibrary.com>
Thu, 1 Oct 2015 11:23:44 +0000 (07:23 -0400)
committerMike Rylander <mrylander@gmail.com>
Thu, 18 Aug 2016 19:34:20 +0000 (15:34 -0400)
under Administration -> Local Administration

Signed-off-by: Jason Etheridge <jason@esilibrary.com>
Signed-off-by: Galen Charlton <gmc@esilibrary.com>

Open-ILS/src/templates/staff/circ/share/t_abort_transit_dialog.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/circ/transits/list.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/circ/transits/t_list.tt2 [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/circ/services/transits.js [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/circ/transits/list.js [new file with mode: 0644]

diff --git a/Open-ILS/src/templates/staff/circ/share/t_abort_transit_dialog.tt2 b/Open-ILS/src/templates/staff/circ/share/t_abort_transit_dialog.tt2
new file mode 100644 (file)
index 0000000..7490e53
--- /dev/null
@@ -0,0 +1,32 @@
+<form ng-submit="ok()" role="form" class="form-horizontal">
+  <div class="modal-content">
+    <div class="modal-header">
+      <button type="button" class="close" 
+        ng-click="cancel($event)" aria-hidden="true">&times;</button>
+      <h4 class="modal-title">
+        [% l('Abort Transits and Reset Associated Holds') %]
+      </h4>
+    </div>
+    <div class="modal-body">
+      <span class="form-group">
+        <ng-pluralize count="num_transits"
+            when="{
+                'one': '[% l('Abort 1 transit?') %]',
+                'other' : '[% l('Abort [_1] transits?','{{num_transits}}') %]'
+            }">
+        </ng-pluralize>
+        <ng-pluralize count="num_hold_transits"
+            when="{
+                'one' : '[% l('There is 1 associated hold.') %]',
+                'other' : '[%l ('There are [_1] associated holds.','{{num_hold_transits}}') %]'
+            }">
+        </ng-pluralize>
+      </span>
+    </div>
+    <div class="modal-footer">
+      <input type="submit" class="btn btn-success" value="[% l('Abort Transit') %]"/>
+      <button class="btn btn-warning" ng-click="cancel($event)">[% l('Exit') %]</button>
+    </div>
+  </div>
+</div>
+
diff --git a/Open-ILS/src/templates/staff/circ/transits/list.tt2 b/Open-ILS/src/templates/staff/circ/transits/list.tt2
new file mode 100644 (file)
index 0000000..950a57e
--- /dev/null
@@ -0,0 +1,18 @@
+[%
+  WRAPPER "staff/base.tt2";
+  ctx.page_title = l("Transit List"); 
+  ctx.page_app = "egTransitListApp";
+%]
+
+[% BLOCK APP_JS %]
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/grid.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/ui.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/user.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/transits.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/transits/list.js"></script>
+<link rel="stylesheet" href="[% ctx.base_path %]/staff/css/circ.css" />
+[% END %]
+
+<div ng-view></div>
+
+[% END %]
diff --git a/Open-ILS/src/templates/staff/circ/transits/t_list.tt2 b/Open-ILS/src/templates/staff/circ/transits/t_list.tt2
new file mode 100644 (file)
index 0000000..47b9b01
--- /dev/null
@@ -0,0 +1,50 @@
+<div class="container-fluid" style="text-align:center">
+  <div class="alert alert-info alert-less-pad strong-text-2">
+    <span>[% l('Transit List') %]</span>
+  </div>
+</div>
+
+<span><label><input type="radio" ng-model="transit_direction" value="to"/>[% l('Transits To') %]</label></span>
+<span><label><input type="radio" ng-model="transit_direction" value="from"/>[% l('Transits From') %]</label></span>
+<br/>
+<span><label>[% l('Library: ' ) %]</label></span>
+<span><eg-org-selector selected="context_org"></eg-org-selector></span>
+<span><label>[% l('Start Date: ') %]</label><input eg-date-input ng-model="dates.start_date" /></span>
+<span><label>[% l('End Date: ') %]</label><input eg-date-input ng-model="dates.end_date" /></span>
+
+<hr/>
+
+<eg-grid
+  id-field="id"
+  idl-class="atc"
+  features="-sort,-multisort"
+  items-provider="grid_data_provider"
+  grid-controls="grid_controls"
+  persist-key="circ.transits.list"
+>
+
+  <eg-grid-menu-item handler="load_item" 
+    label="[% l('Item Status') %]"></eg-grid-menu-item>
+
+  <eg-grid-menu-item handler="abort_transit" 
+    label="[% l('Abort Transit') %]"></eg-grid-menu-item>
+
+  <eg-grid-field path='id' hidden></eg-grid-field>
+  <eg-grid-field path='target_copy.barcode'></eg-grid-field>
+  <eg-grid-field path='target_copy.circ_lib.shortname' hidden></eg-grid-field>
+  <eg-grid-field path='target_copy.call_number.label' hidden></eg-grid-field>
+  <eg-grid-field path='target_copy.call_number.record.simple_record.title'></eg-grid-field>
+  <eg-grid-field path='target_copy.call_number.record.simple_record.author' hidden></eg-grid-field>
+  <eg-grid-field path='source.shortname' label="[% l('Source Library') %]"></eg-grid-field>
+  <eg-grid-field path='dest.shortname' label="[% l('Destination Library') %]"></eg-grid-field>
+  <eg-grid-field path='source_send_time'></eg-grid-field>
+  <eg-grid-field path='dest_recv_time'></eg-grid-field>
+  <eg-grid-field path='hold_transit_copy.hold.hold_type'></eg-grid-field>
+  <eg-grid-field path='hold_transit_copy.hold.request_time' hidden></eg-grid-field>
+  <eg-grid-field path='hold_transit_copy.hold.capture_time' hidden></eg-grid-field>
+  <eg-grid-field path='hold_transit_copy.hold.expire_time' hidden></eg-grid-field>
+  <eg-grid-field path='hold_transit_copy.hold.usr.family_name' hidden></eg-grid-field>
+  <eg-grid-field path='hold_transit_copy.hold.usr.first_given_name' hidden></eg-grid-field>
+  <eg-grid-field path='hold_transit_copy.hold.usr.card.barcode' label="[% l('Patron Barcode') %]" hidden></eg-grid-field>
+</eg-grid>
+
diff --git a/Open-ILS/web/js/ui/default/staff/circ/services/transits.js b/Open-ILS/web/js/ui/default/staff/circ/services/transits.js
new file mode 100644 (file)
index 0000000..32781a1
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * Transits, yo
+ */
+
+angular.module('egCoreMod')
+
+.factory('egTransits',
+
+       ['$uibModal','$q','egCore','egConfirmDialog','egAlertDialog',
+function($uibModal , $q , egCore , egConfirmDialog , egAlertDialog) {
+
+    var service = {};
+
+    service.abort_transits = function(transits,callback) {
+       
+        return $uibModal.open({
+            templateUrl : './circ/share/t_abort_transit_dialog',
+            controller : 
+                ['$scope', '$uibModalInstance',
+                function($scope, $uibModalInstance) {
+
+                    $scope.num_transits = transits.length;
+                    $scope.num_hold_transits = 0;
+                    angular.forEach(transits, function(t) {
+                        if (t.hold_transit_copy()) {
+                            $scope.num_hold_transits++;
+                        }
+                    });
+                    
+                    $scope.cancel = function($event) {
+                        $uibModalInstance.dismiss();
+                        $event.preventDefault();
+                    }
+
+                    $scope.ok = function() {
+
+                        function abort_one() {
+                            var transit = transits.pop();
+                            if (!transit) {
+                                $uibModalInstance.close();
+                                return;
+                            }
+                            egCore.net.request(
+                                'open-ils.circ', 'open-ils.circ.transit.abort',
+                                egCore.auth.token(), { 'transitid' : transit.id() }
+                            ).then(function(resp) {
+                                if (evt = egCore.evt.parse(resp)) {
+                                    console.error('unable to abort transit: ' 
+                                        + evt.toString());
+                                }
+                                abort_one();
+                            });
+                        }
+
+                        abort_one();
+                    }
+                }
+            ]
+        }).result.then(
+            function() {
+                callback();
+            }
+        );
+    }
+
+    return service;
+}])
+;
diff --git a/Open-ILS/web/js/ui/default/staff/circ/transits/list.js b/Open-ILS/web/js/ui/default/staff/circ/transits/list.js
new file mode 100644 (file)
index 0000000..b3634c4
--- /dev/null
@@ -0,0 +1,154 @@
+angular.module('egTransitListApp', 
+    ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod'])
+
+.config(function($routeProvider, $locationProvider, $compileProvider) {
+    $locationProvider.html5Mode(true);
+    $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export
+
+    var resolver = {delay : 
+        ['egStartup', function(egStartup) {return egStartup.go()}]}
+
+    $routeProvider.when('/circ/transits/list', {
+        templateUrl: './circ/transits/t_list',
+        controller: 'TransitListCtrl',
+        resolve : resolver
+    });
+
+    $routeProvider.otherwise({redirectTo : '/circ/transits/list'});
+})
+
+.controller('TransitListCtrl',
+       ['$scope','$q','$routeParams','$window','$location','egCore','egTransits','egGridDataProvider',
+function($scope , $q , $routeParams , $window , $location , egCore , egTransits , egGridDataProvider) {
+
+    var transits = [];
+    var provider = egGridDataProvider.instance({});
+    $scope.grid_data_provider = provider;
+    $scope.transit_direction = 'to';
+
+    function init_dates() {
+        // setup date filters
+        var start = new Date(); // midnight this morning
+        start.setHours(0);
+        start.setMinutes(0);
+        var end = new Date(); // near midnight tonight
+        end.setHours(23);
+        end.setMinutes(59);
+        $scope.dates = {
+            start_date : start,
+            end_date : new Date()
+        }
+    }
+    init_dates();
+
+    function date_range() {
+        if ($scope.dates.start_date > $scope.dates.end_date) {
+            var tmp = $scope.dates.start_date;
+            $scope.dates.start_date = $scope.dates.end_date;
+            $scope.dates.end_date = tmp;
+        }
+        $scope.dates.start_date.setHours(0);
+        $scope.dates.start_date.setMinutes(0);
+        $scope.dates.end_date.setHours(23);
+        $scope.dates.end_date.setMinutes(59);
+        try {
+            var start = $scope.dates.start_date.toISOString().replace(/T.*/,'');
+            var end = $scope.dates.end_date.toISOString().replace(/T.*/,'');
+        } catch(E) { // handling empty date widgets; maybe dangerous if something else can happen
+            init_dates();
+            return date_range();
+        }
+        var today = new Date().toISOString().replace(/T.*/,'');
+        if (end == today) end = 'now';
+        return [start, end];
+    }
+
+    function load_item(transits) {
+        if (!transits) return;
+        if (!angular.isArray(transits)) transits = [transits];
+        angular.forEach(transits, function(transit) {
+            $window.open(
+                $location.path(
+                    '/cat/item/' + transit.target_copy().id()
+                ).absUrl(),
+                '_blank'
+            ).focus();
+        });
+    }
+
+    $scope.load_item = function(action, data, transits) {
+        load_item(transits);
+    }
+
+    function abort_transit(transits) {
+        if (!transits) return;
+        if (!angular.isArray(transits)) transits = [transits];
+        if (transits.length == 0) return;
+        egTransits.abort_transits( transits, refresh_page );
+    }
+
+    $scope.abort_transit = function(action, date, transits) {
+        abort_transit(transits);
+    }
+
+    $scope.grid_controls = {
+        activateItem : load_item
+    }
+
+    function refresh_page() {
+        transits = [];
+        provider.refresh();
+    }
+
+    provider.get = function(offset, count) {
+        var deferred = $q.defer();
+        var recv_index = 0;
+
+        var filter = {
+            'source_send_time' : { 'between' : date_range() }
+        };
+        if ($scope.transit_direction == 'to') { filter['dest'] = $scope.context_org.id(); }
+        if ($scope.transit_direction == 'from') { filter['source'] = $scope.context_org.id(); }
+
+        egCore.pcrud.search('atc',
+            filter, {
+                'flesh' : 5,
+                // atc -> target_copy       -> call_number -> record -> simple_record
+                // atc -> hold_transit_copy -> hold        -> usr    -> card
+                'flesh_fields' : {
+                    'atc' : ['target_copy','dest','source','hold_transit_copy'],
+                    'acp' : ['call_number','location','circ_lib'],
+                    'acn' : ['record'],
+                    'bre' : ['simple_record'],
+                    'ahtc' : ['hold'],
+                    'ahr' : ['usr'],
+                    'au' : ['card']
+                },
+                'select' : { 'bre' : ['id'] }
+            }
+        ).then(
+            deferred.resolve, null, 
+            function(transit) {
+                transits[offset + recv_index++] = transit;
+                deferred.notify(transit);
+            }
+        );
+
+        return deferred.promise;
+    }
+
+    $scope.context_org = egCore.org.get(egCore.auth.user().ws_ou());
+    $scope.$watch('context_org', function(newVal, oldVal) {
+        if (newVal && newVal != oldVal) refresh_page();
+    });
+    $scope.$watch('transit_direction', function(newVal, oldVal) {
+        if (newVal && newVal != oldVal) refresh_page();
+    });
+    $scope.$watch('dates.start_date', function(newVal, oldVal) {
+        if (newVal && newVal != oldVal) refresh_page();
+    });
+    $scope.$watch('dates.end_date', function(newVal, oldVal) {
+        if (newVal && newVal != oldVal) refresh_page();
+    });
+}])
+