/**
- * TODO:
- * Instead of making pcrud calls, followed by per-item server calls,
- * consider a server-side API for all of this stuff for speed, etc.
- * OR consider using all flattener-based calls.
- *
- * Consolidate the various item structures into one common,
- * authoritative structure for display and print templates.
+ * FulfILLment application.
+ * Includes pending items, inbound/outbound transits, on shelf,
+ * circulating, item status, and bib record file upload.
*/
-
angular.module('ffMain',
['ngRoute', 'egCoreMod', 'egUiMod', 'egUserMod', 'egListMod'])
.config(function($routeProvider, $locationProvider) {
-
- // The route-specified controller will not get instantiated
- // until the promise returned by this function is resolved
+ $locationProvider.html5Mode(true);
var resolver = {delay : function(egStartup) {return egStartup.go()}};
// record management UI
resolve : resolver
});
+ // item status by barcode
$routeProvider.when('/fulfillment/status/:barcode', {
templateUrl: './fulfillment/t_ill',
controller: 'ILLCtrl',
resolve : resolver
});
+ // item status, barcode pending
$routeProvider.when('/fulfillment/status', {
templateUrl: './fulfillment/t_ill',
controller: 'ILLCtrl',
resolve : resolver
});
+ // transaction-focused tabs
$routeProvider.when('/fulfillment/:orientation/:tab', {
templateUrl: './fulfillment/t_ill',
controller: 'ILLCtrl',
$routeProvider.otherwise({
redirectTo : '/fulfillment/borrower/pending'
});
-
- $locationProvider.html5Mode(true);
})
/**
// so our child controllers can access our route info
$scope.illRouteParams = $routeParams;
- $scope.itemList = egList.create({limit : 10}); // limit TBD
+ $scope.itemList = egList.create({limit : 10}); // UI?
$scope.columns = [];
$scope.addColumn = function(col) {
$scope.columns.push(col);
}
+ // sort by column name. if already sorting on the selected column,
+ // sort 'desc' instead.
+ $scope.sort = function(colname) {
+ if (typeof $scope.itemList.sort == 'string' &&
+ $scope.itemList.sort == colname) {
+ $scope.itemList.sort = {};
+ $scope.itemList.sort[colname] = 'desc';
+ } else {
+ $scope.itemList.sort = colname;
+ }
+ $scope.collect();
+ }
+
// map of flattener fields to retrieve for each query type
+ // TODO: there's some duplication here, since the fields
+ // are also defined in the template.
$scope.flatFields = {
ahr : {
- id : {path : 'id'},
- hold_id : {path : 'id'},
- request_time : {path : 'request_time'},
- expire_time : {path : 'expire_time'},
- patron_id : {path : 'usr.id'},
- patron_barcode : {path : 'usr.card.barcode'},
- patron_given_name : {path : 'usr.first_given_name'},
- patron_family_name : {path : 'usr.family_name'},
- hold_request_lib : {path : 'request_lib.shortname'}, // TODO: causes query problem, wha?
- hold_pickup_lib : {path : 'pickup_lib.shortname'},
- title : {path : 'bib_rec.bib_record.simple_record.title'},
- author : {path : 'bib_rec.bib_record.simple_record.author'},
- copy_id : {path : 'current_copy.id'},
- copy_status : {path : 'current_copy.status.name'},
- copy_barcode : {path : 'current_copy.barcode'},
- copy_circ_lib : {path : 'current_copy.circ_lib.shortname'},
- call_number : {path : 'current_copy.call_number.label'}
+ id : 'id',
+ hold_id : 'id',
+ request_time : 'request_time',
+ expire_time : 'expire_time',
+ patron_id : 'usr.id',
+ patron_barcode : 'usr.card.barcode',
+ patron_given_name : 'usr.first_given_name',
+ patron_family_name : 'usr.family_name',
+ hold_request_lib : 'request_lib.shortname',
+ hold_pickup_lib : 'pickup_lib.shortname',
+ title : 'bib_rec.bib_record.simple_record.title',
+ author : 'bib_rec.bib_record.simple_record.author',
+ copy_id : 'current_copy.id',
+ copy_status : 'current_copy.status.name',
+ copy_barcode : 'current_copy.barcode',
+ copy_circ_lib : 'current_copy.circ_lib.shortname',
+ call_number : 'current_copy.call_number.label'
},
circ : {
- id : {path : 'id'},
- circ_id : {path : 'id'},
- patron_id : {path : 'usr.id'},
- patron_barcode : {path : 'usr.card.barcode'},
- patron_given_name : {path : 'usr.first_given_name'},
- patron_family_name : {path : 'usr.family_name'},
- title : {path : 'target_copy.call_number.record.simple_record.title'},
- author : {path : 'target_copy.call_number.record.simple_record.author'},
- copy_id : {path : 'target_copy.id'},
- copy_status : {path : 'target_copy.status.name'},
- copy_barcode : {path : 'target_copy.barcode'},
- copy_circ_lib : {path : 'target_copy.circ_lib.shortname'},
- call_number : {path : 'target_copy.call_number.label'},
- circ_circ_lib : {path : 'circ_lib.shortname'},
- due_date : {path : 'due_date'},
- xact_start : {path : 'xact_start'},
- checkin_time : {path : 'checkin_time'}
+ id : 'id',
+ circ_id : 'id',
+ patron_id : 'usr.id',
+ patron_barcode : 'usr.card.barcode',
+ patron_given_name : 'usr.first_given_name',
+ patron_family_name : 'usr.family_name',
+ title : 'target_copy.call_number.record.simple_record.title',
+ author : 'target_copy.call_number.record.simple_record.author',
+ copy_id : 'target_copy.id',
+ copy_status : 'target_copy.status.name',
+ copy_barcode : 'target_copy.barcode',
+ copy_circ_lib : 'target_copy.circ_lib.shortname',
+ call_number : 'target_copy.call_number.label',
+ circ_circ_lib : 'circ_lib.shortname',
+ due_date : 'due_date',
+ xact_start : 'xact_start',
+ checkin_time : 'checkin_time'
},
atc : {
- id : {path : 'id'},
- transit_id : {path : 'id'},
- hold_id : {path : 'hold_transit_copy.hold.id'},
- hold_request_lib : {path : 'hold_transit_copy.hold.request_lib.shortname'},
- hold_pickup_lib : {path : 'hold_transit_copy.hold.pickup_lib.shortname'},
- patron_id : {path : 'hold_transit_copy.hold.usr.id'},
- patron_barcode : {path : 'hold_transit_copy.hold.usr.card.barcode'},
- patron_given_name : {path : 'hold_transit_copy.hold.usr.first_given_name'},
- patron_family_name : {path : 'hold_transit_copy.hold.usr.family_name'},
- title : {path : 'target_copy.call_number.record.simple_record.title'},
- author : {path : 'target_copy.call_number.record.simple_record.author'},
- copy_status : {path : 'target_copy.status.name'},
- copy_id : {path : 'target_copy.id'},
- copy_barcode : {path : 'target_copy.barcode'},
- copy_circ_lib : {path : 'target_copy.circ_lib.shortname'},
- call_number : {path : 'target_copy.call_number.label'},
- transit_time : {path : 'source_send_time'},
- transit_source : {path : 'source.shortname'},
- transit_dest : {path : 'dest.shortname'}
+ id : 'id',
+ transit_id : 'id',
+ hold_id : 'hold_transit_copy.hold.id',
+ hold_request_lib : 'hold_transit_copy.hold.request_lib.shortname',
+ hold_pickup_lib : 'hold_transit_copy.hold.pickup_lib.shortname',
+ patron_id : 'hold_transit_copy.hold.usr.id',
+ patron_barcode : 'hold_transit_copy.hold.usr.card.barcode',
+ patron_given_name : 'hold_transit_copy.hold.usr.first_given_name',
+ patron_family_name : 'hold_transit_copy.hold.usr.family_name',
+ title : 'target_copy.call_number.record.simple_record.title',
+ author : 'target_copy.call_number.record.simple_record.author',
+ copy_status : 'target_copy.status.name',
+ copy_id : 'target_copy.id',
+ copy_barcode : 'target_copy.barcode',
+ copy_circ_lib : 'target_copy.circ_lib.shortname',
+ call_number : 'target_copy.call_number.label',
+ transit_time : 'source_send_time',
+ transit_source : 'source.shortname',
+ transit_dest : 'dest.shortname'
}
};
- // set display=true for each flattener field
- angular.forEach($scope.flatFields, function(val) {
- angular.forEach(val, function(val2) {
- val2.display = true;
- });
- });
-
+ // apply the route-specific data loading class/query
+ // query == flattener query
$scope.setCollector = function(class_, query) {
$scope.collector = {
query : query,
}
}
+ // called on each item as it's received
$scope.setMunger = function(func) {
$scope.munger = func;
}
+ // table paging...
+ $scope.firstPage = function() {
+ $scope.itemList.offset = 0;
+ $scope.collect();
+ };
+
+ $scope.nextPage = function() {
+ $scope.itemList.incrementPage();
+ $scope.collect();
+ };
+
+ $scope.prevPage = function() {
+ $scope.itemList.decrementPage();
+ $scope.collect();
+ };
+
+ // fire the flattened search query to collect items
$scope.collect = function() {
$scope.lookupComplete = false;
- $scope.itemList.items = [];
+ $scope.itemList.resetPageData();
egNet.request(
'open-ils.fielder',
'open-ils.fielder.flattened_search',
}
- /* Actions
- * Performed on flattened items (see above)
- */
- $scope.actions = {
+ // Actions
+ // Performed on flattened items
+ $scope.actions = {};
- checkin : function(item) {
- $scope.action_pending = true;
- var deferred = $q.defer();
- egNet.request(
- 'open-ils.circ',
- 'open-ils.circ.checkin.override',
- egAuth.token(), {
- circ_lib : orgSelector.current().id(),
- copy_id: item.copy_id,
- ff_action: item.next_action
- }
- ).then(function(response) {
- $scope.action_pending = false;
- // do some basic sanity checking before passing
- // the response to the caller.
- if (response) {
- if (angular.isArray(response))
- response = response[0];
- // TODO: check for failure events
- deferred.resolve(response);
- } else {
- // warn that checkin failed
- deferred.reject();
- }
- });
- return deferred.promise;
- },
+ $scope.actions.checkin = function(item) {
+ $scope.action_pending = true;
+ var deferred = $q.defer();
+ egNet.request('open-ils.circ',
+ 'open-ils.circ.checkin.override',
+ egAuth.token(), {
+ circ_lib : orgSelector.current().id(),
+ copy_id: item.copy_id, ff_action: item.next_action
+ }
+ ).then(function(response) {
+ $scope.action_pending = false;
+ // do some basic sanity checking before passing
+ // the response to the caller.
+ if (response) {
+ if (angular.isArray(response))
+ response = response[0];
+ // TODO: check for failure events
+ deferred.resolve(response);
+ } else {
+ // warn that checkin failed
+ deferred.reject();
+ }
+ });
+ return deferred.promise;
+ }
- checkout : function(item) {
- $scope.action_pending = true;
- var deferred = $q.defer();
- egNet.request(
- 'open-ils.circ',
- 'open-ils.circ.checkout.full.override',
- egAuth.token(), {
- circ_lib : orgSelector.current().id(),
- patron_id : item.user_id,
- copy_id: item.copy_id,
- ff_action: item.next_action
- }
- ).then(function(response) {
- $scope.action_pending = false;
- // do some basic sanity checking before passing
- // the response to the caller.
- if (response) {
- if (angular.isArray(response))
- response = response[0];
- // TODO: check for failure events
- deferred.resolve(response);
- } else {
- // warn that checkin failed
- deferred.reject();
- }
- });
- return deferred.promise;
- },
+ $scope.actions.checkout = function(item) {
+ $scope.action_pending = true;
+ var deferred = $q.defer();
+ egNet.request('open-ils.circ',
+ 'open-ils.circ.checkout.full.override',
+ egAuth.token(), {
+ circ_lib : orgSelector.current().id(),
+ patron_id : item.user_id,
+ copy_id: item.copy_id,
+ ff_action: item.next_action
+ }
+ ).then(function(response) {
+ $scope.action_pending = false;
+ // do some basic sanity checking before passing
+ // the response to the caller.
+ if (response) {
+ if (angular.isArray(response))
+ response = response[0];
+ // TODO: check for failure events
+ deferred.resolve(response);
+ } else {
+ // warn that checkin failed
+ deferred.reject();
+ }
+ });
+ return deferred.promise;
+ }
- cancel : function(item) {
- var deferred = $q.defer();
- $scope.action_pending = true;
- egNet.request(
- 'open-ils.circ', 'open-ils.circ.hold.cancel',
- egAuth.token(), item.hold_id
- ).then(function() {
- $scope.action_pending = false;
- deferred.resolve();
- });
- return deferred.promise;
- },
+ $scope.actions.cancel = function(item) {
+ var deferred = $q.defer();
+ $scope.action_pending = true;
+ egNet.request('open-ils.circ',
+ 'open-ils.circ.hold.cancel',
+ egAuth.token(), item.hold_id
+ ).then(function() {
+ $scope.action_pending = false;
+ deferred.resolve();
+ });
+ return deferred.promise;
+ }
- retarget : function(item) {
- var deferred = $q.defer();
- $scope.action_pending = true;
- egNet.request(
- 'open-ils.circ', 'open-ils.circ.hold.reset',
- egAuth.token(), item.hold_id
- ).then(function() {
- $scope.action_pending = false;
- deferred.resolve();
- });
- return deferred.promise;
- },
+ $scope.actions.retarget = function(item) {
+ var deferred = $q.defer();
+ $scope.action_pending = true;
+ egNet.request('open-ils.circ',
+ 'open-ils.circ.hold.reset',
+ egAuth.token(), item.hold_id
+ ).then(function() {
+ $scope.action_pending = false;
+ deferred.resolve();
+ });
+ return deferred.promise;
+ }
- abort_transit : function(item) {
- var deferred = $q.defer();
- $scope.action_pending = true;
- egNet.request(
- 'open-ils.circ', 'open-ils.circ.transit.abort',
- egAuth.token(), {transitid : item.transit_id}
- ).then(function() {
- $scope.action_pending = false;
- deferred.resolve();
- });
- return deferred.promise;
- },
+ $scope.actions.abort_transit = function(item) {
+ var deferred = $q.defer();
+ $scope.action_pending = true;
+ egNet.request('open-ils.circ',
+ 'open-ils.circ.transit.abort',
+ egAuth.token(), {transitid : item.transit_id}
+ ).then(function() {
+ $scope.action_pending = false;
+ deferred.resolve();
+ });
+ return deferred.promise;
+ }
- mark_lost : function(item) {
- var deferred = $q.defer();
- $scope.action_pending = true;
- egNet.request(
- 'open-ils.circ', 'open-ils.circ.circulation.set_lost',
- egAuth.token(), {barcode : item.item_barcode}
- ).then(function(resp) {
- $scope.action_pending = false;
- if (resp == 1) {
- deferred.resolve();
- } else {
- console.error('mark lost failed: ' + js2JSON(resp));
- deferred.reject();
- }
- });
- return deferred.promise;
- },
+ $scope.actions.mark_lost = function(item) {
+ var deferred = $q.defer();
+ $scope.action_pending = true;
+ egNet.request('open-ils.circ',
+ 'open-ils.circ.circulation.set_lost',
+ egAuth.token(), {barcode : item.item_barcode}
+ ).then(function(resp) {
+ $scope.action_pending = false;
+ if (resp == 1) {
+ deferred.resolve();
+ } else {
+ console.error('mark lost failed: ' + js2JSON(resp));
+ deferred.reject();
+ }
+ });
+ return deferred.promise;
+ }
- print : function(item) {
- var deferred = $q.defer();
- $scope.action_pending = true;
- var focus = item.hold ? 'hold' :
- (item.circ ? 'circ' :
- (item.transit ? 'transit' : 'copy'));
+ $scope.actions.print = function(item) {
+ var deferred = $q.defer();
+ $scope.action_pending = true;
+ var focus = item.hold ? 'hold' :
+ (item.circ ? 'circ' : (item.transit ? 'transit' : 'copy'));
- // TODO: line up print template variables with
- // local data structures
- item.barcode = item.copy_barcode || item.item_barcode;
- item.status = item.status_str || item.copy_status;
- item.item_circ_lib = item.copy_circ_lib || item.circ_lib;
+ // TODO: line up print template variables with
+ // local data structures
+ item.barcode = item.copy_barcode || item.item_barcode;
+ item.status = item.status_str || item.copy_status;
+ item.item_circ_lib = item.copy_circ_lib || item.circ_lib;
- egNet.request(
- 'open-ils.actor',
- 'open-ils.actor.web_action_print_template.fetch',
- orgSelector.current().id(), focus
- ).then(function(template) {
-
- if (!template || !(template = template.template())) { // assign
- console.warn('unable to find template for ' +
- item.copy_barcode + ' : ' + focus);
- return;
- }
+ egNet.request(
+ 'open-ils.actor',
+ 'open-ils.actor.web_action_print_template.fetch',
+ orgSelector.current().id(), focus
+ ).then(function(template) {
+
+ if (!template || !(template = template.template())) { // assign
+ console.warn('unable to find template for ' +
+ item.copy_barcode + ' : ' + focus);
+ return;
+ }
- // NOTE: templates stored for now as dojo-style
- // template. mangle to angular templates manually.
- template = template.replace(/\${([^}]+)}/g, '{{$1}}');
+ // TODO: templates stored for now as dojo-style
+ // template. mangle to angular templates manually.
+ template = template.replace(/\${([^}]+)}/g, '{{$1}}');
- // compile the template w/ a temporary print scope
- var printScope = $rootScope.$new();
- angular.forEach(item, function(val, key) {
- printScope[key] = val;
- });
- var element = angular.element(template);
- $compile(element)(printScope);
-
- // append the compiled element to the new window and print
- var w = window.open();
- $(w.document.body).append(element);
- w.document.close();
-
- // $timeout needed in some environments (Mac confirmed)
- // to allow the new window to fully digest before printing.
- $timeout(
- function() {
- w.print();
- w.close();
- $scope.action_pending = false;
- deferred.resolve();
- }
- );
+ // compile the template w/ a temporary print scope
+ var printScope = $rootScope.$new();
+ angular.forEach(item, function(val, key) {
+ printScope[key] = val;
});
- return deferred.promise;
- }
- };
+ var element = angular.element(template);
+ $compile(element)(printScope);
+
+ // append the compiled element to the new window and print
+ var w = window.open();
+ $(w.document.body).append(element);
+ w.document.close();
+
+ // $timeout needed in some environments (Mac confirmed)
+ // to allow the new window to fully $digest() before printing.
+ $timeout(
+ function() {
+ w.print();
+ w.close();
+ $scope.action_pending = false;
+ deferred.resolve();
+ }
+ );
+ });
+ return deferred.promise;
+ }
// default batch action handlers.
- // when a batch is done, reload the route, unless printing.
-
- function performAction(action, item) {
+ function performOneAction(action, item) {
console.debug(item.index + ' => ' + action);
- $scope.actions[action](item).then(
- // when all items are done processing, reload the route
- function(resp) {
- console.debug(item.index + ' => ' + action + ' : done');
- if (--total == 0 && action != 'print')
- $route.reload()
- },
- function(resp) {
- console.error("error in " + action + ": " + resp);
- }
+ return $scope.actions[action](item).then(
+ function(resp) {console.debug(item.index + ' => ' + action + ' : done')},
+ function(resp) {console.error("error in " + action + " : " + resp)}
);
}
Object.keys($scope.actions),
function(action) {
$scope[action] = function() {
- var total = Object.keys($scope.itemList.selected).length;
- angular.forEach($scope.itemList.selected,
- function(val, idx) {
- var item = $scope.itemList.items.filter(
- function(i) {return i.index == idx})[0];
- performAction(action, item)
+ var promises = [];
+ angular.forEach(
+ $scope.itemList.selectedItems(),
+ function(item) {
+ promises.push(performOneAction(action, item))
}
);
- };
+ // when a batch has successfully completed,
+ // reload the route, unless printing.
+ $q.all(promises).then(function() {
+ if (action != 'print') $route.reload();
+ });
+ }
}
);
-
- $scope.firstPage = function() {
- $scope.itemList.offset = 0;
- $scope.collect();
- };
-
- $scope.nextPage = function() {
- $scope.itemList.incrementPage();
- $scope.collect();
- };
-
- $scope.prevPage = function() {
- $scope.itemList.decrementPage();
- $scope.collect();
- };
-
}])
.controller('PendingRequestsCtrl',