From: Bill Erickson Date: Fri, 16 Apr 2021 16:44:10 +0000 (-0400) Subject: LP1904036 billing history X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=e5e8dc17a331d57ad915d8f630f245a1577a8077;p=Evergreen.git LP1904036 billing history Signed-off-by: Bill Erickson Signed-off-by: Jane Sandberg Signed-off-by: Galen Charlton --- diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/billing-history.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/billing-history.component.html new file mode 100644 index 0000000000..d7166365b2 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/billing-history.component.html @@ -0,0 +1,41 @@ +

Bill History

+ + + + + + + +
+ diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/billing-history.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/billing-history.component.ts new file mode 100644 index 0000000000..751140aa48 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/billing-history.component.ts @@ -0,0 +1,138 @@ +import {Component, Input, OnInit, AfterViewInit, ViewChild} from '@angular/core'; +import {Router, ActivatedRoute, ParamMap} from '@angular/router'; +import {from, empty, range} from 'rxjs'; +import {concatMap, tap, takeLast} from 'rxjs/operators'; +import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap'; +import {IdlObject, IdlService} from '@eg/core/idl.service'; +import {EventService} from '@eg/core/event.service'; +import {OrgService} from '@eg/core/org.service'; +import {NetService} from '@eg/core/net.service'; +import {PcrudService, PcrudContext} from '@eg/core/pcrud.service'; +import {AuthService} from '@eg/core/auth.service'; +import {ServerStoreService} from '@eg/core/server-store.service'; +import {PatronService} from '@eg/staff/share/patron/patron.service'; +import {PatronContextService, BillGridEntry} from './patron.service'; +import {GridDataSource, GridColumn, GridCellTextGenerator} from '@eg/share/grid/grid'; +import {GridComponent} from '@eg/share/grid/grid.component'; +import {Pager} from '@eg/share/util/pager'; +import {CircService, CircDisplayInfo} from '@eg/staff/share/circ/circ.service'; +import {PrintService} from '@eg/share/print/print.service'; +import {PromptDialogComponent} from '@eg/share/dialog/prompt.component'; +import {AlertDialogComponent} from '@eg/share/dialog/alert.component'; +import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component'; +import {BillingService} from '@eg/staff/share/billing/billing.service'; +import {AddBillingDialogComponent} from '@eg/staff/share/billing/billing-dialog.component'; +import {AudioService} from '@eg/share/util/audio.service'; +import {ToastService} from '@eg/share/toast/toast.service'; + +@Component({ + templateUrl: 'billing-history.component.html', + selector: 'eg-patron-billing-history' +}) +export class BillingHistoryComponent implements OnInit { + + @Input() patronId: number; + @Input() tab: string; + + xactsDataSource: GridDataSource = new GridDataSource(); + paymentsDataSource: GridDataSource = new GridDataSource(); + + xactsTextGenerator: GridCellTextGenerator; + paymentsTextGenerator: GridCellTextGenerator; + + @ViewChild('xactsGrid') private xactsGrid: GridComponent; + @ViewChild('paymentsGrid') private paymentsGrid: GridComponent; + @ViewChild('billingDialog') private billingDialog: AddBillingDialogComponent; + + constructor( + private router: Router, + private route: ActivatedRoute, + private org: OrgService, + private evt: EventService, + private net: NetService, + private pcrud: PcrudService, + private auth: AuthService, + private idl: IdlService, + private circ: CircService, + private billing: BillingService, + private printer: PrintService, + public patronService: PatronService, + public context: PatronContextService + ) {} + + ngOnInit() { + + this.xactsDataSource.getRows = (pager: Pager, sort: any[]) => { + const orderBy: any = {}; + if (sort.length) { + orderBy.mb = sort[0].name + ' ' + sort[0].dir; + } + + return this.pcrud.search('mbt', {usr: this.patronId}, { + order_by: orderBy, + join: { + mbts: { + filter: { + '-or': [ + {balance_owed: {'<>': 0}}, + {last_payment_ts: {'<>': null}} + ] + } + } + } + }); + }; + + /* + this.paymentsDataSource.getRows = (pager: Pager, sort: any[]) => { + const orderBy: any = {}; + if (sort.length) { + orderBy.mp = sort[0].name + ' ' + sort[0].dir; + } + return this.pcrud.search( + 'mp', {xact: this.xactId}, {order_by: orderBy}); + }; + */ + } + + showStatement(row: BillGridEntry) { + this.router.navigate(['/staff/circ/patron', + this.patronId, 'bills', row.xact.id(), 'statement']); + } + + addBillingForXact(rows: BillGridEntry[]) { + if (rows.length === 0) { return; } + const xactIds = rows.map(r => r.xact.id()); + + this.billingDialog.newXact = false; + let changesApplied = false; + + from(xactIds) + .pipe(concatMap(id => { + this.billingDialog.xactId = id; + return this.billingDialog.open(); + })) + .pipe(tap(data => { + if (data) { + changesApplied = true; + } + })) + .subscribe(null, null, () => { + if (changesApplied) { + this.xactsGrid.reload(); + } + }); + } + + printBills(rows: BillGridEntry[]) { + if (rows.length === 0) { return; } + + this.printer.print({ + templateName: 'bills_historical', + contextData: {xacts: rows.map(r => r.xact)}, + printContext: 'default' + }); + } +} + + diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/bills.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/bills.component.html index 9b4b07dd89..6e790eadd0 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/bills.component.html +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/bills.component.html @@ -181,11 +181,15 @@ - + + + + diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/bills.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/bills.component.ts index 0705c0a4c0..035ffc0450 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/bills.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/bills.component.ts @@ -167,7 +167,8 @@ export class BillsComponent implements OnInit, AfterViewInit { return this.net.request( 'open-ils.actor', 'open-ils.actor.user.transactions.for_billing', - this.auth.token(), this.patronId, refreshXacts + this.auth.token(), this.patronId, + {have_balance: true, xact_ids: refreshXacts} ).pipe(tap(resp => { diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.html index dba472e041..61655c07d0 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.html +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.html @@ -92,7 +92,11 @@ - + + + + + diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.ts index d87bac094e..b48e106734 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.ts @@ -25,6 +25,7 @@ export class PatronComponent implements OnInit, AfterViewInit { patronTab = 'search'; altTab: string; statementXact: number; + billingHistoryTab: string; showSummary = true; loading = true; @@ -92,10 +93,12 @@ export class PatronComponent implements OnInit, AfterViewInit { } watchForTabChange() { + this.route.paramMap.subscribe((params: ParamMap) => { this.patronTab = params.get('tab') || 'search'; this.patronId = +params.get('id'); this.statementXact = +params.get('xactId'); + this.billingHistoryTab = params.get('billingHistoryTab'); if (MAIN_TABS.includes(this.patronTab)) { this.altTab = null; diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.module.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.module.ts index 4997a5439c..0417d3eb9d 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.module.ts @@ -31,6 +31,7 @@ import {PatronBarcodesDialogComponent} from './barcodes.component'; import {HoldNotifyUpdateDialogComponent} from './hold-notify-update.component'; import {PatronMessagesComponent} from './messages.component'; import {PatronPermsComponent} from './perms.component'; +import {BillingHistoryComponent} from './billing-history.component'; @NgModule({ declarations: [ @@ -44,6 +45,7 @@ import {PatronPermsComponent} from './perms.component'; ItemsComponent, BillsComponent, BillStatementComponent, + BillingHistoryComponent, TestPatronPasswordComponent, PatronMessagesComponent, PatronSurveyResponsesComponent, diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/routing.module.ts index 728b86be23..4e4e142f2a 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/routing.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/routing.module.ts @@ -48,6 +48,10 @@ const routes: Routes = [{ component: PatronComponent, resolve: {resolver : PatronResolver} }, { + path: ':id/:tab/history/:billingHistoryTab', + component: PatronComponent, + resolve: {resolver : PatronResolver} + }, { path: ':id/:tab', component: PatronComponent, resolve: {resolver : PatronResolver}, diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm index 5db789a3b4..c591ad8b70 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm @@ -5359,6 +5359,11 @@ __PACKAGE__->register_method( params => [ {desc => 'Authentication token', type => 'string'}, {desc => 'User ID', type => 'number'}, + {desc => q/Options: { + xact_ids: load specific transactions + have_balance: + have_charge: + }/, type => 'object'}, {desc => 'Xact IDs. Optionally limit to specific transactions', type => 'array'} ], @@ -5370,7 +5375,13 @@ __PACKAGE__->register_method( ); sub user_billing_xacts { - my ($self, $client, $auth, $user_id, $xact_ids) = @_; + my ($self, $client, $auth, $user_id, $options) = @_; + + $options ||= {}; + my $xact_ids = $options->{xact_ids}; + my $have_balance = $options->{have_balance}; + my $have_charge = $options->{have_charge}; + my $have_payment = $options->{have_payment}; my $e = new_editor(authtoken => $auth, xact => 1); return $e->die_event unless $e->checkauth; @@ -5383,6 +5394,12 @@ sub user_billing_xacts { # Start with the user summary. $client->respond($e->retrieve_money_open_with_balance_user_summary($user_id)); + my $where = {}; + if ($xact_ids) { $where->{id} = $xact_ids; } + if ($have_balance) { $where->{balance_owed} = {'<>' => 0}; } + if ($have_charge) { $where->{last_billing_ts} = {'<>' => undef}; } + if ($have_payment) { $where->{last_payment_ts} = {'<>' => undef}; } + # Even if xact_ids are specified, run this query to confirm the # provided IDs are linked to the specified user and have a balance. $xact_ids = $e->json_query({ diff --git a/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 index ccd7320571..e26a9681b3 100644 --- a/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 +++ b/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 @@ -50,7 +50,9 @@ +