LP#1942220: (follow-up) implement claiming from LI
authorGalen Charlton <gmc@equinoxOLI.org>
Wed, 13 Jul 2022 00:16:10 +0000 (00:16 +0000)
committerGalen Charlton <gmc@equinoxOLI.org>
Wed, 13 Jul 2022 00:16:10 +0000 (00:16 +0000)
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Open-ILS/src/eg2/src/app/staff/acq/lineitem/lineitem-list.component.html
Open-ILS/src/eg2/src/app/staff/acq/lineitem/lineitem-list.component.ts
Open-ILS/src/eg2/src/app/staff/acq/lineitem/lineitem.module.ts
Open-ILS/src/eg2/src/app/staff/acq/lineitem/manage-claims-dialog.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/acq/lineitem/manage-claims-dialog.component.ts [new file with mode: 0644]

index 5926390..8368690 100644 (file)
@@ -5,6 +5,7 @@
 <eg-acq-add-copies-dialog #addCopiesDialog></eg-acq-add-copies-dialog>
 <eg-acq-link-invoice-dialog #linkInvoiceDialog></eg-acq-link-invoice-dialog>
 <eg-acq-claim-policy-dialog #claimPolicyDialog></eg-acq-claim-policy-dialog>
+<eg-acq-manage-claims-dialog #manageClaimsDialog></eg-acq-manage-claims-dialog>
 <eg-acq-export-attributes-dialog #exportAttributesDialog></eg-acq-export-attributes-dialog>
 <eg-lineitem-alert-dialog #confirmAlertsDialog></eg-lineitem-alert-dialog>
 
                     (click)="editHoldings(li)" i18n>Update Barcodes</button>
                   <button ngbDropdownItem [disabled]="!liHasRealCopies(li)"
                     (click)="jumpToHoldings(li)" i18n>Open Holdings View</button>
+                  <button ngbDropdownItem [disabled]="!liHasRealCopies(li)"
+                    (click)="manageClaims(li)" i18n>Claims ({{countClaims(li)}} existing)</button>
                   <a ngbDropdownItem routerLink="lineitem/{{li.id()}}/history"
                     queryParamsHandling="merge" i18n>View History</a>
                 </div>
index d6bab06..135a7e9 100644 (file)
@@ -22,6 +22,7 @@ import {AddCopiesDialogComponent} from './add-copies-dialog.component';
 import {LinkInvoiceDialogComponent} from './link-invoice-dialog.component';
 import {ExportAttributesDialogComponent} from './export-attributes-dialog.component';
 import {ClaimPolicyDialogComponent} from './claim-policy-dialog.component';
