LP#1942220: (follow-up) implement dupe PO name checking
authorGalen Charlton <gmc@equinoxOLI.org>
Tue, 12 Jul 2022 16:43:37 +0000 (16:43 +0000)
committerGalen Charlton <gmc@equinoxOLI.org>
Tue, 12 Jul 2022 16:43:37 +0000 (16:43 +0000)
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Open-ILS/src/eg2/src/app/staff/acq/po/create.component.html
Open-ILS/src/eg2/src/app/staff/acq/po/create.component.ts
Open-ILS/src/eg2/src/app/staff/acq/po/po.service.ts
Open-ILS/src/eg2/src/app/staff/acq/po/summary.component.html
Open-ILS/src/eg2/src/app/staff/acq/po/summary.component.ts

index 1912c21..f65caa5 100644 (file)
   </div>
   <div class="form-group">
     <label for="name-input" i18n>Name (optional)</label>
-    <input id="name-input" class="form-control" type="text" [(ngModel)]="poName"/>
+    <input id="name-input" class="form-control" type="text" [ngModel]="poName"
+      (ngModelChange)="poName = $event; checkDuplicatePoName()"
+    />
+  </div>
+  <div *ngIf="dupeResults.dupeFound" class="alert alert-warning" i18n>
+    This name is already in used by another PO: 
+    <a target="_blank" routerLink="/staff/acq/po/{{dupeResults.dupePoId}}">View PO</a>
   </div>
   <div class="form-group">
     <label for="name-input" i18n>Provider</label>
