<link field="perm" reltype="has_a" key="id" map="" class="ppl"/>
</links>
</class>
- <class id="mp" controller="open-ils.cstore" oils_obj:fieldmapper="money::payment" oils_persist:tablename="money.payment_view" reporter:core="true" reporter:label="Payments: All">
+ <class id="mp" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="money::payment" oils_persist:tablename="money.payment_view" reporter:core="true" reporter:label="Payments: All">
<fields oils_persist:primary="id" oils_persist:sequence="">
<field reporter:label="Amount" name="amount" reporter:datatype="money" />
<field reporter:label="Payment ID" name="id" reporter:datatype="id" />
<link field="goods_payment" reltype="might_have" key="id" map="" class="mgp"/>
<link field="xact" reltype="has_a" key="id" map="" class="mbt"/>
</links>
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+ <actions>
+ <retrieve permission="VIEW_USER_TRANSACTIONS">
+ <context link="xact" jump="usr" field="home_ou"/>
+ </retrieve>
+ </actions>
+ </permacrud>
</class>
<class id="mbp" controller="open-ils.cstore" oils_obj:fieldmapper="money::bnm_payment" oils_persist:tablename="money.bnm_payment_view" reporter:core="true" reporter:label="Payments: Brick-and-mortar">
<fields oils_persist:primary="id" oils_persist:sequence="">
<div class="tab-content">
<div class="tab-pane active">
- <div ng-if="bill_tab == 'transactions'">
- <div class="flex-row padded">
- <div>[% l('Selected Billed:') %]</div>
- <div>{{selected_billed() | currency}}</div>
- <div>[% l('Selected Paid:') %]</div>
- <div>{{selected_paid() | currency}}</div>
- <div class="flex-cell"></div>
- <div>[% l('Start Date:') %]</div>
- <div><input eg-date-input class="form-control" ng-model="dates.xact_start"/></div>
- <div>[% l('End Date:') %]</div>
- <div><input eg-date-input class="form-control" ng-model="dates.xact_finish"/></div>
- </div><!-- top row -->
- <hr/>
-
- <eg-grid
- idl-class="mbt"
- id-field="id"
- query="xactQuery"
- activate-item="activateBill"
- on-selection-change="gridSelectionChanged"
- revision="xactRevision">
-
- <eg-grid-action
- label="[% l('Add Billing') %]" handler=""></eg-grid-action>
- <eg-grid-action
- label="[% l('Full Details') %]" handler="showFullDetails"></eg-grid-action>
-
- <eg-grid-field path="summary.balance_owed"></eg-grid-field>
- <eg-grid-field path="id" label="[% l('Bill #') %]"></eg-grid-field>
- <eg-grid-field path="xact_finish" label="[% l('Finish') %]"></eg-grid-field>
- <eg-grid-field path="xact_start" label="[% l('Start') %]"></eg-grid-field>
- <eg-grid-field path="summary.total_owed" label="[% l('Total Billed') %]"></eg-grid-field>
- <eg-grid-field path="summary.total_paid" label="[% l('Total Paid') %]"></eg-grid-field>
- <eg-grid-field path="summary.xact_type" label="[% l('Type') %]"></eg-grid-field>
-
-
- <eg-grid-field label="[% l('Title') %]" name="title"
- path="circulation.target_copy.call_number.record.simple_record.title">
- <a href="[% ctx.base_path %]/opac/record/{{item.record_id}}">{{item.title}}</a>
- </eg-grid-field>
-
- <!-- needed for bib link -->
- <eg-grid-field name="record_id"
- path="circulation.target_copy.call_number.record.id"
- required hidden></eg-grid-field>
-
- <eg-grid-field label="[% l('Barcode') %]" name="copy_barcode"
- path="circulation.target_copy.barcode">
- <a target="_self" href="./cat/item/{{item.copy_id}}">{{item.copy_barcode}}</a>
- </eg-grid-field>
-
- <!-- needed for item link -->
- <eg-grid-field name="copy_id"
- path="circulation.target_copy.id" required hidden></eg-grid-field>
-
- <!-- needed for grid query -->
- <eg-grid-field path="summary.last_payment_ts" required hidden></eg-grid-field>
-
- <eg-grid-field path="summary.*" hidden></eg-grid-field>
- <eg-grid-field path="circulation.target_copy.*" hidden></eg-grid-field>
- <eg-grid-field path="circulation.target_copy.call_number.*" hidden></eg-grid-field>
-
- </eg-grid>
- </div><!-- xacts -->
-
+ <div class="flex-row padded">
+ <div ng-if="bill_tab == 'transactions'">[% l('Selected Billed:') %]</div>
+ <div ng-if="bill_tab == 'transactions'">{{totals.selected_billed() | currency}}</div>
+ <div>[% l('Selected Paid:') %]</div>
+ <div>{{totals.selected_paid() | currency}}</div>
+ <div class="flex-cell"></div>
+ <div>[% l('Start Date:') %]</div>
+ <div><input eg-date-input class="form-control" ng-model="dates.xact_start"/></div>
+ <div>[% l('End Date:') %]</div>
+ <div><input eg-date-input class="form-control" ng-model="dates.xact_finish"/></div>
+ </div><!-- top row -->
+ <hr/>
+ [% INCLUDE 'staff/circ/patron/t_bill_history_xacts.tt2' %]
+ [% INCLUDE 'staff/circ/patron/t_bill_history_payments.tt2' %]
</div>
</div>
--- /dev/null
+
+<div ng-if="bill_tab == 'payments'" ng-controller="BillPaymentHistoryCtrl">
+
+ <eg-grid
+ idl-class="mp"
+ id-field="id"
+ query="paymentQuery"
+ sort="paymentSort"
+ activate-item="activatePayment"
+ selected-items="getSelectedItems"
+ all-items="getAllItems"
+ revision="paymentRevision">
+
+ <eg-grid-action
+ label="[% l('Full Details') %]" handler="showFullDetails"></eg-grid-action>
+
+ <eg-grid-field path="amount" label="[% l('Amount') %]"></eg-grid-field>
+ <eg-grid-field path="id" label="[% l('Payment ID') %]"></eg-grid-field>
+ <eg-grid-field path="payment_ts" label="[% l('Payment Time') %]"></eg-grid-field>
+ <eg-grid-field path="note" label="[% l('Note') %]"></eg-grid-field>
+ <eg-grid-field path="voided" label="[% l('Voided') %]"></eg-grid-field>
+ <eg-grid-field path="xact.summary.xact_type" label="[% l('Transaction Type') %]"></eg-grid-field>
+ <eg-grid-field path="xact.summary.last_billing_type" label="[% l('Last Billing Type') %]"></eg-grid-field>
+
+ <eg-grid-field label="[% l('Title') %]" name="title"
+ path="xact.circulation.target_copy.call_number.record.simple_record.title">
+ <a href="[% ctx.base_path %]/opac/record/{{item.record_id}}">{{item.title}}</a>
+ </eg-grid-field>
+
+ <!-- needed for bib link -->
+ <eg-grid-field name="record_id"
+ path="xact.circulation.target_copy.call_number.record.id"
+ required hidden></eg-grid-field>
+
+ <eg-grid-field label="[% l('Barcode') %]" name="copy_barcode"
+ path="xact.circulation.target_copy.barcode">
+ <a target="_self" href="./cat/item/{{item.copy_id}}">{{item.copy_barcode}}</a>
+ </eg-grid-field>
+
+ <!-- needed for item link -->
+ <eg-grid-field name="copy_id"
+ path="xact.circulation.target_copy.id" required hidden></eg-grid-field>
+
+
+
+ <!-- ... -->
+
+ <eg-grid-field path="xact.id" required hidden></eg-grid-field>
+ <eg-grid-field path="xact.usr" required hidden></eg-grid-field>
+ <eg-grid-field path="xact.*" hidden></eg-grid-field>
+ <eg-grid-field path="xact.summary.*" hidden></eg-grid-field>
+
+ <!--
+ <eg-grid-field path="xact.summary.balance_owed"></eg-grid-field>
+ <eg-grid-field path="xact.xact_finish" label="[% l('Finish') %]"></eg-grid-field>
+ <eg-grid-field path="xact.xact_start" label="[% l('Start') %]"></eg-grid-field>
+ <eg-grid-field path="xact.summary.total_owed" label="[% l('Total Billed') %]"></eg-grid-field>
+ <eg-grid-field path="xact.summary.total_paid" label="[% l('Total Paid') %]"></eg-grid-field>
+ <eg-grid-field path="xact.summary.xact_type" label="[% l('Type') %]"></eg-grid-field>
+
+ <eg-grid-field label="[% l('Title') %]" name="title"
+ path="circulation.target_copy.call_number.record.simple_record.title">
+ <a href="[% ctx.base_path %]/opac/record/{{item.record_id}}">{{item.title}}</a>
+ </eg-grid-field>
+
+ <eg-grid-field name="record_id"
+ path="circulation.target_copy.call_number.record.id"
+ required hidden></eg-grid-field>
+
+ <eg-grid-field label="[% l('Barcode') %]" name="copy_barcode"
+ path="circulation.target_copy.barcode">
+ <a target="_self" href="./cat/item/{{item.copy_id}}">{{item.copy_barcode}}</a>
+ </eg-grid-field>
+
+ <eg-grid-field name="copy_id"
+ path="circulation.target_copy.id" required hidden></eg-grid-field>
+
+ <eg-grid-field path="summary.last_payment_ts" required hidden></eg-grid-field>
+
+ <eg-grid-field path="summary.*" hidden></eg-grid-field>
+ <eg-grid-field path="circulation.target_copy.*" hidden></eg-grid-field>
+ <eg-grid-field path="circulation.target_copy.call_number.*" hidden></eg-grid-field>
+ -->
+ </eg-grid>
+</div>
+
--- /dev/null
+
+<div ng-if="bill_tab == 'transactions'" ng-controller="BillXactHistoryCtrl">
+
+ <eg-grid
+ idl-class="mbt"
+ id-field="id"
+ query="xactQuery"
+ activate-item="activateBill"
+ selected-items="getSelectedItems"
+ all-items="getAllItems"
+ revision="xactRevision">
+
+ <eg-grid-action
+ label="[% l('Add Billing') %]" handler=""></eg-grid-action>
+ <eg-grid-action
+ label="[% l('Full Details') %]" handler="showFullDetails"></eg-grid-action>
+
+ <eg-grid-field path="summary.balance_owed"></eg-grid-field>
+ <eg-grid-field path="id" label="[% l('Bill #') %]"></eg-grid-field>
+ <eg-grid-field path="xact_finish" label="[% l('Finish') %]"></eg-grid-field>
+ <eg-grid-field path="xact_start" label="[% l('Start') %]"></eg-grid-field>
+ <eg-grid-field path="summary.total_owed" label="[% l('Total Billed') %]"></eg-grid-field>
+ <eg-grid-field path="summary.total_paid" label="[% l('Total Paid') %]"></eg-grid-field>
+ <eg-grid-field path="summary.xact_type" label="[% l('Type') %]"></eg-grid-field>
+
+ <eg-grid-field label="[% l('Title') %]" name="title"
+ path="circulation.target_copy.call_number.record.simple_record.title">
+ <a href="[% ctx.base_path %]/opac/record/{{item.record_id}}">{{item.title}}</a>
+ </eg-grid-field>
+
+ <!-- needed for bib link -->
+ <eg-grid-field name="record_id"
+ path="circulation.target_copy.call_number.record.id"
+ required hidden></eg-grid-field>
+
+ <eg-grid-field label="[% l('Barcode') %]" name="copy_barcode"
+ path="circulation.target_copy.barcode">
+ <a target="_self" href="./cat/item/{{item.copy_id}}">{{item.copy_barcode}}</a>
+ </eg-grid-field>
+
+ <!-- needed for item link -->
+ <eg-grid-field name="copy_id"
+ path="circulation.target_copy.id" required hidden></eg-grid-field>
+
+ <!-- needed for grid query -->
+ <eg-grid-field path="summary.last_payment_ts" required hidden></eg-grid-field>
+
+ <eg-grid-field path="summary.*" hidden></eg-grid-field>
+ <eg-grid-field path="circulation.target_copy.*" hidden></eg-grid-field>
+ <eg-grid-field path="circulation.target_copy.call_number.*" hidden></eg-grid-field>
+ </eg-grid>
+</div>
+
activate-item="activateBill"
on-item-retrieved="gridItemRetrieved"
- on-selection-change="gridSelectionChanged"
+ selected-items="getSelectedItems"
+ all-items="getAllItems"
revision="gridRevision"
persist-key="circ.patron.bills">
main-label="[% l('Patron Search Results') %]"
items-provider="patronSearchGridProvider"
activate-item="activatePatron"
- on-selection-change="gridSelectionChanged"
+ selected-items="getSelectedItems"
persist-key="circ.patron.search">
<eg-grid-field label="[% ('ID') %]" path='id' visible></eg-grid-field>
<eg-grid-field label="[% ('Card') %]" path='card.barcode' visible></eg-grid-field>
.pad-horiz {padding : 0px 10px 0px 10px; }
.pad-vert {padding : 20px 0px 10px 0px;}
-/*#print-div { display: none; }*/
+#print-div { display: none; }
/* by default, give all tab panes some top padding */
.tab-pane { padding-top: 20px; }
return {offset : 0};
}
- $scope.gridSelectionChanged = function(all) {
- if (all[0])
- patronSvc.setDefault(null, all[0]);
- }
-
+ $scope.getSelectedItems = function() {return []};
+ $scope.$watch(
+ function() {return $scope.getSelectedItems()},
+ function(list) {
+ if (list[0])
+ patronSvc.setDefault(null, list[0]);
+ },
+ true
+ );
+
provider.get = function(offset, count) {
var deferred = $q.defer();
$scope.annotate_payment = false;
$scope.gridRevision = 0;
var allItems = []; // all grid items (flattened mbt's)
- var selectedItems = []; // selected grid items
+ // until the grid wakes up, return empty set
+ $scope.getSelectedItems = $scope.getAllItems = function() {return []};
billSvc.fetchSummary().then(function(s) {$scope.summary = s});
// TODO: move me to service
function selected_payment_info() {
var info = {owed : 0, billed : 0, paid : 0};
- angular.forEach(selectedItems, function(item) {
+ angular.forEach($scope.getSelectedItems(), function(item) {
info.owed += Number(item['summary.balance_owed']) * 100;
info.billed += Number(item['summary.total_owed']) * 100;
info.paid += Number(item['summary.total_paid']) * 100;
}
$scope.refunds_available = function() {
var amount = 0;
- angular.forEach(allItems, function(item) {
+ angular.forEach($scope.getAllItems(), function(item) {
if (item['summary.balance_owed'] < 0)
amount += item['summary.balance_owed'] * 100;
});
$scope.gridItemRetrieved = function(item) {
item.payment_pending = 0;
- allItems.push(item);
}
- $scope.gridSelectionChanged = function(all) {
- // update the item.payment_pending value each time the user
- // selects different transactions to pay against.
- selectedItems = all;
- updatePendingColumn();
- }
+ // update the item.payment_pending value each time the user
+ // selects different transactions to pay against.
+ $scope.$watch(
+ function() {return $scope.getSelectedItems()},
+ function() {updatePendingColumn()},
+ true
+ );
// update the item.payment_pending for each (selected)
// transaction any time the user-entered payment amount is modified
// of our local scope variables.
function updatePendingColumn() {
// reset all to zero..
- angular.forEach(allItems, function(item) {item.payment_pending = 0});
+ angular.forEach($scope.getAllItems(),
+ function(item) {item.payment_pending = 0});
var payment_amount = $scope.pending_payment();
- for (var i = 0; i < selectedItems.length; i++) { // for/break
- var item = selectedItems[i];
+ var selected = $scope.getSelectedItems();
+ for (var i = 0; i < selected.length; i++) { // for/break
+ var item = selected[i];
var owed = Number(item['summary.balance_owed']);
if (payment_amount > owed) {
// which have a pending payment amount.
function generatePayments() {
var payments = [];
- angular.forEach(selectedItems, function(item) {
+ angular.forEach($scope.getSelectedItems(), function(item) {
if (item.payment_pending == 0) return;
payments.push([item.id, item.payment_pending]);
});
}
$scope.printBills = function(selected) {
- console.log('HEREEE');
-
// bills print receipt assumes nested hashes, but our grid
// stores flattener data. Fetch the selected xacts as
// fleshed pcrud objects. (Consider an alternate approach..)
{authoritative : true}
).then(
function() {
- egCore.hatch.printFromTemplate('receipt', 'bills_current',
+ egCore.hatch.printFromTemplate(
+ 'receipt', 'bills_current',
{ transactions : xacts,
current_location : egCore.idl.toHash(
egCore.org.get(egCore.auth.user().ws_ou()))
$scope.initTab('bills', $routeParams.id);
billSvc.userId = $routeParams.id;
$scope.bill_tab = $routeParams.history_tab;
+ $scope.totals = {};
- var selectedItems = [];
- $scope.gridSelectionChanged = function(selected) {
- selectedItems = selected;
+ var start = new Date(); // now - 1 year
+ start.setFullYear(start.getFullYear() - 1),
+ $scope.dates = {
+ xact_start : start,
+ xact_finish : new Date()
+ }
+
+ $scope.date_range = function() {
+ var start = $scope.dates.xact_start.toISOString().replace(/T.*/,'');
+ var end = $scope.dates.xact_finish.toISOString().replace(/T.*/,'');
+ var today = new Date().toISOString().replace(/T.*/,'');
+ if (end == today) end = 'now';
+ return [start, end];
}
+}])
+
+
+.controller('BillXactHistoryCtrl',
+ ['$scope','$q','egCore','patronSvc','billSvc','egPromptDialog','$location',
+function($scope, $q , egCore , patronSvc , billSvc , egPromptDialog , $location) {
+ $scope.getSelectedItems = function() {return []};
+
// TODO; move me to service
function selected_payment_info() {
var info = {owed : 0, billed : 0, paid : 0};
- angular.forEach(selectedItems, function(item) {
+ angular.forEach($scope.getSelectedItems(), function(item) {
info.owed += Number(item['summary.balance_owed']) * 100;
info.billed += Number(item['summary.total_owed']) * 100;
info.paid += Number(item['summary.total_paid']) * 100;
return info;
}
- $scope.selected_billed = function() {
+ $scope.totals.selected_billed = function() {
return selected_payment_info().billed;
}
- $scope.selected_paid = function() {
+ $scope.totals.selected_paid = function() {
return selected_payment_info().paid;
}
- var start = new Date(); // now - 1 year
- start.setFullYear(start.getFullYear() - 1),
- $scope.dates = {
- xact_start : start,
- xact_finish : new Date()
- }
-
$scope.xactQuery = function() {
- // strip the time for our search purposes
- var start = $scope.dates.xact_start.toISOString().replace(/T.*/,'');
- var end = $scope.dates.xact_finish.toISOString().replace(/T.*/,'');
- var today = new Date().toISOString().replace(/T.*/,'');
-
// open-ils.actor.user.transactions.history.have_bill_or_payment
- var query = {
+ return {
'-or' : [
{'summary.balance_owed' : {'<>' : 0}},
{'summary.last_payment_ts' : {'<>' : null}}
],
- xact_start : {'>=' : start},
+ xact_start : {between : $scope.date_range()},
usr : billSvc.userId
}
-
- // end date of today implies that xacts with a null xact_finish
- // are also acceptable
- if (end < today) query.xact_finish = {'<=' : end};
-
- return query;
}
$scope.showFullDetails = function(all) {
$scope.showFullDetails([xact]);
}
+}])
+
+.controller('BillPaymentHistoryCtrl',
+ ['$scope','$q','egCore','patronSvc','billSvc','$location',
+function($scope, $q , egCore , patronSvc , billSvc , $location) {
+ $scope.getSelectedItems = function() {return []};
+
+ $scope.paymentSort = [{'payment_ts' : 'DESC'}, 'id'];
+ $scope.paymentQuery = function() {
+ return {
+ 'payment_ts' : {between : $scope.date_range()},
+ 'xact.usr' : billSvc.userId
+ }
+ }
+
+ $scope.showFullDetails = function(all) {
+ if (all[0])
+ $location.path('/circ/patron/' +
+ patronSvc.current.id() + '/bill/' + all[0]['xact.id']);
+ }
+ $scope.activatePayment = function(payment) {
+ $scope.showFullDetails([payment]);
+ }
+
+ $scope.totals.selected_paid = function() {
+ var paid = 0;
+ angular.forEach($scope.getSelectedItems(), function(payment) {
+ paid += Number(payment.amount) * 100;
+ });
+ return paid / 100;
+ }
}])
+
// are absorbed by the grid.
onItemRetrieved : '=',
+ // TODO: deprecated
// function called with all selected items each time the
// selection set changes
- onSelectionChange : '=',
+ //onSelectionChange : '=',
// function; if set, row index values will be hyperlinked and
// the onclick for an item will call activateItem with the item
// as the argument.
activateItem : '=',
+ // Function. Returns an array of selected items to the caller.
+ // The contents of this function are defined by the grid and
+ // called by the caller.
+ selectedItems : '=',
+
+ // Function. Returns an array of all grid items.
+ // The contents of this function are defined by the grid and
+ // called by the caller.
+ allItems : '=',
+
+ // Function. Accepts an array of index values.
+ // The contents of this function are defined by the grid and
+ // called by the caller.
+ //setSelected : '=',
+
// if set, we watch this scope variable for changes. If it
// changes, we refresh the grid.
revision : '='
delete $scope.autoFields;
}
+ /*
if (grid.onSelectionChange) {
$scope.$watch('selected', function() {
grid.onSelectionChange(grid.getSelectedItems())
}, true);
}
-
+ */
if (!grid.dataProvider) {
return grid.selectOneItem(index);
}
+ // only link the caller mediation functions into
+ // the scope if the caller defines handlers.
+ if ($scope.selectedItems) {
+ console.log('updating selecte..');
+ $scope.selectedItems = function() {
+ return grid.getSelectedItems()
+ };
+ }
+ if ($scope.allItems) {
+ $scope.allItems = function() {
+ return grid.items;
+ }
+ }
+ if ($scope.setSelected) {
+ $scope.setSelected = function(ids) {
+ $scope.selected = {};
+ angular.forEach(ids, function(id) {
+ $scope.selected[''+id] = true;
+ });
+ }
+ }
+
$scope.itemFieldValue = grid.dataProvider.itemFieldValue;
$scope.indexValue = function(item) {
return grid.indexValue(item)
$scope.printCSV = function() {
$scope.gridColumnPickerIsOpen = false;
- egCore.hatch.print('text/plain', grid.generateCSV())
+ egCore.hatch.print('default', 'text/plain', grid.generateCSV())
.then(function() { console.debug('print complete') });
}