<eg-alert-dialog #maxPayDialog
i18n-dialogBody dialogBody="Payments over 100,000 are denied by policy.">
</eg-alert-dialog>
+
<eg-confirm-dialog #warnPayDialog
i18n-dialogBody i18n-dialogTitle dialogTitle="Confirm Payment Amount"
dialogBody="Are you sure you want to apply a payment of {{paymentAmount | currency}}?">
</eg-confirm-dialog>
+<eg-confirm-dialog #voidBillsDialog
+ i18n-dialogBody i18n-dialogTitle dialogTitle="Void Billings"
+ dialogBody="Are you sure you would like to void {{voidAmount | currency}} in bills for the selected transactions?">
+</eg-confirm-dialog>
+
<eg-credit-card-dialog [patron]="patron()" #creditCardDialog>
</eg-credit-card-dialog>
<eg-grid-toolbar-button i18n-label label="Add Billing"
(onClick)="addBilling()"></eg-grid-toolbar-button>
+ <!-- ACTIONS FOR SELECTED -->
+
<eg-grid-toolbar-button i18n-label label="Select All Refunds"
(onClick)="selectRefunds()"></eg-grid-toolbar-button>
i18n-label label="Print Bills" (onClick)="printBills($event)">
</eg-grid-toolbar-action>
+ <eg-grid-toolbar-action
+ i18n-label label="Void All Billings" (onClick)="voidBillings($event)">
+ </eg-grid-toolbar-action>
+
+ <!-- COLUMNS -->
+
<eg-grid-column path="xact.id" [index]="true" label="Bill #" i18n-label>
</eg-grid-column>
import {concatMap, tap, takeLast} from 'rxjs/operators';
import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
import {IdlObject} 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';
maxPayAmount = 100000;
warnPayAmount = 1000;
+ voidAmount = 0;
gridDataSource: GridDataSource = new GridDataSource();
cellTextGenerator: GridCellTextGenerator;
@ViewChild('annotateDialog') private annotateDialog: PromptDialogComponent;
@ViewChild('maxPayDialog') private maxPayDialog: AlertDialogComponent;
@ViewChild('warnPayDialog') private warnPayDialog: ConfirmDialogComponent;
+ @ViewChild('voidBillsDialog') private voidBillsDialog: ConfirmDialogComponent;
@ViewChild('creditCardDialog') private creditCardDialog: CreditCardDialogComponent;
@ViewChild('billingDialog') private billingDialog: AddBillingDialogComponent;
private router: Router,
private route: ActivatedRoute,
private org: OrgService,
+ private evt: EventService,
private net: NetService,
private pcrud: PcrudService,
private auth: AuthService,
// summary, and slot them back into the entries array.
load(refreshXacts?: number[]): Promise<any> {
- if (!refreshXacts) { this.entries = []; }
+ const entriesFetched: number[] = [];
this.summary = null;
this.gridDataSource.requestingData = true;
+ if (!refreshXacts) { this.entries = []; }
+
return this.net.request(
'open-ils.actor',
'open-ils.actor.user.transactions.for_billing',
return;
}
- if (refreshXacts) {
- let idx = 0;
- for (;idx < this.entries.length; idx++) {
- const entry = this.entries[idx];
- if (entry.xact.id() === resp.id()) { break; }
- }
+ if (!refreshXacts) {
+ this.entries.push(this.formatForDisplay(resp));
+ return;
+ }
- if (idx < this.entries.length) {
- // Update the existing entry
- this.entries[idx] = this.formatForDisplay(resp);
- } else {
- // Adding a new transaction (e.g. from new billing)
- this.entries.push(this.formatForDisplay(resp));
- }
+ entriesFetched.push(resp.id());
+
+ let idx;
+ for (idx = 0; idx < this.entries.length; idx++) {
+ const entry = this.entries[idx];
+ if (entry.xact.id() === resp.id()) { break; }
+ }
+ if (idx < this.entries.length) {
+ // Update the existing entry
+ this.entries[idx] = this.formatForDisplay(resp);
} else {
+ // Adding a new transaction (e.g. from new billing)
this.entries.push(this.formatForDisplay(resp));
}
+
})).toPromise()
.then(_ => {
+ if (!refreshXacts) { return; }
+
+ // Refreshing means some transactions may be removed from the list
+ // Remove them from the local entries array.
+ refreshXacts.forEach(xactId => {
+ if (entriesFetched.includes(xactId)) { return; }
+
+ let idx;
+ for (idx = 0; idx < this.entries.length; idx++) {
+ const entry = this.entries[idx];
+ if (entry.xact.id() === xactId) { break; }
+ }
+
+ this.billGrid.context.rowSelector.deselect(xactId + '');
+ this.entries.splice(idx, 1);
+ });
+ })
+
+ .then(_ => {
this.gridDataSource.requestingData = false;
+ if (refreshXacts) { this.context.refreshPatron(); }
this.billGrid.reload();
});
}
})
.then(paymentIds => this.handlePayReceipt(payments, paymentIds))
.then(_ => this.load(payments.map(p => p[0]))) // load xact IDs
- .then(_ => this.context.refreshPatron())
.catch(msg => console.debug('Payment Canceled:', msg))
.finally(() => this.applyingPayment = false);
}
addBilling() {
this.billingDialog.open().subscribe(data => {
if (data) {
- this.context.refreshPatron();
this.load([data.xactId]);
}
});
}
+
+ voidBillings(rows: BillGridEntry[]) {
+
+ const xactIds = rows.map(r => r.xact.id());
+ const billIds = [];
+ let cents = 0;
+
+ from(xactIds)
+ // Grab the billings
+ .pipe(concatMap(xactId => {
+ return this.pcrud.search('mb', {xact: xactId}, {}, {authoritative: true})
+ .pipe(tap(billing => {
+ if (billing.voided() === 'f') {
+ cents += billing.amount() * 100
+ billIds.push(billing.id());
+ }
+ }));
+ }))
+ // Confirm the void action
+ .pipe(concatMap(_ => {
+ this.voidAmount = cents / 100;
+ return this.voidBillsDialog.open();
+ }))
+ // Do the void
+ .pipe(concatMap(confirmed => {
+ if (!confirmed) { return empty(); }
+
+ return this.net.requestWithParamList(
+ 'open-ils.circ',
+ 'open-ils.circ.money.billing.void',
+ [this.auth.token()].concat(billIds) // positional params
+ );
+ }))
+ // Clean up and reresh data
+ .subscribe(resp => {
+ if (!resp) { return; } // canceled
+
+ const evt = this.evt.parse(resp);
+ if (evt) {
+ console.error(evt);
+ alert(evt);
+ return;
+ }
+
+ this.sessionVoided = (this.sessionVoided * 100 + cents) / 100;
+ this.voidAmount = 0;
+ this.load(xactIds);
+ });
+ }
}