diff --git a/.commit b/.commit index 59e1d3699..3d7f197a9 100644 --- a/.commit +++ b/.commit @@ -1 +1 @@ -83347a0472cb668acbdacd5c219a2d3c843d08a9 +deb3af53cfba7230233e3e3d444679956488799e diff --git a/README.md b/README.md index 2684c3003..17619c502 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,12 @@ ## Change log +### December 11, 2024 +- 430843: Fixes an inconsistency in the Attestation History when an approver is defined in multiple sublevels. +- 467451: Fixes some issues with the „Show attestation cases to be approved by chief approval team“ toggle on the Pending Attestation page. +- 472174: Fixes an issue with the data export in data tables, when using additional columns in the configuration. +- 470782: Fixes the information, provided for attestors and receivers. + ### November 19, 2024 - 468962: Method confirmGeneral returns a valid value on OkResult. - 465213: Fixes an infinite loop if the an invalid value is set on a basic CDR. diff --git a/imxweb/imx-modules/elemental-ui-cadence-icon-3.1.107.tgz b/imxweb/imx-modules/elemental-ui-cadence-icon-3.1.107.tgz new file mode 100644 index 000000000..41db7489e Binary files /dev/null and b/imxweb/imx-modules/elemental-ui-cadence-icon-3.1.107.tgz differ diff --git a/imxweb/imx-modules/elemental-ui-core-18.0.14.tgz b/imxweb/imx-modules/elemental-ui-core-18.0.14.tgz new file mode 100644 index 000000000..3b35826d9 Binary files /dev/null and b/imxweb/imx-modules/elemental-ui-core-18.0.14.tgz differ diff --git a/imxweb/projects/att/src/lib/attestation-action/attestation-action.service.ts b/imxweb/projects/att/src/lib/attestation-action/attestation-action.service.ts index 48c04a35c..1f4b6bc34 100644 --- a/imxweb/projects/att/src/lib/attestation-action/attestation-action.service.ts +++ b/imxweb/projects/att/src/lib/attestation-action/attestation-action.service.ts @@ -24,29 +24,39 @@ * */ -import { Injectable, Type } from '@angular/core'; import { OverlayRef } from '@angular/cdk/overlay'; +import { Injectable, Type } from '@angular/core'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { Subject } from 'rxjs'; import { PortalAttestationApprove } from 'imx-api-att'; -import { CompareOperator, EntityData, FilterType, ValType } from 'imx-qbm-dbts'; -import { SnackBarService, EntityService, ColumnDependentReference, BaseCdr, ExtService, BaseReadonlyCdr, CdrFactoryService } from 'qbm'; +import { CompareOperator, EntityData, FilterType, TypedEntity, ValType } from 'imx-qbm-dbts'; +import { + AuthenticationService, + BaseCdr, + BaseReadonlyCdr, + CdrFactoryService, + ColumnDependentReference, + EntityService, + ExtService, + SnackBarService, +} from 'qbm'; import { JustificationService, JustificationType, PersonService, UserModelService } from 'qer'; +import { ApiService } from '../api.service'; +import { AttestationCase } from '../decision/attestation-case'; import { AttestationCasesService } from '../decision/attestation-cases.service'; +import { AttestationInquiry } from '../decision/attestation-inquiries/attestation-inquiry.model'; import { AttestationActionComponent } from './attestation-action.component'; -import { AttestationCase } from '../decision/attestation-case'; -import { AttestationWorkflowService } from './attestation-workflow.service'; import { AttestationCaseAction } from './attestation-case-action.interface'; -import { ApiService } from '../api.service'; -import { AttestationInquiry } from '../decision/attestation-inquiries/attestation-inquiry.model'; +import { AttestationWorkflowService } from './attestation-workflow.service'; @Injectable({ providedIn: 'root', }) export class AttestationActionService { public readonly applied = new Subject(); + private uidUser: string; constructor( private readonly apiService: ApiService, @@ -58,10 +68,13 @@ export class AttestationActionService { private readonly snackBar: SnackBarService, private readonly entityService: EntityService, private readonly person: PersonService, - private readonly workflow: AttestationWorkflowService, + private readonly workflow: AttestationWorkflowService, private readonly userService: UserModelService, - private readonly extService: ExtService - ) {} + private readonly extService: ExtService, + authentication: AuthenticationService + ) { + authentication.onSessionResponse.subscribe((state) => (this.uidUser = state.UserUid ?? '')); + } public async directDecision(attestationCases: AttestationCase[], userUid: string): Promise { const actionParameters = { @@ -384,7 +397,7 @@ export class AttestationActionService { ColumnName: 'ReasonHead', Type: ValType.Text, IsMultiLine: true, - MinLen:metadata.mandatory ? 1 : 0 + MinLen: metadata.mandatory ? 1 : 0, }), metadata.display || '#LDS#Reason for your decision' ); @@ -452,7 +465,7 @@ export class AttestationActionService { try { justification = await this.justification.createCdr( - approve ? JustificationType.approveAttestation : JustificationType.denyAttestation, + approve ? JustificationType.approveAttestation : JustificationType.denyAttestation ); } finally { setTimeout(() => this.busyService.hide(busyIndicator)); @@ -479,6 +492,7 @@ export class AttestationActionService { Reason: actionParameters.reason.column.GetValue(), UidJustification: actionParameters.justification?.column?.GetValue(), Decision: approve, + SubLevel: this.getSubLevel(attestationCase, attestationCase.data), }); }, }); @@ -544,4 +558,26 @@ export class AttestationActionService { display || '#LDS#Identity' ); } + + private getSubLevel(entity: TypedEntity, extended: any): number { + //get all workflowsteps for the current decision level + const steps = extended.WorkflowSteps?.Entities?.filter( + (elem) => + elem?.Columns?.UID_QERWorkingMethod.Value === entity.GetEntity().GetColumn('UID_QERWorkingMethod').GetValue() && + elem.Columns.LevelNumber.Value === entity.GetEntity().GetColumn('DecisionLevel').GetValue() + ); + // get the Workflow data that + // - belong to one of the current workflow steps + // - can be decided by the user + // - are not decided yet + const data = steps.flatMap((step) => + extended.WorkflowData.Entities.filter( + (elem) => + elem?.Columns?.UID_QERWorkingStep.Value === step?.Columns?.UID_QERWorkingStep.Value && + elem?.Columns?.UID_PersonHead.Value === this.uidUser && + elem?.Columns?.Decision?.Value === '' + ) + ); + return data[0]?.Columns?.SubLevelNumber?.Value ?? 0; //return the sublevel number + } } diff --git a/imxweb/projects/att/src/lib/decision/approvers.interface.ts b/imxweb/projects/att/src/lib/decision/approvers.interface.ts index fb048cf2f..85c8d622f 100644 --- a/imxweb/projects/att/src/lib/decision/approvers.interface.ts +++ b/imxweb/projects/att/src/lib/decision/approvers.interface.ts @@ -30,4 +30,5 @@ import { EntityData } from 'imx-qbm-dbts'; export interface Approvers { current: EntityData[]; future: EntityData[]; + canSeeSteps: boolean; } diff --git a/imxweb/projects/att/src/lib/decision/approvers/approvers.component.html b/imxweb/projects/att/src/lib/decision/approvers/approvers.component.html index 14e16ae81..b42929b29 100644 --- a/imxweb/projects/att/src/lib/decision/approvers/approvers.component.html +++ b/imxweb/projects/att/src/lib/decision/approvers/approvers.component.html @@ -1,7 +1,9 @@
  • + *ngIf="approvers.current.length === 0 && approvers.canSeeSteps"> - {{'#LDS#The next approval step is currently being calculated.' | translate}} + {{ + '#LDS#The next approval step is currently being calculated.' | translate + }}
  • | undefined> { - const collection = await this.attClient.typedClient.PortalAttestationApprove.Get(attDecisionParameters, { - signal: this.abortController.signal, - }); - if (!collection) { - return undefined; - } + attDecisionParameters?: AttestationDecisionLoadParameters, + isUserEscalationApprover = false, + signal?: AbortSignal, + ): Promise> { + const navigationState = { + ...attDecisionParameters, + Escalation: + ((attDecisionParameters?.uid_attestationcase ?? '') !== '' && isUserEscalationApprover) || attDecisionParameters?.Escalation, + }; + + const collection = await this.attClient.typedClient.PortalAttestationApprove.Get(navigationState, { signal }); return { tableName: collection?.tableName, totalCount: collection?.totalCount, @@ -166,6 +168,7 @@ export class AttestationCasesService { return { current: approverContainer.approverNow, future: approverContainer.approverFuture, + canSeeSteps: approverContainer.canSeeSteps, }; } diff --git a/imxweb/projects/att/src/lib/decision/attestation-decision.component.ts b/imxweb/projects/att/src/lib/decision/attestation-decision.component.ts index f22833004..f295a578b 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-decision.component.ts +++ b/imxweb/projects/att/src/lib/decision/attestation-decision.component.ts @@ -378,7 +378,7 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { attestationCaseWithPolicy = ( await this.attestationCases.get( { - Escalation: this.viewEscalation, + Escalation: this.isUserEscalationApprover, uidpolicy: attestationCase.UID_AttestationPolicy.value, filter: [ { diff --git a/imxweb/projects/qbm/src/lib/data-export/export-columns.service.ts b/imxweb/projects/qbm/src/lib/data-export/export-columns.service.ts index 7ea4d4c9f..f60946dc9 100644 --- a/imxweb/projects/qbm/src/lib/data-export/export-columns.service.ts +++ b/imxweb/projects/qbm/src/lib/data-export/export-columns.service.ts @@ -32,19 +32,20 @@ import _ from 'lodash'; import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; export interface DSTExportState { - dataModel?: DataModel, - selectedExport?: FormControl, - exportOptions?: EuiSelectOption[], - isAllData?: boolean, - columns?: FormControl[], - columnOptions?: EuiSelectOption[] + dataModel?: DataModel; + selectedExport?: FormControl; + exportOptions?: EuiSelectOption[]; + isAllData?: boolean; + columns?: FormControl[]; + columnOptions?: EuiSelectOption[]; } -export interface FilteredColumnOption{ - value: string; options: EuiSelectOption[]; +export interface FilteredColumnOption { + value: string; + options: EuiSelectOption[]; } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ExportColumnsService { public columnOptions: EuiSelectOption[]; @@ -63,14 +64,15 @@ export class ExportColumnsService { { display: 'PDF', value: 'application/pdf', - } + }, ]; - public exportOptionsFilter = (option: EuiSelectOption, searchInputValue: string) => option.display.toLowerCase().includes(searchInputValue.toLowerCase()); + public exportOptionsFilter = (option: EuiSelectOption, searchInputValue: string) => + option.display.toLowerCase().includes(searchInputValue.toLowerCase()); public columnOptionsFilter = (option: EuiSelectOption, searchInputValue: string) => { const sanitizedInput = searchInputValue.toLowerCase(); return option.display.toLowerCase().includes(sanitizedInput) || option?.displayDetail?.toLowerCase().includes(sanitizedInput); - } + }; // This function will check if the incoming data model is different from what exists. public setupExport(settings?: DataSourceToolbarSettings): void { @@ -84,32 +86,41 @@ export class ExportColumnsService { } public checkDataModel(dataModel: DataModel): boolean { - const stashedProperties = this.stashedState.dataModel.Properties.map(column => column.Property.ColumnName); - const properties = dataModel.Properties.map(column => column.Property.ColumnName); + const stashedProperties = this.stashedState.dataModel.Properties.map((column) => column.Property.ColumnName); + const properties = dataModel.Properties.map((column) => column.Property.ColumnName); return _.isEqual(stashedProperties, properties); } // Saves the column options internally public createInitialState(settings: DataSourceToolbarSettings): void { // Column Options sorted alphebetically, not filtering by IsAdditional - this leads to empty exports - const columnOptions = settings.dataModel.Properties.map(prop => { + + const columnOptions = settings.dataModel.Properties.map((prop) => { return this.makeOption(prop); }); - columnOptions.sort((a, b) => a.display >= b.display ? 1: -1); + columnOptions.sort((a, b) => (a.display >= b.display ? 1 : -1)); + + const selectedExport = new FormControl('text/csv'); // Check for initial columns, or try to use displayed columns const columns: FormControl[] = []; if (settings.exportMethod?.initialColumns) { - settings.exportMethod.initialColumns.forEach(column => { - const option = columnOptions.find(prop => prop.value === column); + // get additional columns from the configuration + const conf = settings.viewConfig?.viewConfigs?.find((elem) => elem.UseAsDefault); + const additional = conf?.AdditionalTableColumns ?? []; + // Create an unique, combined list + const columnsToDisplay = new Set(settings.exportMethod.initialColumns.concat(additional)); + + columnsToDisplay.forEach((column) => { + const option = columnOptions.find((prop) => prop.value === column); if (option) { columns.push(this.createColumn(option)); } - }) + }); } else if (settings?.displayedColumns) { - settings.displayedColumns.forEach(column => { - const option = columnOptions.find(prop => prop.value === column.ColumnName); + settings.displayedColumns.forEach((column) => { + const option = columnOptions.find((prop) => prop.value === column.ColumnName); if (option) { columns.push(this.createColumn(option)); } @@ -127,8 +138,8 @@ export class ExportColumnsService { columns, isAllData: this.isAllData, selectedExport, - exportOptions: this.exportOptions - } + exportOptions: this.exportOptions, + }; } // Setup an option for column export @@ -137,8 +148,8 @@ export class ExportColumnsService { return { display, value: property.Property.ColumnName, - displayDetail: property.Property?.Description - } + displayDetail: property.Property?.Description, + }; } // Create a new column for export @@ -149,10 +160,10 @@ export class ExportColumnsService { // Stash the state of the sidesheet for later use public stashState(): void { // Filter out all invalid columns, leave one if all are invalid - if (this.stashedState.columns.every(column => column.invalid)) { + if (this.stashedState.columns.every((column) => column.invalid)) { this.stashedState.columns = [this.createColumn()]; } else { - this.stashedState.columns = this.stashedState.columns.filter(column => column.valid); + this.stashedState.columns = this.stashedState.columns.filter((column) => column.valid); } } } diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.ts b/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.ts index f36954ceb..e8571bef6 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.ts @@ -37,6 +37,8 @@ export class ApproverContainer { */ public approverNow: EntityData[] = []; + public canSeeSteps: boolean; + /** * List of next approvers */ @@ -120,6 +122,8 @@ export class ApproverContainer { const orderedWorkingSteps = this.buildOrderedWorkingSteps(); this.logger?.trace(this, 'working steps with order', orderedWorkingSteps); + this.canSeeSteps = this.request.pwoData?.WorkflowSteps?.Entities.length > 0; + if (canSeeCurrent) { const currentSteps = orderedWorkingSteps.filter((step) => step.order === 1); this.logger?.trace(this, 'current steps', currentSteps);