From: Galen Charlton Date: Tue, 14 Dec 2021 02:01:26 +0000 (-0500) Subject: LP#1942220: add 'Disencumber' action for PO items X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=f4552455a74cadf7fced1c2f89f273ef56bde4a2;p=working%2FEvergreen.git LP#1942220: add 'Disencumber' action for PO items This adds a button to, upon user confirmation, change the fund debit for a PO item to a zero-value encumbrance. This button is available for a charge only if: - the PO item is attached to a fund debit that has no invoice entries or items attached - the PO is activated but not cancelled - the fund debit is not an expenditure - the debit amount is not already zero The purpose of this button is to clean up encumbrances for miscellaneous charges on invoiced POs that have not been linked to invoice items. Signed-off-by: Galen Charlton --- diff --git a/Open-ILS/src/eg2/src/app/staff/acq/po/charges.component.html b/Open-ILS/src/eg2/src/app/staff/acq/po/charges.component.html index 4e64164048..6e4d85011e 100644 --- a/Open-ILS/src/eg2/src/app/staff/acq/po/charges.component.html +++ b/Open-ILS/src/eg2/src/app/staff/acq/po/charges.component.html @@ -3,6 +3,8 @@ + +
Charge Type
@@ -32,6 +34,12 @@ [idlQuerySort]="{acqf: 'year DESC, code'}" [idlQueryAnd]="{active: 't'}"> + +
+ Fund actually debited is + +
{{charge.title()}} @@ -56,12 +64,23 @@ + +
+ + Amount encumbered is {{charge.fund_debit().amount() | currency}} + + + Amount expended is {{charge.fund_debit().amount() | currency}} + +
+
diff --git a/Open-ILS/src/eg2/src/app/staff/acq/po/charges.component.ts b/Open-ILS/src/eg2/src/app/staff/acq/po/charges.component.ts index 34a4be362d..705be70ea4 100644 --- a/Open-ILS/src/eg2/src/app/staff/acq/po/charges.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/acq/po/charges.component.ts @@ -1,10 +1,14 @@ -import {Component, OnInit, OnDestroy, Input} from '@angular/core'; +import {Component, OnInit, OnDestroy, Input, ViewChild} from '@angular/core'; import {Subscription} from 'rxjs'; import {Router, ActivatedRoute, ParamMap} from '@angular/router'; import {IdlService, IdlObject} from '@eg/core/idl.service'; +import {AuthService} from '@eg/core/auth.service'; +import {NetService} from '@eg/core/net.service'; +import {EventService} from '@eg/core/event.service'; import {PcrudService} from '@eg/core/pcrud.service'; import {ComboboxEntry} from '@eg/share/combobox/combobox.component'; import {PoService} from './po.service'; +import {DisencumberChargeDialogComponent} from './disencumber-charge-dialog.component'; @Component({ templateUrl: 'charges.component.html', @@ -17,8 +21,13 @@ export class PoChargesComponent implements OnInit, OnDestroy { autoId = -1; poSubscription: Subscription; + @ViewChild('disencumberChargeDialog') disencumberChargeDialog: DisencumberChargeDialogComponent; + constructor( private idl: IdlService, + private net: NetService, + private evt: EventService, + private auth: AuthService, private pcrud: PcrudService, public poService: PoService ) {} @@ -77,6 +86,49 @@ export class PoChargesComponent implements OnInit, OnDestroy { } } + canDisencumber(charge: IdlObject): boolean { + if (!this.po() || !this.po().order_date() || this.po().state() === 'cancelled') { + return false; // order must be loaded, activated, and not cancelled + } + if (!charge.fund_debit()) { + return false; // that which is not encumbered cannot be disencumbered + } + + const debit = charge.fund_debit(); + if (debit.encumbrance() === 'f') { + return false; // that which is expended cannot be disencumbered + } + if (debit.invoice_entry()) { + return false; // we shouldn't actually be a po_item that is + // linked to an invoice_entry, but if we are, + // do NOT touch + } + if (debit.invoice_items() && debit.invoice_items().length) { + return false; // we're linked to an invoice item, so the disposition of the + // invoice entry should govern things + } + if (Number(debit.amount()) === 0) { + return false; // we're already at zero + } + return true; // we're likely OK to disencumber + } + + disencumberCharge(charge: IdlObject) { + this.disencumberChargeDialog.charge = charge; + this.disencumberChargeDialog.open().subscribe(doIt => { + if (!doIt) { return; } + + return this.net.request( + 'open-ils.acq', + 'open-ils.acq.po_item.disencumber', + this.auth.token(), charge.id() + ).toPromise().then(res => { + const evt = this.evt.parse(res); + if (evt) { return Promise.reject(evt + ''); } + }).then(_ => this.poService.refreshOrderSummary(true)); + }); + } + removeCharge(charge: IdlObject) { this.po().po_items( // remove local copy this.po().po_items().filter(item => item.id() !== charge.id()) diff --git a/Open-ILS/src/eg2/src/app/staff/acq/po/disencumber-charge-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/acq/po/disencumber-charge-dialog.component.html new file mode 100644 index 0000000000..03a6cc481f --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/acq/po/disencumber-charge-dialog.component.html @@ -0,0 +1,49 @@ + +
+ + + +
+
+ diff --git a/Open-ILS/src/eg2/src/app/staff/acq/po/disencumber-charge-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/acq/po/disencumber-charge-dialog.component.ts new file mode 100644 index 0000000000..4bd442c4ab --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/acq/po/disencumber-charge-dialog.component.ts @@ -0,0 +1,17 @@ +import {Component, Input, ViewChild, TemplateRef, OnInit} from '@angular/core'; +import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; +import {DialogComponent} from '@eg/share/dialog/dialog.component'; +import {IdlService, IdlObject} from '@eg/core/idl.service'; +import {ComboboxEntry} from '@eg/share/combobox/combobox.component'; + +@Component({ + selector: 'eg-acq-disencumber-charge-dialog', + templateUrl: './disencumber-charge-dialog.component.html' +}) + +export class DisencumberChargeDialogComponent extends DialogComponent { + @Input() charge: IdlObject; + constructor(private modal: NgbModal) { super(modal); } +} + + diff --git a/Open-ILS/src/eg2/src/app/staff/acq/po/po.module.ts b/Open-ILS/src/eg2/src/app/staff/acq/po/po.module.ts index 55b0663920..11345e4d38 100644 --- a/Open-ILS/src/eg2/src/app/staff/acq/po/po.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/acq/po/po.module.ts @@ -15,6 +15,7 @@ import {PoNotesComponent} from './notes.component'; import {PoCreateComponent} from './create.component'; import {PoChargesComponent} from './charges.component'; import {PicklistUploadService} from '../picklist/upload.service'; +import {DisencumberChargeDialogComponent} from './disencumber-charge-dialog.component'; @NgModule({ declarations: [ @@ -25,7 +26,8 @@ import {PicklistUploadService} from '../picklist/upload.service'; PoNotesComponent, PoCreateComponent, PoChargesComponent, - PrintComponent + PrintComponent, + DisencumberChargeDialogComponent ], imports: [ StaffCommonModule, diff --git a/Open-ILS/src/eg2/src/app/staff/acq/po/po.service.ts b/Open-ILS/src/eg2/src/app/staff/acq/po/po.service.ts index 25d1924c6b..d8fa0a534c 100644 --- a/Open-ILS/src/eg2/src/app/staff/acq/po/po.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/acq/po/po.service.ts @@ -34,6 +34,7 @@ export class PoService { flesh_provider: true, flesh_notes: true, flesh_po_items: true, + flesh_po_items_further: true, flesh_price_summary: true, flesh_lineitem_count: true }, params.fleshMore || {}); @@ -56,18 +57,28 @@ export class PoService { // Fetch the PO again (with less fleshing) and update the // order summary totals our main fully-fleshed PO. - refreshOrderSummary(): Promise { + refreshOrderSummary(update_po_items = false): Promise { + const flesh = Object.assign({ + flesh_price_summary: true + }); + if (update_po_items) { + flesh['flesh_po_items'] = true; + flesh['flesh_po_items_further'] = true; + } return this.net.request('open-ils.acq', 'open-ils.acq.purchase_order.retrieve.authoritative', this.auth.token(), this.currentPo.id(), - {flesh_price_summary: true} + flesh ).toPromise().then(po => { this.currentPo.amount_encumbered(po.amount_encumbered()); this.currentPo.amount_spent(po.amount_spent()); this.currentPo.amount_estimated(po.amount_estimated()); + if (update_po_items) { + this.currentPo.po_items(po.po_items()); + } }); } }