index d8b1ae3..7655529 100644 (file)
@@ -37,6 +37,10 @@ export class PoCreateComponent implements OnInit {
     provider: ComboboxEntry;
     prepaymentRequired = false;
     createAssets = false;
+    dupeResults = {
+        dupeFound: false,
+        dupePoId: -1
+    };
 
     constructor(
         private router: Router,
@@ -64,6 +68,8 @@ export class PoCreateComponent implements OnInit {
     }
 
     load() {
+        this.dupeResults.dupeFound = false;
+        this.dupeResults.dupePoId = -1;
         if (this.origLiCount > 0) {
             const fleshed_lis: IdlObject[] = [];
             this.liService.getFleshedLineitems(this.lineitems, { fromCache: false }).subscribe(
@@ -84,10 +90,16 @@ export class PoCreateComponent implements OnInit {
 
     orgChange(org: IdlObject) {
         this.orderAgency = org ? org.id() : null;
+        this.checkDuplicatePoName();
     }
 
     canCreate(): boolean {
-        return (Boolean(this.orderAgency) && Boolean(this.provider));
+        return (Boolean(this.orderAgency) && Boolean(this.provider) &&
+                !this.dupeResults.dupeFound);
+    }
+
+    checkDuplicatePoName() {
+        this.poService.checkDuplicatePoName(this.orderAgency, this.poName, this.dupeResults);
     }
 
     create() {
index d8fa0a5..07e7c67 100644 (file)
@@ -2,12 +2,18 @@ import {Injectable, EventEmitter} from '@angular/core';
 import {Observable, from} from 'rxjs';
 import {switchMap, map, tap, merge} from 'rxjs/operators';
 import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.service';
 import {EventService} from '@eg/core/event.service';
 import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
 import {PcrudService} from '@eg/core/pcrud.service';
 import {LineitemService, FleshCacheParams} from '@eg/staff/acq/lineitem/lineitem.service';
 
+export interface PoDupeCheckResults {
+    dupeFound: boolean;
+    dupePoId: number;
+}
+
 @Injectable()
 export class PoService {
 
@@ -18,6 +24,8 @@ export class PoService {
     constructor(
         private evt: EventService,
         private net: NetService,
+        private org: OrgService,
+        private pcrud: PcrudService,
         private auth: AuthService
     ) {}
 
@@ -81,6 +89,24 @@ export class PoService {
             }
         });
     }
+
+    checkDuplicatePoName(orderAgency: number, poName: string, results: PoDupeCheckResults) {
+        if (Boolean(orderAgency) && Boolean(poName)) {
+            this.pcrud.search('acqpo',
+                { name: poName, ordering_agency: this.org.descendants(orderAgency, true) },
+                {}, { idlist: true, atomic: true }
+            ).toPromise().then(ids => {
+                if (ids && ids.length) {
+                    results.dupeFound = true;
+                    results.dupePoId = ids[0];
+                } else {
+                    results.dupeFound = false;
+                }
+            });
+        } else {
+            results.dupeFound = false;
+        }
+    }
 }
 
 
index 04220b8..4881848 100644 (file)
           <div class="flex-4">
             <ng-container *ngIf="editPoName">
               <input id='pl-name-input' type="text" class="form-control"
-                [(ngModel)]="newPoName" (keyup.enter)="toggleNameEdit(true)" 
+                [ngModel]="newPoName" (ngModelChange)="newPoName = $event; checkDuplicatePoName()"
+                (keyup.enter)="toggleNameEdit(true)" 
                 (blur)="toggleNameEdit()"/>
             </ng-container>
             <ng-container *ngIf="!editPoName">
               <a (click)="toggleNameEdit()" href='javascript:;'
                 class='font-weight-bold'>{{po().name()}}</a>
             </ng-container>
+            <div *ngIf="dupeResults.dupeFound" class="alert alert-warning" i18n>
+              This name is already in used by another PO: 
+              <a target="_blank" routerLink="/staff/acq/po/{{dupeResults.dupePoId}}">View PO</a>
+            </div>
           </div> 
         </div>
         <div class="col-lg-8 d-flex">
index 6a4af23..6090d47 100644 (file)
@@ -37,6 +37,10 @@ export class PoSummaryComponent implements OnInit, OnDestroy {
 
     newPoName: string;
     editPoName = false;
+    dupeResults = {
+        dupeFound: false,
+        dupePoId: -1
+    };
     initDone = false;
     ediMessageCount = 0;
     invoiceCount = 0;
@@ -94,6 +98,9 @@ export class PoSummaryComponent implements OnInit, OnDestroy {
     load(): Promise<any> {
         if (!this.poId) { return Promise.resolve(); }
 
+        this.dupeResults.dupeFound = false;
+        this.dupeResults.dupePoId = -1;
+
         return this.poService.getFleshedPo(this.poId, {fromCache: true, toCache: true})
         .then(po => {
 
@@ -122,6 +129,13 @@ export class PoSummaryComponent implements OnInit, OnDestroy {
     // running it again on the blur, which will happen directly after
     // the Enter.
     toggleNameEdit(fromEnter?: boolean) {
+
+        // don't allow change if new name is currently
+        // a duplicate
+        if (this.dupeResults.dupeFound) {
+            return;
+        }
+
         if (fromEnter) {
             this.nameEditEnterToggled = true;
         } else {
@@ -141,11 +155,13 @@ export class PoSummaryComponent implements OnInit, OnDestroy {
                 if (node) { node.select(); }
             });
 
-        } else if (this.newPoName && this.newPoName !== this.po().name()) {
+        } else if (this.newPoName && this.newPoName !== this.po().name() &&
+                   !this.dupeResults.dupeFound) {
 
             const prevName = this.po().name();
             this.po().name(this.newPoName);
             this.newPoName = null;
+            this.dupeResults.dupeFound = false;
 
             this.pcrud.update(this.po()).subscribe(resp => {
                 const evt = this.evt.parse(resp);
@@ -157,6 +173,12 @@ export class PoSummaryComponent implements OnInit, OnDestroy {
         }
     }
 
+    checkDuplicatePoName() {
+        this.poService.checkDuplicatePoName(
+            this.po().ordering_agency(), this.newPoName, this.dupeResults
+        );
+    }
+
     cancelPo() {
         this.cancelDialog.open().subscribe(reason => {
             if (!reason) { return; }