From 00ca092f929daf60a00110b46be183b893641c0a Mon Sep 17 00:00:00 2001 From: senator Date: Wed, 31 Mar 2010 19:38:02 +0000 Subject: [PATCH] Acq: Indicate funds at stop/warning balance level with color and warnings Specifically, in the lineitem detail (copy) interface, where there are fund dropdowns. git-svn-id: svn://svn.open-ils.org/ILS/trunk@16071 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- .../src/perlmods/OpenILS/Application/Acq/Order.pm | 62 ++++++++--- Open-ILS/web/js/dojo/openils/acq/nls/acq.js | 4 +- Open-ILS/web/js/ui/default/acq/common/li_table.js | 115 +++++++++++++++++++-- 3 files changed, 160 insertions(+), 21 deletions(-) diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm index 220c65ec9d..c0283c4ef4 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm @@ -698,20 +698,54 @@ sub create_lineitem_detail_debit { } -sub fund_exceeds_balance_stop_percent { - return fund_exceeds_balance_percent( - @_, "balance_stop_percent", "ACQ_FUND_EXCEEDS_STOP_PERCENT" - ); -} +__PACKAGE__->register_method( + "method" => "fund_exceeds_balance_percent_api", + "api_name" => "open-ils.acq.fund.check_balance_percentages", + "signature" => { + "desc" => q/Determine whether a given fund exceeds its defined + "balance stop and warning percentages"/, + "params" => [ + {"desc" => "Authentication token", "type" => "string"}, + {"desc" => "Fund ID", "type" => "number"}, + {"desc" => "Theoretical debit amount (optional)", + "type" => "number"} + ], + "return" => {"desc" => q/An array of two values, for stop and warning, + in that order: 1 if fund exceeds that balance percentage, else 0/} + } +); -sub fund_exceeds_balance_warning_percent { - return fund_exceeds_balance_percent( - @_, "balance_warning_percent", "ACQ_FUND_EXCEEDS_WARN_PERCENT" - ); +sub fund_exceeds_balance_percent_api { + my ($self, $conn, $auth, $fund_id, $debit_amount) = @_; + + $debit_amount ||= 0; + + my $e = new_editor("authtoken" => $auth); + return $e->die_event unless $e->checkauth; + + my $fund = $e->retrieve_acq_fund($fund_id) or return $e->die_event; + return $e->die_event unless $e->allowed("VIEW_FUND", $fund->org); + + my $result = [ + fund_exceeds_balance_percent($fund, $debit_amount, $e, "stop"), + fund_exceeds_balance_percent($fund, $debit_amount, $e, "warning") + ]; + + $e->disconnect; + return $result; } sub fund_exceeds_balance_percent { - my ($fund, $debit_amount, $e, $method_name, $event_name) = @_; + my ($fund, $debit_amount, $e, $which) = @_; + + my ($method_name, $event_name) = @{{ + "warning" => [ + "balance_warning_percent", "ACQ_FUND_EXCEEDS_WARN_PERCENT" + ], + "stop" => [ + "balance_stop_percent", "ACQ_FUND_EXCEEDS_STOP_PERCENT" + ] + }->{$which}}; if ($fund->$method_name) { my $balance = @@ -752,10 +786,12 @@ sub create_fund_debit { my $fund = $mgr->editor->retrieve_acq_fund($args{fund}) or return 0; return 0 if - fund_exceeds_balance_stop_percent($fund, $args{"amount"}, $mgr->editor); + fund_exceeds_balance_percent( + $fund, $args{"amount"}, $mgr->editor, "stop" + ); return 0 if - $dry_run and fund_exceeds_balance_warning_percent( - $fund, $args{"amount"}, $mgr->editor + $dry_run and fund_exceeds_balance_percent( + $fund, $args{"amount"}, $mgr->editor, "warning" ); my $debit = Fieldmapper::acq::fund_debit->new; diff --git a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js index 56ba178a31..e88bdc848d 100644 --- a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js +++ b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js @@ -53,5 +53,7 @@ 'PO_COULD_ACTIVATE' : "Yes.", 'PO_WARNING_NO_BLOCK_ACTIVATION': "Yes; fund ${0} (${1}) would be encumbered beyond its warning level.", 'PO_STOP_BLOCKS_ACTIVATION': "No; fund ${0} (${1}) would be encumbered beyond its stop level.", - 'PO_FUND_WARNING_CONFIRM': "Are you sure? Did you see the warning about over-encumbering a fund?" + 'PO_FUND_WARNING_CONFIRM': "Are you sure? Did you see the warning about over-encumbering a fund?", + 'CONFIRM_FUNDS_AT_STOP': "One or more of the selected funds has a balance below its stop level.\nYou may not be able to activate purchase orders incorporating these copies.\nContinue?", + 'CONFIRM_FUNDS_AT_WARNING': "One or more of the selected funds has a balance below its warning level.\nContinue?", } diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js index 7f49c95749..121bdb4db5 100644 --- a/Open-ILS/web/js/ui/default/acq/common/li_table.js +++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js @@ -22,7 +22,9 @@ var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq'); const XUL_OPAC_WRAPPER = 'chrome://open_ils_staff_client/content/cat/opac.xul'; var li_exportable_attrs = ["issn", "isbn", "upc"]; -var fundLabelFormat = ['${0} (${1})', 'code', 'year']; +var fundLabelFormat = [ + '${1} (${2})', 'id', 'code', 'year' +]; var fundSearchFormat = ['${0} (${1})', 'code', 'year']; function nodeByName(name, context) { @@ -32,6 +34,10 @@ function nodeByName(name, context) { var liDetailBatchFields = ['fund', 'owning_lib', 'location', 'collection_code', 'circ_modifier', 'cn_label']; var liDetailFields = liDetailBatchFields.concat(['barcode', 'note']); +var fundStyles = { + "stop": "color: #c00; font-weight: bold;", + "warning": "color: #c93;" +}; function AcqLiTable() { @@ -40,6 +46,8 @@ function AcqLiTable() { this.plCache = {}; this.poCache = {}; this.relCache = {}; + this.haveFundClass = {} + this.fundBalanceState = {}; this.realDfaCache = {}; this.virtDfaCounts = {}; this.virtDfaId = -1; @@ -1131,14 +1139,27 @@ function AcqLiTable() { searchFilter : (field == 'fund') ? {"active": "t"} : null, parentNode : dojo.query('[name='+field+']', row)[0], orgLimitPerms : ['CREATE_PICKLIST'], - dijitArgs : {required:false}, + dijitArgs : { + "required": false, + "labelType": (field == "fund") ? "html" : null + }, + noCache: (field == "fund"), forceSync : true }); widget.build( function(w, ww) { + if (field == "fund" && w.store) + self._ensureCSSFundClasses(w.store); self.copyBatchWidgets[field] = w; } ); + if (field == "fund") { + dojo.connect( + widget.widget, "onChange", function(val) { + self._updateFundSelectorStyle(widget, val); + } + ); + } } } ); @@ -1234,8 +1255,9 @@ function AcqLiTable() { fmField : field, labelFormat : (field == 'fund') ? fundLabelFormat : null, searchFormat : (field == 'fund') ? fundSearchFormat : null, + dijitArgs: {"labelType": (field == 'fund') ? "html" : null}, searchFilter : searchFilter, - noCache: true, + noCache: (field == "fund"), fmClass : 'acqlid', parentNode : dojo.query('[name='+field+']', row)[0], orgLimitPerms : ['CREATE_PICKLIST', 'CREATE_PURCHASE_ORDER'], @@ -1244,13 +1266,18 @@ function AcqLiTable() { widget.build( // make sure we capture the value from any async widgets function(w, ww) { + if (field == "fund" && w.store) + self._ensureCSSFundClasses(w.store); copy[field](ww.getFormattedValue()) self.copyWidgetCache[copy.id()][field] = w; } ); dojo.connect(widget.widget, 'onChange', function(val) { - if(copy.isnew() || val != copy[field]()) { + if (field == "fund") + self._updateFundSelectorStyle(widget, val); + + if (copy.isnew() || val != copy[field]()) { // prevent setting ischanged() automatically on widget load for existing copies copy[field](widget.getFormattedValue()) copy.ischanged(true); @@ -1263,6 +1290,55 @@ function AcqLiTable() { this.updateLidState(copy, row); }; + this._ensureCSSFundClass = function(id) { + if (!this.fundStyleSheet) { + dojo.create( + "style", {"type": "text/css"}, + document.getElementsByTagName("head")[0], "last" + ); + this.fundStyleSheet = document.styleSheets[ + document.styleSheets.length - 1 + ]; + } + + var cn = "fund_" + id; + if (!this.haveFundClass[cn]) { + fieldmapper.standardRequest( + ["open-ils.acq", "open-ils.acq.fund.check_balance_percentages"], + { + "params": [openils.User.authtoken, id], + "async": true, + "oncomplete": function(r) { + r = openils.Util.readResponse(r); + self.fundBalanceState[id] = r; + var style = ""; + if (r[0] /* stop */) + style = fundStyles.stop; + else if (r[1] /* warning */) + style = fundStyles.warning; + self.fundStyleSheet.insertRule( + "." + cn + " { " + style + " }", + self.fundStyleSheet.cssRules.length + ); + self.haveFundClass[cn] = true; + } + } + ); + } + }; + + this._ensureCSSFundClasses = function(store) { + store.fetch({ + "query": {"id": "*"}, + "onItem": function(o) { self._ensureCSSFundClass(o.id[0]); } + }); + }; + + this._updateFundSelectorStyle = function(widget, fund_id) { + openils.Util.removeCSSClass(widget.widget.domNode, /fund_\d+/); + openils.Util.addCSSClass(widget.widget.domNode, "fund_" + fund_id); + }; + this.updateLidState = function(copy, row) { if (typeof(row) == "undefined") { row = dojo.query( @@ -1444,6 +1520,28 @@ function AcqLiTable() { return L; } + this.confirmBreachedCopyFunds = function(copies) { + var stop = 0, warning = 0; + copies.forEach( + function(o) { + if (o.fund()) { + var state = self.fundBalanceState[o.fund()]; + if (state[0] /* stop */) + stop++; + else if (state[1] /* warning */) + warning++; + } + } + ); + + if (stop) { + return confirm(localeStrings.CONFIRM_FUNDS_AT_STOP); + } else if (warning) { + return confirm(localeStrings.CONFIRM_FUNDS_AT_WARNING); + } + return true; + }; + this.saveCopyChanges = function(liId) { var self = this; var copies = []; @@ -1459,14 +1557,17 @@ function AcqLiTable() { } } - if (typeof(this._copy_count_cb) == "function") { - this._copy_count_cb(liId, total); - } dojo.byId('acq-lit-copy-count-label-' + liId).innerHTML = total; if (copies.length > 0) { + if (!this.confirmBreachedCopyFunds(copies)) + return; + + if (typeof(this._copy_count_cb) == "function") + this._copy_count_cb(liId, total); + openils.Util.show("acq-lit-update-copies-progress"); fieldmapper.standardRequest( ['open-ils.acq', 'open-ils.acq.lineitem_detail.cud.batch'], -- 2.11.0