+import {ManageClaimsDialogComponent} from './manage-claims-dialog.component';
 import {LineitemAlertDialogComponent} from './lineitem-alert-dialog.component';
 
 const DELETABLE_STATES = [
@@ -125,6 +126,7 @@ export class LineitemListComponent implements OnInit {
     @ViewChild('linkInvoiceDialog') linkInvoiceDialog: LinkInvoiceDialogComponent;
     @ViewChild('exportAttributesDialog') exportAttributesDialog: ExportAttributesDialogComponent;
     @ViewChild('claimPolicyDialog') claimPolicyDialog: ClaimPolicyDialogComponent;
+    @ViewChild('manageClaimsDialog') manageClaimsDialog: ManageClaimsDialogComponent;
     @ViewChild('lineItemsUpdatedString', { static: false }) lineItemsUpdatedString: StringComponent;
     @ViewChild('noActionableLIs', { static: true }) private noActionableLIs: AlertDialogComponent;
     @ViewChild('selectorReadyConfirmDialog', { static: true }) selectorReadyConfirmDialog: ConfirmDialogComponent;
@@ -895,6 +897,22 @@ export class LineitemListComponent implements OnInit {
         window.open('/eg2/staff/catalog/record/' + li.eg_bib_id() + '/holdings', '_blank');
     }
 
+    manageClaims(li: IdlObject) {
+        this.manageClaimsDialog.li = li;
+        this.manageClaimsDialog.open().subscribe(result => {
+            if (result) {
+                delete this.liService.liCache[li.id()];
+                this.loadPageOfLis();
+            }
+        });
+    }
+
+    countClaims(li: IdlObject): number {
+        let total = 0;
+        li.lineitem_details().forEach(lid => total += lid.claims().length);
+        return total;
+    }
+
     receiveSelected() {
         this.markReceived(this.selectedIds());
     }
index 49f6734..8247e35 100644 (file)
@@ -24,6 +24,7 @@ import {AddCopiesDialogComponent} from './add-copies-dialog.component';
 import {LinkInvoiceDialogComponent} from './link-invoice-dialog.component';
 import {ExportAttributesDialogComponent} from './export-attributes-dialog.component';
 import {ClaimPolicyDialogComponent} from './claim-policy-dialog.component';
+import {ManageClaimsDialogComponent} from './manage-claims-dialog.component';
 import {LineitemAlertDialogComponent} from './lineitem-alert-dialog.component';
 import {MarcEditModule} from '@eg/staff/share/marc-edit/marc-edit.module';
 import {AcqCommonModule} from '../acq-common.module';
@@ -46,6 +47,7 @@ import {AcqCommonModule} from '../acq-common.module';
     LinkInvoiceDialogComponent,
     ExportAttributesDialogComponent,
     ClaimPolicyDialogComponent,
+    ManageClaimsDialogComponent,
     LineitemAlertDialogComponent,
     BriefRecordComponent,
     CreateAssetsComponent,
@@ -60,6 +62,7 @@ import {AcqCommonModule} from '../acq-common.module';
     LinkInvoiceDialogComponent,
     ExportAttributesDialogComponent,
     ClaimPolicyDialogComponent,
+    ManageClaimsDialogComponent,
     LineitemAlertDialogComponent
   ],
   imports: [
diff --git a/Open-ILS/src/eg2/src/app/staff/acq/lineitem/manage-claims-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/acq/lineitem/manage-claims-dialog.component.html
new file mode 100644 (file)
index 0000000..4b30bd5
--- /dev/null
@@ -0,0 +1,70 @@
+<ng-template #dialogContent>
+  <form class="form-validated">
+    <div class="modal-header bg-info">
+      <h3 class="modal-title" i18n>Manage Claims</h3>
+      <button type="button" class="close"
+        i18n-aria-label aria-label="Close" (click)="close()">
+        <span aria-hidden="true">&times;</span>
+      </button>
+    </div>
+    <div class="modal-body">
+      <h4 i18n>Claims</h4>
+      <span i18n>Against line item {{liService.getFirstAttributeValue(li, 'title')}} ({{li.id()}})</span>
+      <ul>
+        <li *ngFor="let lid of lidsWithClaims" i18n>
+          {{lid.barcode()}} /
+          <ng-container *ngIf="lid.cancel_reason()">Cancelled ({{lid.cancel_reason().label()}})</ng-container>
+          <ng-container *ngIf="lid.recv_time() && !lid.cancel_reason()">Received {{lid.recv_time() | formatValue:'timestamp'}}</ng-container>
+          <ng-container *ngIf="!lid.recv_time() && !lid.cancel_reason()">Not received</ng-container>
+          <ul>
+            <li *ngFor="let claim of lid.claims()">
+               {{claim.type().code()}} <a href="javascript:;" (click)="printVoucher(lid.id())">Print Voucher</a>
+            </li>
+          </ul>
+        </li>
+      </ul>
+      <hr>
+      <h4 i18n>Initiate New Claims</h4>
+      <div *ngFor="let lid of li.lineitem_details()" i18n>
+        <input type="checkbox" name="lidsToClaim" [(ngModel)]="lid._selected_for_claim">
+        {{lid.barcode()}} /
+        <ng-container *ngIf="lid.cancel_reason()">Cancelled ({{lid.cancel_reason().label()}})</ng-container>
+        <ng-container *ngIf="lid.recv_time() && !lid.cancel_reason()">Received {{lid.recv_time() | formatValue:'timestamp'}}</ng-container>
+        <ng-container *ngIf="!lid.recv_time() && !lid.cancel_reason()">Not received</ng-container>
+      </div>
+      <ng-container *ngIf="claimEventTypes.length > 0">
+        <label for="selectClaimEventTypes" i18n>Select Claim Action(s)</label>
+        <select class="form-control"  multiple="true" [size]="claimEventTypes.length"
+          [(ngModel)]="selectedClaimEventTypes" [ngModelOptions]="{standalone: true}" id="selectClaimEventTypes">
+          <option *ngFor="let clet of claimEventTypes" [value]="clet.id()" i18n>
+           {{clet.code()}} ({{clet.org_unit().shortname()}}) <i>{{clet.description()}}</i>
+           <ng-container *ngIf="clet.library_initiated()"> [Library initiated]</ng-container>
+          </option>
+        </select>
+      </ng-container>
+      <label for="claimType" i18n>Claim Type</label>
+      <eg-combobox domId="claimType" name="claimType" 
+        [asyncSupportsEmptyTermClick]="true"
+        idlClass="acqclt" [(ngModel)]="claimType" [ngModelOptions]="{standalone: true}"></eg-combobox>
+      <label for="note" i18n>Claim Note</label>
+      <input class="form-control" type="text" i18n-placeholder placeholder="Note" [(ngModel)]="note"
+        [ngModelOptions]="{standalone: true}" id="note">
+    </div>
+    
+    <div class="modal-footer">
+      <button type="button" class="btn btn-success"
+        [disabled]="!canPerformClaim()"
+        (click)="claimItems()" i18n>Claim Selected</button>
+      <button type="button" class="btn btn-warning"
+        (click)="close()" i18n>Exit Dialog</button>
+    </div>
+  </form>
+</ng-template>
+
+<ng-template #printTemplate let-context>
+  <div>
+    <h1>Claim Voucher</h1>
+    <hr>
+    <span [innerHtml]="context.voucher"></span>
+  </div>
+</ng-template>
diff --git a/Open-ILS/src/eg2/src/app/staff/acq/lineitem/manage-claims-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/acq/lineitem/manage-claims-dialog.component.ts
new file mode 100644 (file)
index 0000000..0cd56d5
--- /dev/null
@@ -0,0 +1,113 @@
+import {Component, Input, ViewChild, TemplateRef} from '@angular/core';
+import {Observable} from 'rxjs';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {AuthService} from '@eg/core/auth.service';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+import {LineitemService} from '../lineitem/lineitem.service';
+import {PrintService} from '@eg/share/print/print.service';
+
+@Component({
+  selector: 'eg-acq-manage-claims-dialog',
+  templateUrl: './manage-claims-dialog.component.html'
+})
+
+export class ManageClaimsDialogComponent extends DialogComponent {
+    @Input() li: IdlObject;
+
+    @ViewChild('printTemplate', { static: true }) private printTemplate: TemplateRef<any>;
+
+    lidsWithClaims: IdlObject[] = [];
+
+    note = '';
+    claimEventTypes: number[] = [];
+    selectedClaimEventTypes: number[] = [];
+    claimType: ComboboxEntry;
+
+    constructor(
+        private modal: NgbModal,
+        private net: NetService,
+        private auth: AuthService,
+        private pcrud: PcrudService,
+        private printer: PrintService,
+        private liService: LineitemService
+    ) { super(modal); }
+
+    open(args?: NgbModalOptions): Observable<any> {
+        if (!args) {
+            args = {};
+        }
+
+        this.lidsWithClaims = this.getLidsWithClaims();
+        this.note = '';
+        this.claimEventTypes = [];
+        this.selectedClaimEventTypes = [];
+        this.getClaimEventTypes();
+
+        return super.open(args);
+    }
+
+    getLidsWithClaims(): IdlObject[] {
+        return this.li.lineitem_details().filter(x => x.claims().length > 0);
+    }
+
+    getClaimEventTypes() {
+        this.pcrud.retrieveAll('acqclet',
+            { 'order_by': {'acqclet': 'code'}, flesh: 1, flesh_fields: {acqclet: ['org_unit']} },
+            {}
+        ).subscribe(t => this.claimEventTypes.push(t));
+    }
+
+    canPerformClaim(): boolean {
+        if (!this.claimType) { return false; }
+        if (!this.claimType.id) { return false; }
+        const lidsToClaim = this.li.lineitem_details().filter(x => x._selected_for_claim);
+        if (lidsToClaim.length < 1) { return false; }
+        return true;
+    }
+
+    claimItems() {
+        if (!this.canPerformClaim()) { return; }
+        const lidsToClaim = this.li.lineitem_details()
+                                .filter(x => x._selected_for_claim)
+                                .map(x => x.id());
+        this.net.request(
+            'open-ils.acq',
+            'open-ils.acq.claim.lineitem_detail.atomic',
+            this.auth.token(),
+            lidsToClaim, null,
+            this.claimType.id,
+            this.note,
+            null,
+            this.selectedClaimEventTypes
+        ).subscribe(result => {
+            if (result && result.length) {
+                const voucher = result.map(x => x.template_output().data()).join('<hr>');
+                this.printer.print({
+                    template: this.printTemplate,
+                    contextData: { voucher: voucher },
+                    printContext: 'default'
+                });
+            }
+            this.close(true);
+        });
+    }
+
+    printVoucher(lidId: number) {
+        this.net.request(
+            'open-ils.acq',
+            'open-ils.acq.claim.voucher.by_lineitem_detail',
+            this.auth.token(), lidId
+        ).subscribe(result => {
+            if (!result) { return; }
+            this.printer.print({
+                template: this.printTemplate,
+                contextData: { voucher: result.template_output().data() },
+                printContext: 'default'
+            });
+        });
+    }
+}