Project

General

Profile

1
import { Right } from './../../../../shared/models/right.interface';
2
import { environment } from './../../../../../environments/environment';
3
import { USER_RIGHTS } from './../../../../shared/enums/USER_RIGHTS.enum';
4
import { AuthService } from 'src/app/shared/services/auth.service';
5
import { CreateVerificationRuleFormValue } from './create-verification-rule-form-value.interface';
6
import { JournalVerificationsService } from './../../../../shared/services/administration/journal-verifications.service';
7
import { CapturingVerificationsService } from './../../../../shared/services/administration/capturing-verifications.service';
8
import { DocumentSubclassificationsService } from 'src/app/shared/services/administration/document-subclassifications.service';
9
import { IpowerClientsService } from 'src/app/shared/services/administration/ipower-clients.service';
10
import { CategoriesService } from 'src/app/shared/services/administration/categories.service';
11
import { DocumentClassificationsService } from 'src/app/shared/services/administration/document-classifications.service';
12
import { FormBuilder, Validators } from '@angular/forms';
13
import { DocumentSubclassification } from 'src/app/shared/models/document-subclassification.interface';
14
import { Component, Input, OnInit } from '@angular/core';
15
import { CapturingVerification } from '../../../../shared/models/capturing-verification.interface';
16
import { Category } from '../../../../shared/models/category.interface';
17
import { IPowerClient } from '../../../../shared/models/ipower-client.interface';
18
import { JournalVerification } from '../../../../shared/models/journal-verification.interface';
19
import { VerificationRule } from './../../../../shared/models/verification-rule.interface';
20
import { DocumentClassification } from '../../../../shared/models/document-classification.interface';
21
import { ErrorHandlingService } from 'src/app/shared/services/error-handling/error-handling.service';
22
import { Observable } from 'rxjs';
23

    
24
@Component({
25
  selector: 'app-verification-rule-form',
26
  templateUrl: './verification-rule-form.component.html',
27
  styleUrls: ['./verification-rule-form.component.scss']
28
})
29
export class VerificationRuleFormComponent implements OnInit {
30

    
31
  /*
32
    * Inputs
33
    */
34
  @Input() dialogLayout: boolean = false;   // Controls .scss classes to allow for better dispay in a dialog.
35
  @Input() requiredFields: string[] | boolean = false;  // True/False indicates that all/none are required. String[] specifies the form controls required.
36
  @Input() displayValidationMessagesEvenIfPristine: boolean = false;  // Serves for manually treating the controls as dirty.
37
  @Input() excludedFormControls: string[];  // Specifies the form controls to be removed and NOT displayed.
38

    
39
  /*
40
   * Reactive Form
41
   */
42
  verificationRuleForm = this.fb.group({
43
    documentClassification: [null],
44
    ipowerName: [null],
45
    ipowerCode: [null],
46
    categoryName: [null],
47
    categoryCode: [null],
48
    subCategoryCode: [null],   // This actually represents the complete DocumentSubclassification object.
49
    confidenceLevelMinThreshold: [null],
50
    capturingVerification: [null],
51
    journalVerification: [null],
52
    alteryxRoutineId: [null],
53
    verificationRuleStatus: [true],
54
    template: [null]
55
  });
56
  initiallySetFormValue: VerificationRule;
57
  editMode: boolean = false;
58

    
59
  /*
60
   * Other Variables
61
   */
62
  documentClassificationsList: DocumentClassification[];
63

    
64
  categoryNameSuggestions: string[];
65
  categoryCodeSuggestions: string[];
66
  categorySuggestions: Category[];
67
  selectedCategory: Category;
68

    
69
  iPowerClientNameSuggestions: string[];
70
  iPowerClientCodeSuggestions: string[];
71
  iPowerClientSuggestions: IPowerClient[];
72
  selectedIPowerClient: IPowerClient;
73

    
74
  capturingVerificationsList: CapturingVerification[];
75
  journalVerificationsList: JournalVerification[];
76

    
77
  documentSubclassificationsList: DocumentSubclassification[];
78
  availableDocumentSubclassifications: DocumentSubclassification[];
79
  subCategoryCodeDisabled: boolean = true;
80

    
81
  /*
82
   * Constructor & Initialisers
83
   */
84
  constructor(
85
    private fb: FormBuilder,
86
    private documentClassificationsService: DocumentClassificationsService,
87
    private categoriesService: CategoriesService,
88
    private iPowerClientsService: IpowerClientsService,
89
    private documentSubclassificationsService: DocumentSubclassificationsService,
90
    private capturingVerificationsService: CapturingVerificationsService,
91
    private journalVerificationsService: JournalVerificationsService,
92
    private authService: AuthService,
93
    private errorHandlingService: ErrorHandlingService
94

    
95
  ) { }
96

    
97
  ngOnInit(): void {
98
    this.initData();
99
    this.initValidators()
100
    this.excludeFields();
101
  }
102

    
103
  initData() {
104
    this.documentClassificationsService.getAll().subscribe(
105
      value => this.documentClassificationsList = value,
106
      err => this.errorHandlingService.showHttpResponseError(err)
107
    );
108

    
109
    // This is NOT the list of Subclassifications used for the dropdown.
110
    this.documentSubclassificationsService.getAll().subscribe(
111
      value => this.documentSubclassificationsList = value,
112
      err => this.errorHandlingService.showHttpResponseError(err)
113
    );
114

    
115
    this.journalVerificationsService.getJournalVerifications().subscribe(
116
      values => this.journalVerificationsList = values,
117
      err => this.errorHandlingService.showHttpResponseError(err)
118
    );
119

    
120
    this.capturingVerificationsService.getCapturingVerifications().subscribe(
121
      values => this.capturingVerificationsList = values,
122
      err => this.errorHandlingService.showHttpResponseError(err)
123
    );
124
  }
125

    
126
  // TODO: Have this mechanism offer the use of custom validators too.
127
  initValidators() {
128

    
129
    if (!this.requiredFields) { return; }
130

    
131
    // In a true/false case.
132
    if (typeof this.requiredFields == 'boolean') {
133
      // If true, enable the required validator for all controls.
134
      if (this.requiredFields) {
135
        Object.keys(this.verificationRuleForm.controls).forEach(key => this.verificationRuleForm.controls[key].setValidators(Validators.required));
136
      }
137

    
138
      // If false, do nothing.
139
      return;
140
    }
141

    
142
    // If it was a string array, enable the validators for all provided field-names.
143
    (<string[]>this.requiredFields).forEach(field => this.verificationRuleForm.controls[field].setValidators(Validators.required))
144
  }
145

    
146
  excludeFields() {
147
    if (!this.excludedFormControls) { return; }
148
    this.excludedFormControls.forEach(field => this.verificationRuleForm.removeControl(field));
149
  }
150

    
151
  /*
152
   * Auto-suggest & Auto-complete Category
153
   */
154
  autosuggestCategoryName(event) {
155

    
156
    if (!event.query || event.query.length < 3) {
157
      this.categoryNameSuggestions = [];
158
      return;
159
    }
160

    
161
    let classId = this.verificationRuleForm.get('documentClassification').value ? this.verificationRuleForm.get('documentClassification').value.classificationId : null;
162
    this.categoriesService.autosuggestCategoryName(event.query, classId).subscribe(
163
      (values: Category[]) => {
164
        let temp: string[] = [];
165
        this.categorySuggestions = values;
166
        values.map(val => temp.push(val.categoryName));
167
        this.categoryNameSuggestions = temp
168
      },
169
      err => this.errorHandlingService.showHttpResponseError(err)
170
    );
171
  }
172

    
173
  autosuggestCategoryCode(event) {
174

    
175
    if (event.query.length < 3) {
176
      this.categoryCodeSuggestions = [];
177
      return;
178
    }
179

    
180
    let classId = this.verificationRuleForm.get('documentClassification').value ? this.verificationRuleForm.get('documentClassification').value.classificationId : null;
181
    this.categoriesService.autosuggestCategoryCode(event.query, classId).subscribe(
182
      (values: Category[]) => {
183
        let temp: string[] = [];
184
        this.categorySuggestions = values;
185
        values.map(val => temp.push(val.categoryCode));
186
        this.categoryCodeSuggestions = temp
187
      },
188
      err => this.errorHandlingService.showHttpResponseError(err)
189
    );
190
  }
191

    
192
  categoryNameSelected(name: string) {
193
    this.selectedCategory = this.categorySuggestions.find(cat => cat.categoryName == name);
194
    this.verificationRuleForm.get('categoryCode').patchValue(this.selectedCategory.categoryCode);
195
    this.verificationRuleForm.updateValueAndValidity();
196
  }
197

    
198
  categoryCodeSelected(code: string) {
199
    this.selectedCategory = this.categorySuggestions.find(cat => cat.categoryCode == code);
200
    this.verificationRuleForm.get('categoryName').patchValue(this.selectedCategory.categoryName);
201
    this.verificationRuleForm.updateValueAndValidity();
202
  }
203

    
204
  // /*
205
  //  * Auto-suggest & Auto-complete IPower Client
206
  //  */
207
  // /*
208
  //  * Rights-check Warning:
209
  //  * If we are NOT in editMode (and are thus on "addMode"), we should only suggest clients for which we actually CAN add a new rule.
210
  //  * Note:
211
  //  * I know that if on editMode the client-input is disabled and thus this check is redundant,
212
  //  * but I think it is better to work with as an abstract perspective as possible, not entailing business logic that may either be unrelated or prone to changes.
213
  //  */
214
  // autosuggestIPowerClientName(event) {
215

    
216
  //   if (!event.query || event.query.length < 3) {
217
  //     this.iPowerClientNameSuggestions = [];
218
  //     return;
219
  //   }
220

    
221
  //   // If the user has the right to preview all rules (B01), we use the endpoint that returns all iPowerClients,
222
  //   // otherwise, the one that checks for the user's User_Access too. Whether the user can see ANY rule has already been handled by the 'search' button.
223
  //   let endpointToSubscribeTo: Observable<IPowerClient[]> = USER_RIGHTS.B01.isGrantedToUser(
224
  //     this.authService.userRights.find(rbc => rbc.client.id == environment.globalRightsClientID)?.rights
225
  //   )
226
  //     ? this.iPowerClientsService.getClientsByNameOnly(event.query)
227
  //     : this.iPowerClientsService.getClientsByNameDistinct(event.query);
228

    
229
  //   endpointToSubscribeTo.subscribe(
230
  //     (values: IPowerClient[]) => {
231

    
232
  //       // ***See comment at method level***
233
  //       if (!this.editMode) {
234
  //         values = values.filter(client => USER_RIGHTS.B03.isGrantedToUser(
235
  //           this.authService.userRights.find(rdc => rdc.client.id == client.id)?.rights
236
  //         ));
237
  //       }
238

    
239
  //       let temp: string[] = [];
240
  //       this.iPowerClientSuggestions = values;
241
  //       values.map(val => temp.push(val.name));
242
  //       this.iPowerClientNameSuggestions = temp
243
  //     },
244
  //     err => this.errorHandlingService.showHttpResponseError(err)
245
  //   );
246
  // }
247

    
248
  // /*
249
  //  * Rights-check Warning:
250
  //  * If we are NOT in editMode (and are thus on "addMode"), we should only suggest clients for which we actually CAN add a new rule.
251
  //  * Note:
252
  //  * I know that if on editMode the client-input is disabled and thus this check is redundant,
253
  //  * but I think it is better to work with as an abstract perspective as possible, not entailing business logic that may either be unrelated or prone to changes.
254
  //  */
255
  // autosuggestIPowerClientCode(event) {
256

    
257
  //   if (event.query.length < 3) {
258
  //     this.iPowerClientCodeSuggestions = [];
259
  //     return;
260
  //   }
261

    
262
  //   // If the user has the right to preview all rules (B01), we use the endpoint that returns all iPowerClients,
263
  //   // otherwise, the one that checks for the user's User_Access too. Whether the user can see ANY rule has already been handled by the 'search' button.
264
  //   let endpointToSubscribeTo: Observable<IPowerClient[]> = USER_RIGHTS.B01.isGrantedToUser(
265
  //     this.authService.userRights.find(rbc => rbc.client.id == environment.globalRightsClientID)?.rights
266
  //   )
267
  //     ? this.iPowerClientsService.getClientsByCodeOnly(event.query): null;
268

    
269
  //   endpointToSubscribeTo.subscribe(
270
  //     (values: IPowerClient[]) => {
271

    
272
  //       // ***See comment at method level***
273
  //       if (!this.editMode) {
274
  //         values = values;
275
  //       }
276

    
277
  //       let temp: string[] = [];
278
  //       this.iPowerClientSuggestions = values;
279
  //       values.map(val => temp.push(val.clientCode));
280
  //       this.iPowerClientCodeSuggestions = temp
281
  //     },
282
  //     err => this.errorHandlingService.showHttpResponseError(err)
283
  //   );
284
  // }
285

    
286

    
287
   /*
288
  * Auto-suggest & Auto-complete IPower Client
289
  */
290
 autosuggestIPowerClientName(event): void {
291

    
292
  if (!event.query || event.query.length < 3) {
293
    this.iPowerClientNameSuggestions = [];
294
    return;
295
  }
296

    
297
  this.iPowerClientsService.getClientsByNameOnly(event.query).subscribe(
298
    (values: IPowerClient[]) => {
299
      this.iPowerClientSuggestions = values;
300

    
301
      const temp: string[] = [];
302
      values.map(val => temp.push(val.name));
303
      this.iPowerClientNameSuggestions = temp;
304
    },
305
    err => this.errorHandlingService.showHttpResponseError(err)
306
  );
307
}
308

    
309
  autosuggestIPowerClientCode(event): void {
310

    
311
    if (event.query.length < 3) {
312
      this.iPowerClientCodeSuggestions = [];
313
      return;
314
    }
315

    
316
    this.iPowerClientsService.getClientsByCodeOnly(event.query).subscribe(
317
      (values: IPowerClient[]) => {
318
        this.iPowerClientSuggestions = values;
319

    
320
        const temp: string[] = [];
321
        values.map(val => temp.push(val.clientCode));
322
        this.iPowerClientCodeSuggestions = temp;
323
      },
324
      err => this.errorHandlingService.showHttpResponseError(err)
325
    );
326
  }
327

    
328
  iPowerClientNameSelected(name: string) {
329
    this.selectedIPowerClient = this.iPowerClientSuggestions.find(client => client.name == name);
330
    this.verificationRuleForm.get('ipowerCode').patchValue(this.selectedIPowerClient.clientCode);
331
    this.verificationRuleForm.updateValueAndValidity();
332
  }
333

    
334
  iPowerClientCodeSelected(code: string) {
335
    this.selectedIPowerClient = this.iPowerClientSuggestions.find(client => client.clientCode == code);
336
    this.verificationRuleForm.get('ipowerName').patchValue(this.selectedIPowerClient.name);
337
    this.verificationRuleForm.updateValueAndValidity();
338
  }
339

    
340
  /*
341
   * Other Methods
342
   */
343
  documentClassificationSelected(selection) {
344
    this.availableDocumentSubclassifications = this.documentSubclassificationsList.filter(element => element.documentClassification.classificationId == selection.value.classificationId);
345
    this.verificationRuleForm.get('subCategoryCode').setValue(this.availableDocumentSubclassifications[0]);
346
    this.subCategoryCodeDisabled = false;
347
  }
348

    
349
  /*
350
   * Utility Methods
351
   */
352
  initiateEditMode() {
353
    // let controlsToDisableInEditMode = ['documentClassification', 'ipowerName', 'ipowerCode', 'categoryName', 'categoryCode', 'subCategoryCode']
354
    // controlsToDisableInEditMode.forEach(ctrl => this.verificationRuleForm.get(ctrl).disable());
355
  }
356

    
357

    
358
  // Auto-suggest inputs  - We have to ensure our reactive form holds values that represent the selectedCategory, otherwise truncate it.
359
  syncSelectedCategory() {
360

    
361
    // Ιf our form has no value, truncate the selectedCategory either way.
362
    if (!this.verificationRuleForm.get('categoryName').value && !this.verificationRuleForm.get('categoryCode').value) {
363
      this.selectedCategory = null;
364
      return;
365
    }
366

    
367
    // If both or either of our form's values match the selectedCategory's and the other one doesn't have a value, all is good.
368
    // Just sync the values in case one is missing. Otherwise truncate the selectedCategory.
369
    if (
370
      this.verificationRuleForm.get('categoryName').value == this.selectedCategory.categoryName
371
      || this.verificationRuleForm.get('categoryCode').value == this.selectedCategory.categoryCode
372
    ) {
373
      this.selectedCategory.categoryName = this.verificationRuleForm.get('categoryName').value;
374
      this.selectedCategory.categoryCode = this.verificationRuleForm.get('categoryCode').value;
375
    }
376

    
377
    // If both our values were different from the selectedCategory's, truncate it. This is an extremely abnormal scenario.
378
    else {
379
      console.error('WARNING - syncSelectedCategory()', 'Both of our form\'s values were different from the selectedCategory\'s.');
380
      this.selectedCategory = null;
381
      this.verificationRuleForm.get('categoryName').setValue('');
382
      this.verificationRuleForm.get('categoryCode').setValue('');
383
      this.verificationRuleForm.updateValueAndValidity();
384
    }
385
  }
386

    
387
  // Auto-suggest inputs  - We have to ensure our reactive form holds values that represent the selectedIPowerClient, otherwise truncate it.
388
  syncSelectedIPowerClient() {
389

    
390
    // Ιf our form has no value, truncate the selectedIPowerClient either way.
391
    if (!this.verificationRuleForm.get('ipowerName').value && !this.verificationRuleForm.get('ipowerCode').value) {
392
      this.selectedIPowerClient = null;
393
      return;
394
    }
395

    
396
    // If both or either of our form's values match the selectedIPowerClient's and the other one doesn't have a value, all is good.
397
    // Just sync the values in case one is missing. Otherwise truncate the selectedIPowerClient.
398
    if (
399
      this.verificationRuleForm.get('ipowerName').value == this.selectedIPowerClient.name
400
      || this.verificationRuleForm.get('ipowerCode').value == this.selectedIPowerClient.clientCode
401
    ) {
402
      this.selectedIPowerClient.name = this.verificationRuleForm.get('ipowerName').value;
403
      this.selectedIPowerClient.clientCode = this.verificationRuleForm.get('ipowerCode').value;
404
    }
405

    
406
    // If both our values were different from the selectedIPowerClient's, truncate it. This is an extremely abnormal scenario.
407
    else {
408
      console.error('WARNING - syncSelectedIPowerClient()', 'Both of our form\'s values were different from the selectedIPowerClient\'s.');
409
      this.selectedIPowerClient = null;
410
      this.verificationRuleForm.get('ipowerName').setValue('');
411
      this.verificationRuleForm.get('ipowerCode').setValue('');
412
      this.verificationRuleForm.updateValueAndValidity();
413
    }
414
  }
415

    
416
  /*
417
   * Rights-check Methods
418
   */
419
  canEditRuleStatus(): boolean {
420
    if (this.initiallySetFormValue) {
421
      return this.authService.userHasRightForClient(USER_RIGHTS.B06, environment.globalRightsClientID);
422
    }
423
    return false;
424
  }
425

    
426
  // canEditConfidenceLevel(): boolean {
427
  //   if (this.initiallySetFormValue) {
428
  //     return this.authService.userHasRightForClient(USER_RIGHTS.B07, this.initiallySetFormValue.client.id);
429
  //   }
430
  //   return false;
431
  // }
432

    
433
  /*
434
   * API methods
435
   */
436
  public resetForm(): void {
437
    this.verificationRuleForm.reset();
438
    this.selectedCategory = null;
439
    this.selectedIPowerClient = null;
440
    this.editMode = null;
441
  }
442

    
443
  public formValue(): CreateVerificationRuleFormValue {
444

    
445
    // We keep track of those two using object-variables separate from our ReactiveForm.
446
    // Thus, we now have to make sure the ReactiveForm really has values that match those objects, before we forward them.
447
    // Also, keep in mind that our auto-suggest inputs limit the user in choosing one of their values.
448
    this.syncSelectedCategory();
449
    this.syncSelectedIPowerClient();
450

    
451
    let formValue: CreateVerificationRuleFormValue = {
452
      id: this.initiallySetFormValue ? this.initiallySetFormValue.id : null,
453
      confidenceLevelMinThreshold: this.verificationRuleForm.get('confidenceLevelMinThreshold').value,
454
      clientId: this.selectedIPowerClient ? this.selectedIPowerClient.id : null,
455
      categoryId: this.selectedCategory ? this.selectedCategory.id : null,
456
      // The backend only requires the subcategory's name, and not the whole object. Don't ask me.
457
      subCategoryCode: this.verificationRuleForm.get('subCategoryCode').value ? this.verificationRuleForm.get('subCategoryCode').value['subclassificationName'] : '',
458
      capturingVerification: this.verificationRuleForm.get('capturingVerification').value,
459
      journalVerification: this.verificationRuleForm.get('journalVerification').value,
460
      alteryxRoutineId: this.verificationRuleForm.get('alteryxRoutineId').value,
461
      template: this.verificationRuleForm.get('template').value,
462

    
463
      // Convert 'verificationRuleStatus' from boolean to string.
464
      verificationRuleStatus: this.verificationRuleForm.get('verificationRuleStatus').value ? 'Enabled' : 'Disabled'
465
    };
466

    
467
    return formValue;
468
  }
469

    
470
  public setValue(value: VerificationRule): void {
471
    if (!value) {
472
      return;
473
    }
474

    
475
    this.editMode = true;
476
    this.initiateEditMode();
477

    
478
    this.initiallySetFormValue = value
479

    
480
    // If a documentClassification is already selected, enable the subclassification dropdown.
481
    this.subCategoryCodeDisabled = !value.docClassificationCategory;
482

    
483
    this.verificationRuleForm.get('documentClassification').setValue(value.docClassificationCategory);
484
    // Having set the documentClassification -and provided there was one- we must also set the availableDocumentSubclassifications.
485
    this.availableDocumentSubclassifications = this.documentSubclassificationsList.filter(element =>
486
      element.documentClassification.classificationId == value.docClassificationCategory.classificationId
487
    );
488

    
489
    this.verificationRuleForm.get('ipowerName').setValue(value.client.name);
490
    this.verificationRuleForm.get('ipowerCode').setValue(value.client.clientCode);
491
    this.selectedIPowerClient = value.client;
492

    
493
    this.verificationRuleForm.get('categoryName').setValue(value.template.category.categoryName);
494
    this.verificationRuleForm.get('categoryCode').setValue(value.template.category.categoryCode);
495
    this.selectedCategory = value.template.category;
496

    
497
    this.verificationRuleForm.get('confidenceLevelMinThreshold').setValue(value.confidenceLevelMinThreshold);
498
    this.verificationRuleForm.get('capturingVerification').setValue(value.capturingVerification);
499
    this.verificationRuleForm.get('journalVerification').setValue(value.journalVerification);
500
    this.verificationRuleForm.get('alteryxRoutineId').setValue(value.alteryxRoutineId);
501
    this.verificationRuleForm.get('template').setValue(value.template);
502

    
503
    // Convert 'verificationRuleStatus' from string to boolean.
504
    this.verificationRuleForm.get('verificationRuleStatus').setValue(value.verificationRuleStatus.trim().toLowerCase() == 'enabled');
505

    
506
    // To set the subcategory/subclassification we also have to make sure the documentClassification is the same.
507
    // WARNING: Since we have enabled the [forceSelection] option of the <p-autoComplete>,
508
    // if the availableDocumentSubclassifications are not set, documentSubclassification won't be displayed.
509
    this.verificationRuleForm.get('subCategoryCode').setValue(
510
      this.documentSubclassificationsList.find(subClass =>
511
        subClass.subclassificationName == value.template.subCategoryCode && subClass.documentClassification.classificationId == value.docClassificationCategory.classificationId
512
      )
513
    );
514

    
515
    this.verificationRuleForm.updateValueAndValidity();
516
  }
517

    
518
  public isValid(): boolean {
519
    return this.verificationRuleForm.valid;
520
  }
521

    
522
  public initForCreation() {
523
    this.editMode = false;
524
    this.verificationRuleForm.get('verificationRuleStatus').setValue(true); // The default value in creation mode.
525
    this.verificationRuleForm.get('template').setValidators(null);  // 'template' is not required during the creation.
526
  }
527
}
(5-5/5)