From 65c3f12b28d877bdb98e0f395ac7bb0562581d41 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Fri, 25 Oct 2013 10:23:16 -0400 Subject: [PATCH] ff/angular UI initial import Signed-off-by: Bill Erickson --- Open-ILS/src/templates/staff/fulfillment/index.tt2 | 105 +++ .../src/templates/staff/fulfillment/t_actions.tt2 | 35 + .../templates/staff/fulfillment/t_circulating.tt2 | 22 + Open-ILS/src/templates/staff/fulfillment/t_ill.tt2 | 40 ++ .../src/templates/staff/fulfillment/t_inbound.tt2 | 23 + .../templates/staff/fulfillment/t_item_table.tt2 | 47 ++ .../src/templates/staff/fulfillment/t_outbound.tt2 | 22 + .../src/templates/staff/fulfillment/t_pending.tt2 | 58 ++ .../src/templates/staff/fulfillment/t_status.tt2 | 134 ++++ .../web/js/ui/default/staff/fulfillment/app.js | 799 +++++++++++++++++++++ 10 files changed, 1285 insertions(+) create mode 100644 Open-ILS/src/templates/staff/fulfillment/index.tt2 create mode 100644 Open-ILS/src/templates/staff/fulfillment/t_actions.tt2 create mode 100644 Open-ILS/src/templates/staff/fulfillment/t_circulating.tt2 create mode 100644 Open-ILS/src/templates/staff/fulfillment/t_ill.tt2 create mode 100644 Open-ILS/src/templates/staff/fulfillment/t_inbound.tt2 create mode 100644 Open-ILS/src/templates/staff/fulfillment/t_item_table.tt2 create mode 100644 Open-ILS/src/templates/staff/fulfillment/t_outbound.tt2 create mode 100644 Open-ILS/src/templates/staff/fulfillment/t_pending.tt2 create mode 100644 Open-ILS/src/templates/staff/fulfillment/t_status.tt2 create mode 100644 Open-ILS/web/js/ui/default/staff/fulfillment/app.js diff --git a/Open-ILS/src/templates/staff/fulfillment/index.tt2 b/Open-ILS/src/templates/staff/fulfillment/index.tt2 new file mode 100644 index 0000000000..2d022de3f7 --- /dev/null +++ b/Open-ILS/src/templates/staff/fulfillment/index.tt2 @@ -0,0 +1,105 @@ + + + + + + FulfILLment + + + + + + + + + [% INCLUDE "staff/t_base_js.tt2" %] + + diff --git a/Open-ILS/src/templates/staff/fulfillment/t_actions.tt2 b/Open-ILS/src/templates/staff/fulfillment/t_actions.tt2 new file mode 100644 index 0000000000..f0bb3ed7c5 --- /dev/null +++ b/Open-ILS/src/templates/staff/fulfillment/t_actions.tt2 @@ -0,0 +1,35 @@ +
+ + + + +
diff --git a/Open-ILS/src/templates/staff/fulfillment/t_circulating.tt2 b/Open-ILS/src/templates/staff/fulfillment/t_circulating.tt2 new file mode 100644 index 0000000000..8561ec69a7 --- /dev/null +++ b/Open-ILS/src/templates/staff/fulfillment/t_circulating.tt2 @@ -0,0 +1,22 @@ +
+
+
+ +
+
+
+
+ +
+ +
+
diff --git a/Open-ILS/src/templates/staff/fulfillment/t_ill.tt2 b/Open-ILS/src/templates/staff/fulfillment/t_ill.tt2 new file mode 100644 index 0000000000..a7e3cd4eb6 --- /dev/null +++ b/Open-ILS/src/templates/staff/fulfillment/t_ill.tt2 @@ -0,0 +1,40 @@ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ diff --git a/Open-ILS/src/templates/staff/fulfillment/t_inbound.tt2 b/Open-ILS/src/templates/staff/fulfillment/t_inbound.tt2 new file mode 100644 index 0000000000..dd1150366e --- /dev/null +++ b/Open-ILS/src/templates/staff/fulfillment/t_inbound.tt2 @@ -0,0 +1,23 @@ +
+
+ +
+
+ +
+
+
+
+
+ +
+ +
+
diff --git a/Open-ILS/src/templates/staff/fulfillment/t_item_table.tt2 b/Open-ILS/src/templates/staff/fulfillment/t_item_table.tt2 new file mode 100644 index 0000000000..8862e31f35 --- /dev/null +++ b/Open-ILS/src/templates/staff/fulfillment/t_item_table.tt2 @@ -0,0 +1,47 @@ + +
+
No Items To Display
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#Item BarcodeOwning LibraryTransit DateTransit SourceTransit DestinationCheckout DateDue DateCirculating LibraryTitle
{{item.index + 1}} + {{item.item_barcode}} + {{item.source_lib}}{{item.transit_time | date}}{{item.transit_source}}{{item.transit_dest}}{{item.circ_xact_start | date}}{{item.due_date | date}}{{item.circ_circ_lib}}{{item.title}}
diff --git a/Open-ILS/src/templates/staff/fulfillment/t_outbound.tt2 b/Open-ILS/src/templates/staff/fulfillment/t_outbound.tt2 new file mode 100644 index 0000000000..564bdbff2b --- /dev/null +++ b/Open-ILS/src/templates/staff/fulfillment/t_outbound.tt2 @@ -0,0 +1,22 @@ +
+
+ + +
+ +
+
diff --git a/Open-ILS/src/templates/staff/fulfillment/t_pending.tt2 b/Open-ILS/src/templates/staff/fulfillment/t_pending.tt2 new file mode 100644 index 0000000000..cac9945e26 --- /dev/null +++ b/Open-ILS/src/templates/staff/fulfillment/t_pending.tt2 @@ -0,0 +1,58 @@ + +
+
+ +
+ + +
+
+
+
+ +
+
+
No Items To Display
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDRequest DateExpire DateRequesting PatronRequesting LibraryCurrent CopyCopy LibraryTitle
{{item.id}}{{item.request_time | date}}{{item.expire_time | date}}{{item.user_name}}{{item.request_lib}} + {{item.current_copy}} + + {{item.current_copy_lib}}{{item.title}}
+
diff --git a/Open-ILS/src/templates/staff/fulfillment/t_status.tt2 b/Open-ILS/src/templates/staff/fulfillment/t_status.tt2 new file mode 100644 index 0000000000..aca004016b --- /dev/null +++ b/Open-ILS/src/templates/staff/fulfillment/t_status.tt2 @@ -0,0 +1,134 @@ + +
+
+
+
+
+
+ + + + +
+
+
+
+
+ + + + + + + + + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + +
Item Details + + View in Catalog + + Item Not Found +
Barcode:{{item.barcode}}
Status: + + {{item.status_str}} + +
Owning Lib:{{item.source_lib}}
Title:{{item.title}}
Author:{{item.author}}
Call Number:{{item.call_number}}
FulfILLment Bib ID:{{item.bib_id}}
Remote ILS Bib ID:{{item.remote_bib_id}}
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Transaction Details
Circulating Library:{{item.circ_circ_lib}}
Circulating Patron:{{item.circ_usr}}
Checkout date:{{item.circ_xact_start | date}}
Due Date:{{item.due_date | date}}
Circ Status:{{item.circ_stop_fines}}
Requesting Patron:{{item.hold_request_usr}}
Requesting Library:{{item.hold_request_lib}}
Pickup Library:{{item.hold_pickup_lib}}
Request Date:{{item.hold_request_time | date}}
Capture Date:{{item.hold_capture_time | date}}
Hold Cancel Date:{{item.hold_cancel_time | date}}
Hold Cancel Reason:{{item.hold_cancel_cause}}
Transit Source:{{item.transit_source}}
Transit Destination:{{item.transit_dest}}
Transit Send Date:{{item.transit_time | date}}
Transit Receive Date:{{item.transit_recv_time | date}}
+
+
+ + +
diff --git a/Open-ILS/web/js/ui/default/staff/fulfillment/app.js b/Open-ILS/web/js/ui/default/staff/fulfillment/app.js new file mode 100644 index 0000000000..6759ce200b --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/fulfillment/app.js @@ -0,0 +1,799 @@ +/** + * 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. + * + * Consolidate the various item structures into one common, + * authoritative structure for display and print templates. + */ + + +angular.module('ffMain', +['ngRoute', 'egNetMod', 'egAuthMod', 'egStartupMod', + 'egUserMod', 'egUiMod', 'egFlattenerMod', 'egPCRUDMod', 'egOrgMod']) + +.config(function($routeProvider, $locationProvider) { + + // The route-specified controller will not get instantiated + // until the promise returned by this function is resolved + var resolver = {delay : function(egStartup) {return egStartup.go()}}; + + // record management UI + $routeProvider.when('/fulfillment/records', { + templateUrl: './fulfillment/records', + controller: 'ILLCtrl', + resolve : resolver + }); + + // Default to ILL management tabs + $routeProvider.when('/fulfillment/status/:barcode', { + templateUrl: './fulfillment/ill', + controller: 'ILLCtrl', + resolve : resolver + }); + + + // Default to ILL management tabs + $routeProvider.otherwise({ + templateUrl: './fulfillment/ill', + controller: 'ILLCtrl', + resolve : resolver + }); + + $locationProvider.html5Mode(true); +}) + +/** + * Data shared across FF controllers. + */ +.factory('ffService', +['$rootScope', 'egOrg', 'egAuth', +function($rootScope, egOrg, egAuth) { + return { + orgList : function() { + // we want all orgs + return egOrg.list(); + }, + + // currently selected org unit + currentOrg : function(id) { + if (id) { + this.org = id; + } else if (!this.org) { + this.org = egAuth.user().ws_ou(); + } + return egOrg.get(this.org); + }, + + /** returns list of IDs for all org units within the + * full path of currentOrg. Useful for pcrud queries. + */ + relatedOrgs : function(id) { + return egOrg.fullPath( + this.currentOrg()).map(function(o) {return o.id()}); + } + } +}]) + +/** + * Top-level page controller. Handles global components, like the org + * selector. + */ +.controller('FFMainCtrl', ['$scope', '$route', 'egStartup', 'ffService', 'egAuth', +function ($scope, $route, egStartup, ffService, egAuth) { + + // run after startup so we can guarantee access to org units + egStartup.go().then(function() { + $scope.ffService = ffService; + }); + + // change the selected org unit and re-draw the page + $scope.selectOrg = function(id) { + ffService.currentOrg(id); + $route.reload(); + } + + $scope.logout = function() { + egAuth.logout(); + return true; + }; + +}]) + + +/** + * Main ILL controller. + * Maintains the table data / attributes. + * Performs actions. + */ +.controller('ILLCtrl', +['$scope', '$q', '$compile', '$timeout', '$rootScope', '$location', + '$route', '$routeParams', 'egNet', 'egAuth', 'ffService', 'egOrg', +function ($scope, $q, $compile, $timeout, $rootScope, $location, + $route, $routeParams, egNet, egAuth, ffService, egOrg) { + + // tabs + var mytab = $location.path().match(/\/fulfillment\/([^\/]+)/)[1]; + $scope['tab_' + mytab] = true; + + // so our child controllers can access our route info + $scope.illRouteParams = $routeParams; + + // This bit of scope is used directly by all child scopes. + // Inherited scopes use shallow copies, hence the nested object. + $scope.itemList = { + items : [], + selected : {}, + limit : 10, // TODO UI + offset : 0, // TODO UI + filter_borrwer : true, + filterLender : false, + + toggleFilters : function(lender) { + $scope.itemList.filterBorrower = !lender; + $scope.itemList.filterLender = lender; + }, + + // select all rows in the list. if any are + // already selected, de-select all. + selectAll : function() { + var action = true; + angular.forEach($scope.itemList.selected, function(val) { + if (val) action = false; + }); + angular.forEach($scope.itemList.items, function(item) { + if (action) { + $scope.itemList.selected[item.index] = action; + } else { + delete $scope.itemList.selected[item.index]; + } + }); + }, + + addItem : function(item) { + // TODO: id version + item.index = $scope.itemList.items.length; + $scope.itemList.items.push(item); + egNet.request( + 'open-ils.circ', + 'open-ils.circ.item.transaction.disposition', + egAuth.token(), + ffService.currentOrg().id(), + item.barcode + ).then(function(items) { + if (items[0]) { + $scope.itemList.flattenItem(item, items[0]); + } else { + $scope.itemList.items.pop().not_found = true; + } + }); + }, + + // given an item disposition blob, flatten it for display + flattenItem : function(item, item_data) { + /* + * TODO: most of this is unnecessary, since we can access + * fields directly in the template. Consider pairing + * this down to fields that need munging only + */ + var copy = item_data.copy; + var transit = item_data.transit; + var circ = item_data.circ; + var hold = item_data.hold; + if (hold) { + if (!transit && hold.transit()) { + transit = item_data.hold.transit(); + } + } else if (transit && transit.hold_transit_copy()) { + hold = transit.hold_transit_copy().hold(); + } + + item.copy = item_data.copy; + item.item_barcode = copy.barcode(); + item.item_barcode_enc = encodeURIComponent(copy.barcode()); + item.source_lib = egOrg.get(copy.source_lib()).shortname(); + item.circ_lib = egOrg.get(copy.circ_lib()).shortname(); + item.title = copy.call_number().record().simple_record().title(); + item.author = copy.call_number().record().simple_record().author(); + item.call_number = copy.call_number().label(); + item.bib_id = copy.call_number().record().id(); + item.remote_bib_id = copy.call_number().record().remote_id(); + item.next_action = item_data.next_action; + item.can_cancel_hold = (item_data.can_cancel_hold == 1); + item.can_retarget_hold = (item_data.can_retarget_hold == 1); + + switch(item_data.next_action) { + // capture lender copy for hold + case 'ill-home-capture' : + item.needs_capture = true; + break; + // receive item at borrower + case 'ill-foreign-receive': + // receive lender copy back home + case 'transit-home-receive': + // transit item for cancelled hold back home (or next hold) + case 'transit-foreign-return': + item.needs_receive = true; + break; + // complete borrower circ, transit item back home + case 'ill-foreign-checkin': + item.needs_checkin = true; + break; + // check out item to borrowing patron + case 'ill-foreign-checkout': + item.needs_checkout = true; + break; + } + + item.status_str = copy.status().name(); + if (copy.status().id() == 8 /* holds shelf */ && + hold && + hold.transit() && + hold.transit().dest_recv_time()) { + item.status_str += ' @ ' + egOrg.get(hold.transit().dest()).shortname(); + } else if (copy.status().id() == 1 /* checked out */ && circ) { + item.status_str += ' @ ' + egOrg.get(circ.circ_lib()).shortname(); + } + + item.copy_status_warning = (copy.status().holdable() == 'f'); + + if (transit) { + item.transit = transit; + item.transit_source = transit.source().shortname(); + item.transit_dest = transit.dest().shortname(); + item.transit_time = transit.source_send_time(); + item.transit_recv_time = transit.dest_recv_time(); + item.open_transit = !Boolean(transit.dest_recv_time()); + } + + if (circ) { + item.circ = circ; + item.due_date = circ.due_date(); + item.circ_circ_lib = egOrg.get(circ.circ_lib()).shortname(); + item.circ_xact_start = circ.xact_start(); + item.circ_stop_fines = circ.stop_fines(); + item.circ_usr = circ.usr().card() ? + circ.usr().card().barcode() : circ.usr().usrname(); + item.can_mark_lost = (item.circ && item.copy.status().id() == 1); // checked out + } + + if (hold) { + item.hold = hold; + item.hold_request_usr = hold.usr().card() ? + hold.usr().card().barcode() : hold.usr().usrname(); + item.hold_request_lib = egOrg.get(hold.request_lib()).shortname(); + item.hold_pickup_lib = egOrg.get(hold.pickup_lib()).shortname(); + item.hold_request_time = hold.request_time(); + item.hold_capture_time = hold.capture_time(); + if (hold.cancel_time()) { + item.hold_cancel_time = hold.cancel_time(); + if (hold.cancel_cause()) { + item.hold_cancel_cause = hold.cancel_cause().label(); + } + } + } + + // TODO: another unnecessary layer of data munging, + // this time to fit the print templates. Can be unified. + item.barcode = item.item_barcode; + item.status = item.status_str; + item.item_circ_lib = item.circ_lib; + } + }; + + /* Actions + * Performed on flattened items (see above) + */ + $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 : ffService.currentOrg().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 : ffService.currentOrg().id(), + patron_id : item.hold.usr().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; + }, + + 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; + }, + + 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')); + + egNet.request( + 'open-ils.actor', + 'open-ils.actor.web_action_print_template.fetch', + ffService.currentOrg().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}}'); + + // 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(); + } + ); + }); + return deferred.promise; + } + }; + + // default batch action handlers. + // when a batch is done, reload the route (unless printing). + angular.forEach( + 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]; + 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); + } + ); + } + ); + }; + } + ); +}]) + +.controller('TransitsCtrl', +['$scope', '$q', 'egPCRUD', 'ffService', +function ($scope, $q, egPCRUD, ffService) { + + $scope.drawTable = function(filterLender) { + var deferred = $q.defer(); + $scope.itemList.items = []; + $scope.itemList.toggleFilters(filterLender); + + var fullPath = ffService.relatedOrgs(); + + var dest = fullPath; // inbound transits + var circ_lib = fullPath; // our copies + + if ($scope.itemList.filterBorrower) { + // borrower always means not-our-copies + circ_lib = {'not in' : fullPath}; + } + if ($scope.tab_outbound) { + // outbound transits away from "here" + dest = {'not in' : fullPath}; + } + + var query = { + dest_recv_time : null, + dest : dest, + target_copy : { + 'in' : { + select: {acp : ['id']}, + from : 'acp', + where : { + deleted : 'f', + id : {'=' : {'+atc' : 'target_copy'}}, + circ_lib : circ_lib + } + } + } + }; + + return egPCRUD.search('atc', query, + { limit : $scope.itemList.limit, + offset : $scope.itemList.offset, + flesh : 1, + flesh_fields : {atc : ['target_copy']} + }, {atomic : true} + ).then(function(transits) { + angular.forEach(transits, function(transit) { + $scope.itemList.addItem( + {barcode : transit.target_copy().barcode()}); + }); + }); + }; + + // outbound tab defaults to lender view + return $scope.drawTable($scope.tab_outbound == true); +}]) + +.controller('CircCtrl', +['$scope', '$q', 'egPCRUD', 'ffService', +function ($scope, $q, egPCRUD, ffService) { + + $scope.drawTable = function(filterLender) { + var deferred = $q.defer(); + $scope.itemList.items = []; + $scope.itemList.toggleFilters(filterLender); + + var fullPath = ffService.relatedOrgs(); + + var copy_lib = fullPath; // our copies + var circ_lib = fullPath; // circulating here + + if ($scope.itemList.filterBorrower) { + // borrower always means not-our-copies + circ_lib = {'not in' : fullPath}; + } else { + copy_lib = {'not in' : fullPath}; + } + + var query = { + checkin_time : null, + circ_lib : circ_lib, + target_copy : { + 'in' : { + select: {acp : ['id']}, + from : 'acp', + where : { + deleted : 'f', + id : {'=' : {'+circ' : 'target_copy'}}, + circ_lib : copy_lib + } + } + } + }; + + return egPCRUD.search('circ', query, + { limit : $scope.itemList.limit, + offset : $scope.itemList.offset, + flesh : 1, + flesh_fields : {circ : ['target_copy']} + }, {atomic : true} + ).then(function(circs) { + angular.forEach(circs, function(circ) { + $scope.itemList.addItem( + {barcode : circ.target_copy().barcode()}); + }); + }); + }; + + // outbound tab defaults to lender view + return $scope.drawTable(); +}]) + + +.controller('ItemStatusCtrl', +['$scope', '$q', '$route', '$location', 'egPCRUD', 'ffService', +function ($scope, $q, $route, $location, egPCRUD, ffService) { + $scope.focusMe = true; + + $scope.draw = function(barcode) { + if ($scope.illRouteParams.barcode != barcode) { + // keep the scan box and URL in sync + $location.path('/fulfillment/status/' + + encodeURIComponent(barcode)); + } else { + $scope.itemList.items = []; + $scope.item = {barcode : barcode}; + $scope.itemList.addItem($scope.item); + $scope.selectMe = true; + } + } + + // item status actions all call the parent scope's action + // handlers unadorned then reload the route. + // TODO: set selected == item; no more need for custom action handers?? + angular.forEach(['checkin', 'checkout', + 'cancel', 'abort_transit', 'retarget', 'mark_lost'], + function(action) { + $scope[action] = function() { + $scope.actions[action]($scope.item) + .then(function(resp) {$route.reload()}); + }; + } + ); + + // barcode passed via URL + if ($scope.illRouteParams.barcode) { + $scope.barcode = $scope.illRouteParams.barcode; + $scope.draw($scope.illRouteParams.barcode); + } +}]) + + + +/** + * Table of pending requests + * This is the only table based purely on holds instead of items, + * so the data is managed a little differently. + */ +.controller('PendingRequestsCtrl', +['$scope', '$q', '$route', 'egNet', 'egAuth', 'egPCRUD', 'egOrg', 'ffService', +function ($scope, $q, $route, egNet, egAuth, egPCRUD, egOrg, ffService) { + var self = this; + + this.displayOne = function(display, hold_blob) { + var hold = display.hold = hold_blob.hold; + display.request_time = hold.hold_request_time = hold.request_time(); + display.expire_time = hold.expire_time(); + display.user_name = display.hold_request_usr = + (hold_blob.patron_first || '') + + ' ' + (hold_blob.patron_last || ''); + display.request_lib = display.hold_request_lib = + egOrg.get(hold.request_lib()).shortname(); + display.hold_pickup_lib = + egOrg.get(hold.pickup_lib()).shortname(); + display.title = hold_blob.mvr.title(); // MVR BOO + display.author = hold_blob.mvr.author(); // MVR BOO + if (hold_blob.copy) { + display.copy = hold_blob.copy; + display.current_copy = hold_blob.copy.barcode(); + display.current_copy_lib = + egOrg.get(hold_blob.copy.source_lib()).shortname(); + display.current_copy_enc = encodeURIComponent(hold_blob.copy.barcode()); + display.barcode = display.copy.barcode(); + display.status = display.copy.status(); + display.item_circ_lib = egOrg.get(display.copy.circ_lib()).shortname(); + } + if (hold_blob.volume) { + display.call_number = hold_blob.volume.label(); + } + if ($scope.itemList.filterLender) + display.next_action = 'ill-home-capture'; + }; + + // call the parent scope's action handler for each selected item. + // when all are done, reload the route (when not printing). + /* + angular.forEach(['checkin', 'cancel', 'retarget', 'print'], + function(action) { + $scope[action] = function() { + var total = Object.keys($scope.itemList.selected).length; + angular.forEach( + $scope.itemList.selected, + function(idx) { + var item = $scope.itemList.items.filter( + function(i) {return i.index == idx})[0]; + $scope.actions[action](item) + .then( + // when all items are done processing, reload the route + function(resp) { + if (--total == 0 && action != 'print') + $route.reload() + }, + function(resp) { + console.error("error in " + action + ": " + resp); + } + ); + } + ); + }; + } + ); + */ + + $scope.drawTable = function(filterLender) { + $scope.itemList.items = []; + $scope.itemList.toggleFilters(filterLender); + + var fullPath = ffService.relatedOrgs(); + + var query = { + capture_time : null, + cancel_time : null, + frozen : 'f' + }; + + if ($scope.itemList.filterBorrower) { + // holds for my patrons originate "here" + // current_copy is not relevant + query.request_lib = fullPath; + } else { + // holds for other originate from not-"here" and + // have a current copy at "here". + query.request_lib = {'not in' : fullPath}; + query.current_copy = { + "in" : { + select: {acp : ['id']}, + from : 'acp', + where: { + deleted : 'f', + circ_lib : fullPath, + id : {'=' : {'+ahr' : 'current_copy'}} + } + } + } + } + + egPCRUD.search('ahr', query, + { limit : $scope.itemList.limit, + offset : $scope.itemList.offset, + order_by : {'ahr' : 'request_time, id'} + }, + {atomic : true} + + ).then(function(holds) { + + // fetch the extended hold details for each hold + // to pick up the title, etc. + angular.forEach(holds, function(hold) { + var display = { + id : hold.id(), + index : $scope.itemList.items.length + }; + // we don't use itemList.addItem(), + // since it fetches data differently + $scope.itemList.items.push(display); + egNet.request( + 'open-ils.circ', + 'open-ils.circ.hold.details.retrieve', + egAuth.token(), hold.id() + ).then(function(hold_blob) { + self.displayOne(display, hold_blob); + }); + }); + }); + }; + + + $scope.firstPage = function() { + $scope.itemList.offset = 0; + $scope.drawTable($scope.itemList.filterLender == true); + }; + + $scope.nextPage = function() { + $scope.itemList.offset += $scope.itemList.limit; + $scope.drawTable($scope.itemList.filterLender == true); + }; + + $scope.prevPage = function() { + $scope.itemList.offset -= $scope.itemList.limit; + $scope.drawTable($scope.itemList.filterLender == true); + }; + + $scope.drawTable(); +}]) + + + + -- 2.11.0