--- /dev/null
+<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">×</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>
+
--- /dev/null
+<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>
+
--- /dev/null
+/**
+ * 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;
+}])
+;
--- /dev/null
+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();
+ });
+}])
+