Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
83347a0472cb668acbdacd5c219a2d3c843d08a9
deb3af53cfba7230233e3e3d444679956488799e
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Binary file not shown.
Binary file added imxweb/imx-modules/elemental-ui-core-18.0.14.tgz
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<void> {
const actionParameters = {
Expand Down Expand Up @@ -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'
);
Expand Down Expand Up @@ -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));
Expand All @@ -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),
});
},
});
Expand Down Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ import { EntityData } from 'imx-qbm-dbts';
export interface Approvers {
current: EntityData[];
future: EntityData[];
canSeeSteps: boolean;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<li data-imx-identifier="decision-step-is-calculating" class="imx-event imx-pending"
*ngIf="approvers.current.length === 0">
*ngIf="approvers.current.length === 0 && approvers.canSeeSteps">
<mat-card>
{{'#LDS#The next approval step is currently being calculated.' | translate}}
{{
'#LDS#The next approval step is currently being calculated.' | translate
}}
</mat-card>
</li>
<li data-imx-identifier="decision-step-current-approvers" class="imx-event imx-pending"
Expand Down
21 changes: 12 additions & 9 deletions imxweb/projects/att/src/lib/decision/attestation-cases.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,17 @@ export class AttestationCasesService {
}

public async get(
attDecisionParameters: AttestationDecisionLoadParameters,
isUserEscalationApprover = false
): Promise<TypedEntityCollectionData<AttestationCase> | 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<TypedEntityCollectionData<AttestationCase>> {
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,
Expand Down Expand Up @@ -166,6 +168,7 @@ export class AttestationCasesService {
return {
current: approverContainer.approverNow,
future: approverContainer.approverFuture,
canSeeSteps: approverContainer.canSeeSteps,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
{
Expand Down
65 changes: 38 additions & 27 deletions imxweb/projects/qbm/src/lib/data-export/export-columns.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand All @@ -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 {
Expand All @@ -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));
}
Expand All @@ -127,8 +138,8 @@ export class ExportColumnsService {
columns,
isAllData: this.isAllData,
selectedExport,
exportOptions: this.exportOptions
}
exportOptions: this.exportOptions,
};
}

// Setup an option for column export
Expand All @@ -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
Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export class ApproverContainer {
*/
public approverNow: EntityData[] = [];

public canSeeSteps: boolean;

/**
* List of next approvers
*/
Expand Down Expand Up @@ -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);
Expand Down