<tr fmclass='au' fmfield='master_account'></tr>
<tr fmclass='au' fmfield='claims_returned_count' wclass='dijit.form.NumberSpinner' wconstraints="{min:0,places:0}" wvalue='0'></tr>
<tr fmclass='au' fmfield='claims_never_checked_out_count' wclass='dijit.form.NumberSpinner' wconstraints="{min:0,places:0}" wvalue='0'></tr>
- <tr fmclass='au' fmfield='alert_message' wclass='dijit.form.Textarea' wstyle='height:5em'></tr>
<tr class='divider hidden' id='uedit-settings-divider'><td colspan='0' id='userSettings'></td></tr>
<tr class='hidden' id='uedit-user-setting-template'>
s.PAGE_TITLE_PATRON_SEARCH = "[% l('Patron Search') %]";
s.PAGE_TITLE_PATRON_NAME = "[% l('[_1], [_2] [_3]', '{{lname}}','{{fname}}','{{mname}}') %]";
s.PAGE_TITLE_PATRON_CHECKOUT = "[% l('Checkout') %]";
- s.PAGE_TITLE_PATRON_MESSAGES = "[% l('Messages') %]";
+ s.PAGE_TITLE_PATRON_MESSAGES = "[% l('Notes') %]";
/* TODO: The "Other" page title could be smarter.. */
s.PAGE_TITLE_PATRON_OTHER = "[% l('Other') %]";
s.PAGE_TITLE_PATRON_BILLS = "[% l('Bills') %]";
<span ng-if="patron().name_keywords()"> <a title="[% l('Name keywords: ') %]{{patron().name_keywords()}}" class="glyphicon glyphicon-tags"></a>
</div>
- <p ng-show="patron().notes().length > 0"><a class='patron-summary-has-notes' href="./circ/patron/{{patron().id()}}/notes"><span class="label label-warning">Notes {{patron().notes().length}}</span></a></p>
<div ng-show="tab != 'search'">
<a href ng-click="toggle_expand_summary()"
title="[% l('Collapse Patron Summary Display') %]"
</a>
</li>
<li ng-class="{active : tab == 'messages', disabled : !patron()}">
- <a a-disabled="!patron()" href="./circ/patron/{{patron().id()}}/messages">[% l('Messages') %]</a>
+ <a a-disabled="!patron()" href="./circ/patron/{{patron().id()}}/messages">[% l('Notes') %]
+ <span ng-if="visible_notes().length > 0">
+ ({{visible_notes().length}})
+ </span>
+ </a>
</li>
<li ng-class="{active : tab == 'edit', disabled : !patron()}">
<a href="./circ/patron/{{patron().id()}}/edit">[% l('Edit') %]</a>
<ul uib-dropdown-menu>
<li>
<a href="./circ/patron/{{patron().id()}}/alerts">
- [% l('Display Alert and Messages') %]
- </a>
- </li>
- <li>
- <a href="./circ/patron/{{patron().id()}}/notes">
- [% l('Notes') %]
+ [% l('Display Alerts') %]
</a>
</li>
<li>
[% l('Patron account has invalid addresses.') %]
</div>
- <!-- alert message -->
- <div class="row" ng-if="patron().alert_message()">
- <div class="col-md-12">
- <div class="panel panel-warning">
- <div class="panel-heading">
- <div class="panel-title text-center">[% l('Alert Message') %]</div>
- </div>
- <div class="panel-body">
- {{patron().alert_message()}}
- </div>
- </div>
- </div>
- </div>
-
<!-- penalties -->
<div class="row" ng-if="alert_penalties().length">
<div class="col-md-12">
<div class="panel panel-warning">
<div class="panel-heading">
- <div class="panel-title text-center">[% l('Penalties') %]</div>
+ <div class="panel-title text-center">[% l('Alerts') %]</div>
</div>
<div class="panel-body">
<div class="row"
{{penalty.org_unit().shortname()}}
</div>
<div class="col-md-8"
- title="{{penalty.standing_penalty().name()}}">
- {{penalty.standing_penalty().label()}}
- <div>{{penalty.note()}}</div><!-- force newline -->
+ title="{{penalty.standing_penalty().name()}} (id {{penalty.id()}})">
+ {{penalty.usr_message().title() || penalty.standing_penalty().label()}}
+ <div>{{penalty.usr_message().message()}}</div><!-- force newline -->
+ <div> </div><!-- should use CSS for this, but spacing out the notes -->
</div>
<div class="col-md-2">
{{penalty.set_date() | date:$root.egDateFormat}}
</div>
</div>
-<!-- ALERT_MESSAGE -->
-
-<div class="row reg-field-row" ng-show="show_field('au.alert_message')">
- [% draw_field_label('au', 'alert_message') %]
- <div class="col-md-3 reg-field-input">
- <textarea
- class="form-control"
- ng-model="patron.alert_message"
- ng-pattern="field_pattern('au', 'alert_message')"
- ng-change="field_modified()"
- ng-blur="handle_field_changed(patron, 'alert_message')">
- </textarea>
- </div>
- <div class="col-md-6 patron-reg-example">
- [% draw_example_text('au', 'alert_message') %]
- </div>
-</div>
-
<div ng-if="!offline">
<div class="alert alert-success row" role="alert">
-
-<div class="strong-text-2">[% l('Penalties and Messages') %]</div>
+<div class="strong-text-2">[% l('Notes') %]</div>
<div class="pad-vert"></div>
<eg-grid
- idl-class="ausp"
+ idl-class="aump"
grid-controls="activeGridControls"
dateformat="{{$root.egDateAndTimeFormat}}"
persist-key="circ.patron.staff_messages">
<eg-grid-menu-item handler="createPenalty"
- label="[% l('Apply Penalty / Message') %]"></eg-grid-menu-item>
+ label="[% l('Create Note') %]"></eg-grid-menu-item>
- <eg-grid-action label="[% l('Remove Penalty / Message') %]"
- handler="removePenalty"></eg-grid-action>
- <eg-grid-action label="[% l('Modify Penalty / Message') %]"
+ <eg-grid-action label="[% l('Remove Note') %]"
+ disabled="test_for_disable_remove_penalty" handler="removePenalty"></eg-grid-action>
+ <eg-grid-action label="[% l('Edit Note') %]"
handler="editPenalty"></eg-grid-action>
- <eg-grid-action label="[% l('Archive Penalty / Message') %]"
+ <eg-grid-action label="[% l('Archive Note') %]"
handler="archivePenalty"></eg-grid-action>
- <eg-grid-field path="set_date" label="[% l('Applied On') %]" datatype="timestamp"></eg-grid-field>
- <eg-grid-field path="standing_penalty.label"></eg-grid-field>
- <eg-grid-field path="org_unit.shortname" label="[% l('Library') %]"></eg-grid-field>
- <eg-grid-field path="note"></eg-grid-field>
- <eg-grid-field path="id" required hidden></eg-grid-field>
+ <eg-grid-field path="read_date"></eg-grid-field>
+ <eg-grid-field path="deleted" required hidden></eg-grid-field>
+ <eg-grid-field path="usr.usrname" label="[% l('User') %]" required hidden></eg-grid-field>
+ <eg-grid-field path="message" label="[% l('Note Text') %]"></eg-grid-field>
+ <eg-grid-field path="title"></eg-grid-field>
+ <eg-grid-field path="pub"></eg-grid-field>
+ <eg-grid-field path="stop_date" hidden></eg-grid-field>
+ <eg-grid-field path="editor.usrname" label="[% l('Editor') %]" required hidden></eg-grid-field>
+ <eg-grid-field path="edit_date" hidden></eg-grid-field>
+ <eg-grid-field path="staff.usrname" label="[% l('Staff') %]" required></eg-grid-field>
+ <eg-grid-field path="standing_penalty.name" required label="[% l('Penalty Name') %]"></eg-grid-field>
<eg-grid-field path="standing_penalty.block_list" required hidden></eg-grid-field>
<eg-grid-field path="standing_penalty.*" hidden></eg-grid-field>
-
+ <eg-grid-field path="org_unit.shortname" label="[% l('Location') %]" required></eg-grid-field>
+ <eg-grid-field path="create_date"></eg-grid-field>
+ <eg-grid-field path="ausp_id" required hidden></eg-grid-field>
+ <eg-grid-field path="aum_id" required hidden></eg-grid-field>
+ <eg-grid-field path="id" required hidden></eg-grid-field>
</eg-grid>
<div class="pad-vert"><hr/></div>
<div class="pad-vert row padded">
<div class="col-md-4">
- <div class="strong-text-2">[% l('Archived Penalties / Messages') %]</div>
+ <div class="strong-text-2">[% l('Archived Notes') %]</div>
</div>
<div class="col-md-4">
<label>[% l('Set Date Start:') %]</label>
</div>
</div>
<eg-grid
- idl-class="ausp"
+ idl-class="aump"
grid-controls="archiveGridControls"
dateformat="{{$root.egDateAndTimeFormat}}"
+ features="-multiselect"
persist-key="circ.patron.archived_messages">
- <eg-grid-field path="set_date" label="[% l('Applied On') %]" datatype="timestamp"></eg-grid-field>
- <eg-grid-field path="standing_penalty.label"></eg-grid-field>
- <eg-grid-field path="org_unit.shortname" label="[% l('Library') %]"></eg-grid-field>
- <eg-grid-field path="note"></eg-grid-field>
- <eg-grid-field path="id" required hidden></eg-grid-field>
+ <eg-grid-field path="read_date"></eg-grid-field>
+ <eg-grid-field path="deleted" required hidden></eg-grid-field>
+ <eg-grid-field path="usr.usrname" label="[% l('User') %]" required hidden></eg-grid-field>
+ <eg-grid-field path="message" label="[% l('Note Text') %]"></eg-grid-field>
+ <eg-grid-field path="title"></eg-grid-field>
+ <eg-grid-field path="pub"></eg-grid-field>
+ <eg-grid-field path="stop_date" hidden></eg-grid-field>
+ <eg-grid-field path="editor.usrname" label="[% l('Editor') %]" required hidden></eg-grid-field>
+ <eg-grid-field path="edit_date" hidden></eg-grid-field>
+ <eg-grid-field path="staff.usrname" label="[% l('Staff') %]" required></eg-grid-field>
+ <eg-grid-field path="standing_penalty.name" required label="[% l('Penalty Name') %]"></eg-grid-field>
<eg-grid-field path="standing_penalty.block_list" required hidden></eg-grid-field>
<eg-grid-field path="standing_penalty.*" hidden></eg-grid-field>
-
+ <eg-grid-field path="org_unit.shortname" label="[% l('Location') %]" required></eg-grid-field>
+ <eg-grid-field path="create_date"></eg-grid-field>
+ <eg-grid-field path="ausp_id" required hidden></eg-grid-field>
+ <eg-grid-field path="aum_id" required hidden></eg-grid-field>
+ <eg-grid-field path="id" required hidden></eg-grid-field>
</eg-grid>
ng-repeat="penalty in alert_penalties()">
<div
class="col-md-9 patron-summary-alert"
- title="{{penalty.standing_penalty().name()}}">
- {{penalty.note() || penalty.standing_penalty().label()}}
+ title="{{penalty.standing_penalty().name()}} (id {{penalty.id()}}): {{penalty.usr_message().message()}}">
+ {{penalty.usr_message().title() || penalty.standing_penalty().label() | limitTo: 40}}
</div>
<div class="col-md-3">
{{penalty.set_date() | date:$root.egDateFormat}}
</div>
</div>
- <div class="row patron-summary-divider">
- <div
- class="col-md-9 patron-summary-alert"
- title="[% l('Patron Alert Message') %]"
- ng-if="patron().alert_message()">
- {{patron().alert_message()}}
- </div>
- </div>
<div ng-if="patron().photo_url()" class="row">
<div class="col-md-8 patron_photo_wrap"><img class="img-responsive img-rounded" src="{{patron().photo_url()}}" alt=""></div>
</div>
<div class="row"
- ng-class="{'patron-summary-divider' : alert_penalties().length || patron().alert_message()}">
+ ng-class="{'patron-summary-divider' : alert_penalties().length}">
<div class="col-md-5">[% l('Profile') %]</div>
<div class="col-md-7">{{patron().profile().name()}}</div>
</div>
-<form ng-submit="ok(args)" role="form">
+<form id="patron-notes-container" ng-submit="ok(args)" role="form">
<div class="modal-header">
<button type="button" class="close" ng-click="cancel()"
aria-hidden="true">×</button>
- <h4 class="modal-title">[% l('Apply Standing Penalty / Message') %]</h4>
+ <h4 class="modal-title">[% l('Create or Edit Note') %]</h4>
</div>
<div class="modal-body">
<div class="row">
- <div class="col-md-8">
+ <div class="col-md-6 pull-left">
<ul class="nav nav-pills">
<!-- 21 == SILENT_NOTE -->
<li ng-class="{active : args.penalty == 21}">
- <a href ng-click="args.penalty=21">[% l('Note') %]</a>
+ <a href ng-click="set_penalty(21);">[% l('Note') %]</a>
</li>
<!-- 20 == ALERT_NOTE -->
<li ng-class="{active : args.penalty == 20}">
- <a href ng-click="args.penalty=20">[% l('Alert') %]</a>
+ <a href ng-click="set_penalty(20);">[% l('Alert') %]</a>
</li>
<!-- 25 == STAFF_CHR -->
<li ng-class="{active : args.penalty == 25}">
- <a href ng-click="args.penalty=25">[% l('Block') %]</a>
+ <a href ng-click="set_penalty(25);">[% l('Block') %]</a>
</li>
</ul>
</div>
- <div class="col-md-4 pull-right nullable">
- <select class="form-control"
- ng-model="args.custom_penalty"
- ng-options="penalty.id() as penalty.label() for penalty in penalties">
- <option value="">[% l('Penalty Type') %]</option>
- </select>
+ <div class="col-md-6 pull-left">
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-6 nullable">
+ <label>[% l('Penalty Type:') %]
+ <select class="form-control"
+ ng-model="args.custom_penalty"
+ ng-disabled="(args.pub && args.read_date) || args.deleted"
+ ng-options="penalty.id() as penalty.label() for penalty in penalties">
+ </select>
+ </label>
+ </div>
+ <div class="col-md-6 pull-right">
+ <div>
+ <label ng-disabled="(args.pub && args.read_date) || args.deleted">[% l('Depth:') %]
+ <eg-share-depth-selector id="org_depth" useOpacLabel maxDepth="{{args.max_depth}}" ng-model="args.custom_depth"></eg-share-depth-selector>
+ </label>
+ </div>
+ <div>
+ <label>[% l('Location:') %]</label><span> {{args.org.shortname()}}</span>
+ <!--<eg-org-selector selected="args.org" onchange="update_org"
+ disable-test="cant_use_org"></eg-org-selector>-->
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-3 pull-left">
+ <label>
+ <input type="checkbox" ng-model="args.pub"
+ ng-disabled="(args.pub && args.read_date) || args.deleted"/>
+ [% l('Patron Visible') %]
+ </label>
+ </div>
+ <div class="col-md-3 pull-left">
+ <label ng-if="args.pub && args.read_date">
+ [% l('Read on [_1]', '{{args.read_date | date:$root.egDateAndTimeFormat}}') %]
+ </label>
+ <label ng-if="args.pub && !args.read_date">
+ [% l('Unread') %]
+ </label>
+ </div>
+ <div class="col-md-3 pull-left">
+ <label ng-if="args.edit_date">
+ [% l('Edited on [_1] by [_2]', '{{args.edit_date | date:$root.egDateAndTimeFormat}}', '{{args.editor.usrname()}}') %]
+ </label>
+ </div>
+ <div class="col-md-3 pull-left">
+ <label class="patron-summary-alert" ng-if="args.deleted">
+ [% l('Deleted') %]
+ </label>
+ </div>
+ </div>
+ <div class="form-group row pad-vert">
+ <div class="col-md-12">
+ <textarea class="form-control"
+ ng-model="args.title" placeholder="[% l('Title...') %]"
+ ng-required="true" ng-disabled="(args.pub && args.read_date) || args.deleted">
+ </textarea>
</div>
</div>
<div class="form-group row pad-vert">
<div class="col-md-12">
<textarea class="form-control"
- ng-model="args.note" placeholder="[% l('Note...') %]">
+ ng-model="args.note" placeholder="[% l('Note Text...') %]"
+ ng-disabled="(args.pub && args.read_date) || args.deleted">
</textarea>
</div>
</div>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" ng-hide="!require_initials"
+ ng-disabled="(args.pub && args.read_date) || args.deleted"
ng-model="args.initials" placeholder="[% l('Initials') %]" ng-required="require_initials"/>
</div>
<div class="col-md-10 pull-right">
- <input type="submit" class="btn btn-primary" value="[% l('OK') %]"/>
+ <input type="submit" class="btn btn-primary"
+ ng-disabled="(args.pub && args.read_date) || args.deleted || !args.org" value="[% l('OK') %]"/>
<button class="btn btn-warning" ng-click="cancel($event)">[% l('Cancel') %]</button>
</div>
</div>
/* Angular applies these classes based on the field's
* required and pattern settings */
+#patron-notes-container .ng-invalid-required,
#patron-reg-container .ng-invalid,
#patron-reg-container .ng-invalid-required,
#patron-pay-by-credit-form .ng-invalid {
<div>Is Group Lead Account: {{patron.master_account}}</div>
<div>Claims-Returned Count: {{patron.claims_returned_count}}</div>
<div>Claims-Never-Checked-Out Count: {{patron.claims_never_checked_out_count}}</div>
- <div>Alert Message: {{patron.alert_message}}</div>
<div>
<div ng-repeat="address in patron.addresses">
'ui.patron.edit.au.claims_returned_count.suggest',
'ui.patron.edit.au.claims_never_checked_out_count.show',
'ui.patron.edit.au.claims_never_checked_out_count.suggest',
- 'ui.patron.edit.au.alert_message.show',
- 'ui.patron.edit.au.alert_message.suggest',
'ui.patron.edit.aua.post_code.regex',
'ui.patron.edit.aua.post_code.example',
'ui.patron.edit.aua.county.require',
master_account : 'f',
claims_returned_count : '0',
claims_never_checked_out_count : '0',
- alert_message : 'Coat is in the lost-and-found behind the circ desk',
ident_type: {name: function() {return 'Drivers License'}},
ident_value: '11332445',
ident_type2: {name: function() {return 'Other'}},
});
}])
+.factory("hasPermAt",function(){
+ return {};
+})
+
.config(function($routeProvider, $locationProvider, $compileProvider) {
$locationProvider.html5Mode(true);
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|mailto|blob):/); // grid export
// data loaded at startup which only requires an authtoken goes
// here. this allows the requests to be run in parallel instead of
// waiting until startup has completed.
- var resolver = {delay : ['egCore','egUser', function(egCore , egUser) {
+ var resolver = {delay : ['egCore','egUser','hasPermAt', function(egCore , egUser , hasPermAt) {
// fetch the org settings we care about during egStartup
// and toss them into egCore.env as egCore.env.aous[name] = value.
]);
}
- return egCore.startup.go();
+ return egCore.startup.go().then(function(go_promise) {
+ // FIXME: the following is really just for PatronMessagesCtrl
+ // and PatronCtrl, so we could refactor to avoid calling it
+ // for every controller
+ return egCore.perm.hasPermFullPathAt('VIEW_USER')
+ .then(function(orgList) {
+ hasPermAt['VIEW_USER'] = orgList;
+ return go_promise;
+ });
+ });
}]};
$routeProvider.when('/circ/patron/search', {
*
* */
.controller('PatronCtrl',
- ['$scope','$q','$location','$filter','egCore','egNet','egUser','egAlertDialog','egConfirmDialog','egPromptDialog','patronSvc',
-function($scope, $q , $location , $filter , egCore , egNet , egUser , egAlertDialog , egConfirmDialog , egPromptDialog , patronSvc) {
+ ['$scope','$q','$location','$filter','egCore','egNet','egUser','egAlertDialog','egConfirmDialog','egPromptDialog','patronSvc','hasPermAt',
+function($scope, $q , $location , $filter , egCore , egNet , egUser , egAlertDialog , egConfirmDialog , egPromptDialog , patronSvc , hasPermAt) {
$scope.is_patron_edit = function() {
return Boolean($location.path().match(/patron\/\d+\/edit$/));
}
$scope.patron = function() { return patronSvc.current }
+ $scope.visible_notes = function() {
+ var p = patronSvc.current;
+ if (p) {
+ var org_ids = hasPermAt['VIEW_USER'];
+ var filtered_notes = p.notes().filter(function(n) { return org_ids.indexOf(n.org_unit()) > -1; });
+ return filtered_notes;
+ }
+ return [];
+ }
$scope.patron_stats = function() { return patronSvc.patron_stats }
$scope.summary_stat_cats = function() { return patronSvc.summary_stat_cats }
$scope.hasAlerts = function() { return patronSvc.hasAlerts }
* Manages messages
*/
.controller('PatronMessagesCtrl',
- ['$scope','$q','$routeParams','egCore','$uibModal','patronSvc','egCirc',
-function($scope , $q , $routeParams, egCore , $uibModal , patronSvc , egCirc) {
+ ['$scope','$q','$routeParams','egCore','$uibModal','patronSvc','egCirc','hasPermAt',
+function($scope , $q , $routeParams, egCore , $uibModal , patronSvc , egCirc , hasPermAt ) {
$scope.initTab('messages', $routeParams.id);
var usr_id = $routeParams.id;
- var org_ids = egCore.org.fullPath(egCore.auth.user().ws_ou(), true);
+ var org_ids = hasPermAt.VIEW_USER;
// setup date filters
var start = new Date(); // now - 1 year
var activeGrid = $scope.activeGridControls = {
setSort : function() {
- return ['set_date'];
+ return ['create_date'];
},
setQuery : function() {
return {
var archiveGrid = $scope.archiveGridControls = {
setSort : function() {
- return ['set_date'];
+ return ['create_date'];
},
watchQuery : function() {
return {
usr : usr_id,
org_unit : org_ids,
stop_date : {'<=' : 'now'},
- set_date : {between : date_range()}
+ create_date : {between : date_range()}
};
}
};
+ $scope.test_for_disable_remove_penalty = function() {
+ var selected = $scope.activeGridControls.selectedItems();
+ var found_pub_and_read_and_not_deleted = false;
+ angular.forEach(selected, function(s) {
+ if (Boolean(s.pub == 't') && Boolean(s.read_date) && !Boolean(s.deleted == 't')) {
+ found_pub_and_read_and_not_deleted = true;
+ }
+ });
+ return found_pub_and_read_and_not_deleted;
+ }
+
$scope.removePenalty = function(selected) {
- // the grid stores flattened penalties. Fetch penalty objects first
+ if (selected.length == 0) return;
- var ids = selected.map(function(s){ return s.id });
- egCore.pcrud.search('ausp',
- {id : ids}, {},
- {atomic : true, authoritative : true}
+ // TODO: need confirmation dialog
- // then delete them
- ).then(function(penalties) {
- return egCore.pcrud.remove(penalties);
+ var promises = [];
+ // figure out the view components
+ var aum_ids = [];
+ var ausp_ids = [];
+ angular.forEach(selected, function(s) {
+ if (s.aum_id) { aum_ids.push(s.aum_id); }
+ if (s.ausp_id) { ausp_ids.push(s.ausp_id); }
+ });
- // then refresh the grid
- }).then(function() {
+ // fetch all of them since trying to pull them
+ // off of patronSvc.current isn't reliable
+ if (ausp_ids.length > 0) {
+ promises.push(
+ egCore.pcrud.search('ausp',
+ {id : ausp_ids}, {},
+ {atomic : true, authoritative : true}
+ ).then(function(penalties) {
+ return egCore.pcrud.remove(penalties);
+ })
+ );
+ }
+ if (aum_ids.length > 0) {
+ promises.push(
+ egCore.pcrud.search('aum',
+ {id : aum_ids}, {},
+ {atomic : true, authoritative : true}
+ ).then(function(messages) {
+ return egCore.pcrud.remove(messages);
+ })
+ );
+ }
+ $q.all(promises).then(function() {
activeGrid.refresh();
+ archiveGrid.refresh();
+ // force a refresh of the user
+ patronSvc.setPrimary(patronSvc.current.id(), null, true);
});
}
$scope.archivePenalty = function(selected) {
- // the grid stores flattened penalties. Fetch penalty objects first
+ if (selected.length == 0) return;
- var ids = selected.map(function(s){ return s.id });
- egCore.pcrud.search('ausp',
- {id : ids}, {},
- {atomic : true, authoritative : true}
+ // TODO: need confirmation dialog
- // then delete them
- ).then(function(penalties) {
- angular.forEach(penalties, function(p){ p.stop_date('now') });
- return egCore.pcrud.update(penalties);
+ var promises = [];
+ // figure out the view components
+ var aum_ids = [];
+ var ausp_ids = [];
+ angular.forEach(selected, function(s) {
+ if (s.aum_id) { aum_ids.push(s.aum_id); }
+ if (s.ausp_id) { ausp_ids.push(s.ausp_id); }
+ });
- // then refresh the grid
- }).then(function() {
+ // fetch all of them since trying to pull them
+ // off of patronSvc.current isn't reliable
+ if (ausp_ids.length > 0) {
+ promises.push(
+ egCore.pcrud.search('ausp',
+ {id : ausp_ids}, {},
+ {atomic : true, authoritative : true}
+ ).then(function(penalties) {
+ angular.forEach(penalties, function(p) {
+ p.stop_date('now');
+ });
+ return egCore.pcrud.update(penalties);
+ })
+ );
+ }
+ if (aum_ids.length > 0) {
+ promises.push(
+ egCore.pcrud.search('aum',
+ {id : aum_ids}, {},
+ {atomic : true, authoritative : true}
+ ).then(function(messages) {
+ angular.forEach(messages, function(m) {
+ m.stop_date('now');
+ });
+ return egCore.pcrud.update(messages);
+ })
+ );
+ }
+ $q.all(promises).then(function() {
activeGrid.refresh();
archiveGrid.refresh();
+ // force a refresh of the user
+ patronSvc.setPrimary(patronSvc.current.id(), null, true);
});
}
$scope.editPenalty = function(selected) {
if (selected.length == 0) return;
- // grab the penalty from the user object
- var penalty = patronSvc.current.standing_penalties().filter(
- function(p) {return p.id() == selected[0].id})[0];
+ var promises = [];
+ // figure out the view components
+ var aum_ids = []; var aum_objs = {};
+ var ausp_ids = []; var ausp_objs = {};
+ var pairs = [];
+ angular.forEach(selected, function(s) {
+ if (s.aum_id) { aum_ids.push(s.aum_id); }
+ if (s.ausp_id) { ausp_ids.push(s.ausp_id); }
+ pairs.push( { aum_id : s.aum_id, ausp_id : s.ausp_id } );
+ });
- egCirc.edit_penalty(penalty).then(function() {
- activeGrid.refresh();
- // force a refresh of the user, since they may now
- // have blocking penalties, etc.
- patronSvc.setPrimary(patronSvc.current.id(), null, true);
+ // fetch all of them since trying to pull them
+ // off of patronSvc.current isn't reliable
+ // (we want deleted user messages too)
+ if (ausp_ids.length > 0) {
+ promises.push(
+ egCore.pcrud.search('ausp',
+ {id : ausp_ids}, {},
+ {atomic : true, authoritative : true}
+ ).then(function(penalties) {
+ angular.forEach(penalties, function(p) {
+ ausp_objs[p.id()] = p;
+ });
+ return $q.when();
+ })
+ );
+ }
+ if (aum_ids.length > 0) {
+ promises.push(
+ egCore.pcrud.search('aum',
+ {id : aum_ids}, {
+ flesh : 1,
+ flesh_fields : {
+ aum : ['editor']
+ }
+ },
+ {atomic : true, authoritative : true}
+ ).then(function(messages) {
+ angular.forEach(messages, function(m) {
+ aum_objs[m.id()] = m;
+ });
+ return $q.when();
+ })
+ );
+ }
+ $q.all(promises).then(function() {
+ angular.forEach(pairs, function(pair) {
+ egCirc.edit_penalty(ausp_objs[pair.ausp_id],aum_objs[pair.aum_id]).then(function() {
+ activeGrid.refresh();
+ // force a refresh of the user, since they may now
+ // have blocking penalties, etc.
+ patronSvc.setPrimary(patronSvc.current.id(), null, true);
+ });
+ });
});
}
}])
'ui.patron.edit.au.claims_returned_count.suggest',
'ui.patron.edit.au.claims_never_checked_out_count.show',
'ui.patron.edit.au.claims_never_checked_out_count.suggest',
- 'ui.patron.edit.au.alert_message.show',
- 'ui.patron.edit.au.alert_message.suggest',
'ui.patron.edit.aua.post_code.regex',
'ui.patron.edit.aua.post_code.example',
'ui.patron.edit.aua.county.require',
});
}
- service.get_staff_penalty_types = function() {
+ service.get_all_penalty_types = function() {
if (egCore.env.csp)
return $q.when(egCore.env.csp.list);
- return egCore.pcrud.search(
- // id <= 100 are reserved for system use
- 'csp', {id : {'>': 100}}, {}, {atomic : true})
- .then(function(penalties) {
- return egCore.env.absorbList(penalties, 'csp').list;
- });
+ return egCore.pcrud.retrieveAll('csp', {}, {atomic : true}).then(
+ function(penalties) {
+ return egCore.env.absorbList(penalties, 'csp').list;
+ }
+ );
}
// ideally all of these data should be returned with the response,
});
}
+ function generate_penalty_dialog_watch_callback($scope,egCore,allPenalties) {
+ return function(newval) {
+ if (newval) {
+ var selected_penalty = allPenalties.filter(function(p) {
+ return p.id() == newval; })[0];
+ var penalty_id = selected_penalty.id();
+ if (penalty_id == 20 || penalty_id == 21 || penalty_id == 25) {
+ $scope.args.custom_penalty = penalty_id;
+ $scope.args.penalty = penalty_id;
+ }
+ if (penalty_id > 100) {
+ $scope.args.custom_penalty = penalty_id;
+ $scope.args.penalty = null;
+ }
+ // there's a $watch on custom_depth
+ if (selected_penalty.org_depth() || selected_penalty.org_depth() == 0) {
+ $scope.args.custom_depth = selected_penalty.org_depth();
+ } else {
+ $scope.args.custom_depth = $scope.args.org.ou_type().depth();
+ }
+ }
+ };
+ }
+
service.create_penalty = function(user_id) {
return $uibModal.open({
templateUrl: './circ/share/t_new_message_dialog',
backdrop: 'static',
controller:
- ['$scope','$uibModalInstance','staffPenalties',
- function($scope , $uibModalInstance , staffPenalties) {
+ ['$scope','$uibModalInstance','allPenalties','goodOrgs',
+ function($scope , $uibModalInstance , allPenalties , goodOrgs) {
$scope.focusNote = true;
- $scope.penalties = staffPenalties;
+ $scope.penalties = allPenalties.filter(
+ function(p) { return p.id() > 100 || p.id() == 20 || p.id() == 21 || p.id() == 25; });
+ $scope.set_penalty = function(id) {
+ if (!($scope.args.pub && $scope.args.read_date) && !$scope.args.deleted) {
+ $scope.args.penalty = id;
+ }
+ }
$scope.require_initials = service.require_initials;
- $scope.args = {penalty : 21}; // default to Note
- $scope.setPenalty = function(id) {
- args.penalty = id;
+ $scope.update_org = function(org) {
+ if (!($scope.args.pub && $scope.args.read_date) && !$scope.args.deleted) {
+ $scope.args.org = org;
+ }
+ }
+ $scope.cant_use_org = function(org_id) {
+ return ($scope.args.pub && $scope.args.read_date) || $scope.args.deleted || goodOrgs.indexOf(org_id) == -1;
}
+ $scope.args = {
+ pub : false,
+ penalty : 21, // default to Note
+ org : egCore.org.get(egCore.auth.user().ws_ou())
+ };
+ $scope.args.max_depth = $scope.args.org.ou_type().depth();
$scope.ok = function(count) { $uibModalInstance.close($scope.args) }
$scope.cancel = function($event) {
$uibModalInstance.dismiss();
$event.preventDefault();
}
+ $scope.$watch('args.penalty', generate_penalty_dialog_watch_callback($scope,egCore,allPenalties));
+ $scope.$watch('args.custom_penalty', generate_penalty_dialog_watch_callback($scope,egCore,allPenalties));
+ $scope.$watch('args.custom_depth', function(org_depth) {
+ if (org_depth || org_depth == 0) {
+ egCore.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.org_unit.ancestor_at_depth.retrieve',
+ egCore.auth.token(), egCore.auth.user().ws_ou(), org_depth
+ ).then(function(ctx_org) {
+ if (ctx_org) {
+ $scope.args.org = egCore.org.get(ctx_org);
+ }
+ });
+ }
+ });
}],
- resolve : { staffPenalties : service.get_staff_penalty_types }
+ resolve : {
+ allPenalties : service.get_all_penalty_types,
+ goodOrgs : egCore.perm.hasPermAt('UPDATE_USER', true)
+ }
}).result.then(
function(args) {
var pen = new egCore.idl.ausp();
+ var msg = {
+ pub : args.pub,
+ title : args.title,
+ message : args.note ? args.note : ''
+ };
pen.usr(user_id);
- pen.org_unit(egCore.auth.user().ws_ou());
- pen.note(args.note);
- if (args.initials) pen.note(args.note + ' [' + args.initials + ']');
+ pen.org_unit(args.org.id());
+ if (args.initials) msg.message = (args.note ? args.note : '') + ' [' + args.initials + ']';
if (args.custom_penalty) {
pen.standing_penalty(args.custom_penalty);
} else {
return egCore.net.request(
'open-ils.actor',
'open-ils.actor.user.penalty.apply',
- egCore.auth.token(), pen
+ egCore.auth.token(), pen, msg
);
}
);
}
// assumes, for now anyway, penalty type is fleshed onto usr_penalty.
- service.edit_penalty = function(usr_penalty) {
+ service.edit_penalty = function(pen,aum) {
return $uibModal.open({
templateUrl: './circ/share/t_new_message_dialog',
backdrop: 'static',
controller:
- ['$scope','$uibModalInstance','staffPenalties',
- function($scope , $uibModalInstance , staffPenalties) {
+ ['$scope','$uibModalInstance','allPenalties','goodOrgs',
+ function($scope , $uibModalInstance , allPenalties , goodOrgs) {
+ // We may need to vivicate usr_penalty (pen) or usr_message (aum)
+ if (!pen) {
+ pen = new egCore.idl.ausp();
+ pen.usr(aum.usr());
+ pen.org_unit(aum.sending_lib()); // FIXME: preserve sending_lib or use ws_ou?
+ pen.staff(egCore.auth.user().id());
+ pen.set_date('now');
+ pen.usr_message(aum.id());
+ pen.isnew(true);
+ aum.ischanged(true);
+ }
+ if (!aum) {
+ aum = new egCore.idl.aum();
+ aum.create_date('now');
+ aum.sending_lib(pen.org_unit());
+ aum.pub(false);
+ aum.usr(pen.usr());
+ aum.isnew(true);
+ pen.ischanged(true);
+ }
+
$scope.focusNote = true;
- $scope.penalties = staffPenalties;
+ $scope.penalties = allPenalties.filter(
+ function(p) { return p.id() > 100 || p.id() == 20 || p.id() == 21 || p.id() == 25; });
+ $scope.set_penalty = function(id) {
+ if (!($scope.args.pub && $scope.args.read_date) && !$scope.args.deleted) {
+ $scope.args.penalty = id;
+ }
+ }
$scope.require_initials = service.require_initials;
+ $scope.update_org = function(org) {
+ if (!($scope.args.pub && $scope.args.read_date) && !$scope.args.deleted) {
+ $scope.args.org = org;
+ }
+ }
+ $scope.cant_use_org = function(org_id) {
+ return ($scope.args.pub && $scope.args.read_date) || $scope.args.deleted || goodOrgs.indexOf(org_id) == -1;
+ }
+ var penalty_id = pen.standing_penalty();
$scope.args = {
- penalty : usr_penalty.standing_penalty().id(),
- note : usr_penalty.note()
+ penalty : pen.isnew()
+ ? 21 // default to Note
+ : penalty_id,
+ pub : typeof aum.pub() == 'boolean'
+ ? aum.pub()
+ : aum.pub() == 't',
+ title : aum.title(),
+ note : aum.message() ? aum.message() : '',
+ org : egCore.org.get(pen.org_unit()),
+ deleted : typeof aum.deleted() == 'boolean'
+ ? aum.deleted()
+ : aum.deleted() == 't',
+ read_date : aum.read_date(),
+ edit_date : aum.edit_date(),
+ editor : aum.editor()
+ }
+ $scope.args.max_depth = $scope.args.org.ou_type().depth();
+ $scope.original_org = $scope.args.org;
+ $scope.workstation_depth = egCore.org.get(egCore.auth.user().ws_ou()).ou_type().depth();
+ if (penalty_id == 20 || penalty_id == 21 || penalty_id == 25) {
+ $scope.args.custom_penalty = penalty_id;
+ }
+ if (penalty_id > 100) {
+ $scope.args.custom_penalty = penalty_id;
+ $scope.args.penalty = null;
}
- $scope.setPenalty = function(id) { args.penalty = id; }
$scope.ok = function(count) { $uibModalInstance.close($scope.args) }
$scope.cancel = function($event) {
$uibModalInstance.dismiss();
$event.preventDefault();
}
+ $scope.$watch('args.penalty', generate_penalty_dialog_watch_callback($scope,egCore,allPenalties));
+ $scope.$watch('args.custom_penalty', generate_penalty_dialog_watch_callback($scope,egCore,allPenalties));
+ $scope.$watch('args.custom_depth', function(org_depth) {
+ if (org_depth || org_depth == 0) {
+ if (org_depth > $scope.workstation_depth) {
+ $scope.args.org = $scope.original_org;
+ } else {
+ egCore.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.org_unit.ancestor_at_depth.retrieve',
+ egCore.auth.token(), egCore.auth.user().ws_ou(), org_depth
+ ).then(function(ctx_org) {
+ if (ctx_org) {
+ $scope.args.org = egCore.org.get(ctx_org);
+ }
+ });
+ }
+ }
+ });
}],
- resolve : { staffPenalties : service.get_staff_penalty_types }
+ resolve : {
+ allPenalties : service.get_all_penalty_types,
+ goodOrgs : egCore.perm.hasPermAt('UPDATE_USER', true)
+ }
}).result.then(
function(args) {
- usr_penalty.note(args.note);
- if (args.initials) usr_penalty.note(args.note + ' [' + args.initials + ']');
- usr_penalty.standing_penalty(args.penalty);
- return egCore.pcrud.update(usr_penalty);
+ aum.pub(args.pub);
+ aum.title(args.title);
+ aum.message(args.note);
+ aum.sending_lib(egCore.org.get(egCore.auth.user().ws_ou()).id());
+ pen.org_unit(egCore.org.get(args.org).id());
+ if (args.initials) aum.message((args.note ? args.note : '') + ' [' + args.initials + ']');
+ if (args.custom_penalty) {
+ pen.standing_penalty(args.custom_penalty);
+ } else {
+ pen.standing_penalty(args.penalty);
+ }
+ return egCore.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.user.penalty.modify',
+ egCore.auth.token(), pen, aum
+ );
}
);
}
});
}
+ /*
+ * Returns a union of the full org path of each org unit at which the
+ * currently logged in user has the selected permissions.
+ * @permList - list or string. Unlike hasPermAt, the response object
+ * is always a list of org ids (or an empty list).
+ */
+ service.hasPermFullPathAt = function(permList) {
+ return service.hasPermAt(permList, true)
+ .then(function(orgs) {
+ var orgHash = {};
+ if (permList.constructor != Array) {
+ orgHash[permList] = orgs;
+ } else {
+ orgHash = orgs;
+ }
+ var org_seen = {};
+ angular.forEach(orgHash, function(orgList) {
+ angular.forEach(orgList, function(org) {
+ var full_path = egOrg.fullPath(org,true);
+ angular.forEach(full_path, function(org2) {
+ org_seen[org2] = true;
+ });
+ });
+ });
+ return Object.keys(org_seen).map(function(o) { return Number(o); });
+ });
+ }
+
return service;
}])
var p = service.current;
if (service.alert_penalties.length ||
- p.alert_message() ||
p.active() == 'f' ||
p.barred() == 't' ||
service.patron_stats.holds.ready) {
transclude : true,
scope : {
ngModel : '=',
+ useOpacLabel : '@',
+ maxDepth : '@',
},
require: 'ngModel',
templateUrl : './share/t_share_depth_selector',
var scratch = [];
angular.forEach(list, function(aout) {
var depth = parseInt(aout.depth());
- if (depth in scratch) {
- scratch[depth].push(aout.name());
- } else {
- scratch[depth] = [ aout.name() ]
+ if (typeof $scope.maxDepth == 'undefined' || depth <= $scope.maxDepth) {
+ var text = $scope.useOpacLabel ? aout.opac_label() : aout.name();
+ if (depth in scratch) {
+ scratch[depth].push(text);
+ } else {
+ scratch[depth] = [ text ]
+ }
}
});
scratch.forEach(function(val, idx) {
$scope.values.push({ id : idx, name : scratch[idx].join(' / ') });
});
});
- }]
+ }],
+ link : function(scope, elm, attrs) {
+ if ('useOpacLabel' in attrs)
+ scope.useOpacLabel = true;
+ if ('maxDepth' in attrs) // I feel like I'm doing this wrong :)
+ scope.maxDepth = parseInt(attrs.maxdepth);
+ }
}
})