DM-311 simple workflow

This commit is contained in:
Dan Percic 2023-06-27 20:23:02 +03:00
parent e4c355cb0a
commit 2dca5bf3f5
4 changed files with 153 additions and 119 deletions

View File

@ -49,11 +49,11 @@ import { Roles } from '@users/roles';
@Injectable() @Injectable()
export class ConfigService { export class ConfigService {
readonly listingMode$: Observable<ListingMode>;
readonly #dossierId = getParam(DOSSIER_ID); readonly #dossierId = getParam(DOSSIER_ID);
readonly #currentUser = getCurrentUser<User>(); readonly #currentUser = getCurrentUser<User>();
readonly #config = getConfig<AppConfig>(); readonly #config = getConfig<AppConfig>();
readonly #listingMode$: BehaviorSubject<ListingMode>; readonly #listingMode$: BehaviorSubject<ListingMode>;
readonly listingMode$: Observable<ListingMode>;
constructor( constructor(
private readonly _permissionsService: PermissionsService, private readonly _permissionsService: PermissionsService,
@ -87,63 +87,10 @@ export class ConfigService {
} }
workflowConfig(): WorkflowConfig<File, WorkflowFileStatus> { workflowConfig(): WorkflowConfig<File, WorkflowFileStatus> {
const { NEW, UNDER_REVIEW, UNDER_APPROVAL, APPROVED } = WorkflowFileStatuses; const newColumn = this.#getNewColumn();
const underReviewColumn = this.#getUnderReviewColumn();
const newColumn: WorkflowColumn<File, typeof NEW> = { const underApprovalColumn = this.#getUnderApprovalColumn();
label: workflowFileStatusTranslations[NEW], const approvedColumn = this.#getApprovedColumn();
key: NEW,
enterFn: files => this._bulkActionsService.setToNew(files),
enterPredicate: files => this._permissionsService.canSetToNew(files, this.#dossier),
color: '#D3D5DA',
entities: new BehaviorSubject([]),
};
const underReviewColumn: WorkflowColumn<File, typeof UNDER_REVIEW> = {
label: workflowFileStatusTranslations[UNDER_REVIEW],
enterFn: async files => {
const statuses: WorkflowFileStatus[] = [UNDER_APPROVAL, APPROVED];
if (statuses.includes(files[0].workflowStatus)) {
await this._bulkActionsService.backToUnderReview(files);
} else {
await this._bulkActionsService.assignToMe(files);
}
},
enterPredicate: files => {
const dossier = this.#dossier;
return (
this._permissionsService.canSetUnderReview(files, dossier) ||
this._permissionsService.canAssignToSelf(files, dossier) ||
this._permissionsService.canAssignUser(files, dossier) ||
this._permissionsService.canUndoApproval(files, dossier)
);
},
key: UNDER_REVIEW,
color: '#FDBD00',
entities: new BehaviorSubject([]),
};
const underApprovalColumn: WorkflowColumn<File, typeof UNDER_APPROVAL> = {
label: workflowFileStatusTranslations[UNDER_APPROVAL],
enterFn: files => this._bulkActionsService.setToUnderApproval(files),
enterPredicate: files => {
const dossier = this.#dossier;
return (
this._permissionsService.canSetUnderApproval(files, dossier) || this._permissionsService.canUndoApproval(files, dossier)
);
},
key: UNDER_APPROVAL,
color: '#374C81',
entities: new BehaviorSubject([]),
};
const approvedColumn: WorkflowColumn<File, typeof APPROVED> = {
label: workflowFileStatusTranslations[APPROVED],
enterFn: files => this._bulkActionsService.approve(files),
enterPredicate: files => this._permissionsService.canBeApproved(files, this.#dossier),
key: APPROVED,
color: '#48C9F7',
entities: new BehaviorSubject([]),
};
return { return {
columnIdentifierFn: entity => entity.workflowStatus, columnIdentifierFn: entity => entity.workflowStatus,
@ -152,12 +99,24 @@ export class ConfigService {
}; };
} }
workflowConfigRss(): WorkflowConfig<File, WorkflowFileStatus> {
const newColumn = this.#getNewColumn();
const underReviewColumn = this.#getUnderReviewColumn();
const approvedColumn = this.#getApprovedColumn();
return {
columnIdentifierFn: entity => entity.workflowStatus,
itemVersionFn: (entity: File) => `${entity.lastUpdated}-${entity.numberOfAnalyses}`,
columns: [newColumn, underReviewColumn, approvedColumn],
};
}
actionConfig(dossierId: string, disabled$: Observable<boolean>): List<ActionConfig> { actionConfig(dossierId: string, disabled$: Observable<boolean>): List<ActionConfig> {
return [ return [
{ {
id: 'editDossier', id: 'editDossier',
label: this._translateService.instant('dossier-overview.header-actions.edit'), label: this._translateService.instant('dossier-overview.header-actions.edit'),
action: () => this._openEditDossierDialog(dossierId), action: () => this.#openEditDossierDialog(dossierId),
icon: 'iqser:edit', icon: 'iqser:edit',
hide: !this.#currentUser.isManager && !this._iqserPermissionsService.has(Roles.dossiers.edit), hide: !this.#currentUser.isManager && !this._iqserPermissionsService.has(Roles.dossiers.edit),
helpModeKey: 'edit_dossier_in_dossier', helpModeKey: 'edit_dossier_in_dossier',
@ -306,7 +265,7 @@ export class ConfigService {
); );
} }
this._sortByName([...allDistinctPeople]).forEach(userId => { this.#sortByName([...allDistinctPeople]).forEach(userId => {
peopleFilters.push( peopleFilters.push(
new NestedFilter({ new NestedFilter({
id: userId, id: userId,
@ -376,7 +335,7 @@ export class ConfigService {
filterGroups.push({ filterGroups.push({
slug: 'quickFilters', slug: 'quickFilters',
filters: this._quickFilters(entities), filters: this.#quickFilters(entities),
checker: (file: File) => checker: (file: File) =>
checkedRequiredFilters().reduce((acc, f) => acc && f.checker(file), true) && checkedRequiredFilters().reduce((acc, f) => acc && f.checker(file), true) &&
(checkedNotRequiredFilters().length === 0 || checkedNotRequiredFilters().reduce((acc, f) => acc || f.checker(file), false)), (checkedNotRequiredFilters().length === 0 || checkedNotRequiredFilters().reduce((acc, f) => acc || f.checker(file), false)),
@ -410,7 +369,71 @@ export class ConfigService {
_assignedToOthersChecker = (file: File) => file.assignee && file.assignee !== this._userService.currentUser.id; _assignedToOthersChecker = (file: File) => file.assignee && file.assignee !== this._userService.currentUser.id;
private _quickFilters(entities: File[]): NestedFilter[] { #getNewColumn(): WorkflowColumn<File, typeof WorkflowFileStatuses.NEW> {
return {
label: workflowFileStatusTranslations[WorkflowFileStatuses.NEW],
key: WorkflowFileStatuses.NEW,
enterFn: files => this._bulkActionsService.setToNew(files),
enterPredicate: files => this._permissionsService.canSetToNew(files, this.#dossier),
color: '#D3D5DA',
entities: new BehaviorSubject([]),
};
}
#getUnderReviewColumn(): WorkflowColumn<File, typeof WorkflowFileStatuses.UNDER_REVIEW> {
return {
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_REVIEW],
enterFn: async files => {
const statuses: WorkflowFileStatus[] = [WorkflowFileStatuses.UNDER_APPROVAL, WorkflowFileStatuses.APPROVED];
if (statuses.includes(files[0].workflowStatus)) {
await this._bulkActionsService.backToUnderReview(files);
} else {
await this._bulkActionsService.assignToMe(files);
}
},
enterPredicate: files => {
const dossier = this.#dossier;
return (
this._permissionsService.canSetUnderReview(files, dossier) ||
this._permissionsService.canAssignToSelf(files, dossier) ||
this._permissionsService.canAssignUser(files, dossier) ||
this._permissionsService.canUndoApproval(files, dossier)
);
},
key: WorkflowFileStatuses.UNDER_REVIEW,
color: '#FDBD00',
entities: new BehaviorSubject([]),
};
}
#getUnderApprovalColumn(): WorkflowColumn<File, typeof WorkflowFileStatuses.UNDER_APPROVAL> {
return {
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_APPROVAL],
enterFn: files => this._bulkActionsService.setToUnderApproval(files),
enterPredicate: files => {
const dossier = this.#dossier;
return (
this._permissionsService.canSetUnderApproval(files, dossier) || this._permissionsService.canUndoApproval(files, dossier)
);
},
key: WorkflowFileStatuses.UNDER_APPROVAL,
color: '#374C81',
entities: new BehaviorSubject([]),
};
}
#getApprovedColumn(): WorkflowColumn<File, typeof WorkflowFileStatuses.APPROVED> {
return {
label: workflowFileStatusTranslations[WorkflowFileStatuses.APPROVED],
enterFn: files => this._bulkActionsService.approve(files),
enterPredicate: files => this._permissionsService.canBeApproved(files, this.#dossier),
key: WorkflowFileStatuses.APPROVED,
color: '#48C9F7',
entities: new BehaviorSubject([]),
};
}
#quickFilters(entities: File[]): NestedFilter[] {
const recentPeriod = this.#config.RECENT_PERIOD_IN_HOURS; const recentPeriod = this.#config.RECENT_PERIOD_IN_HOURS;
return [ return [
{ {
@ -447,11 +470,11 @@ export class ConfigService {
].map(filter => new NestedFilter(filter)); ].map(filter => new NestedFilter(filter));
} }
private _openEditDossierDialog(dossierId: string) { #openEditDossierDialog(dossierId: string) {
this._dialogService.openDialog('editDossier', { dossierId }); this._dialogService.openDialog('editDossier', { dossierId });
} }
private _sortByName(ids: string[]) { #sortByName(ids: string[]) {
return ids.sort((a, b) => this._userService.getName(a).localeCompare(this._userService.getName(b))); return ids.sort((a, b) => this._userService.getName(a).localeCompare(this._userService.getName(b)));
} }
} }

View File

@ -20,6 +20,7 @@ import {
CustomError, CustomError,
ErrorService, ErrorService,
getParam, getParam,
IqserPermissionsService,
ListingComponent, ListingComponent,
ListingModes, ListingModes,
listingProvidersFactory, listingProvidersFactory,
@ -44,6 +45,7 @@ import { FilesService } from '@services/files/files.service';
import { BulkActionsService } from '../services/bulk-actions.service'; import { BulkActionsService } from '../services/bulk-actions.service';
import { DossiersService } from '@services/dossiers/dossiers.service'; import { DossiersService } from '@services/dossiers/dossiers.service';
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider'; import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
import { Roles } from '@users/roles';
@Component({ @Component({
templateUrl: './dossier-overview-screen.component.html', templateUrl: './dossier-overview-screen.component.html',
@ -51,10 +53,15 @@ import { dossiersServiceProvider } from '@services/entity-services/dossiers.serv
providers: [...listingProvidersFactory(DossierOverviewScreenComponent), ConfigService, BulkActionsService, dossiersServiceProvider], providers: [...listingProvidersFactory(DossierOverviewScreenComponent), ConfigService, BulkActionsService, dossiersServiceProvider],
}) })
export class DossierOverviewScreenComponent extends ListingComponent<File> implements OnInit, OnAttach, OnDetach, OnDestroy { export class DossierOverviewScreenComponent extends ListingComponent<File> implements OnInit, OnAttach, OnDetach, OnDestroy {
#dossier: Dossier;
@ViewChild('needsWorkFilterTemplate', { read: TemplateRef, static: true })
private readonly _needsWorkFilterTemplate: TemplateRef<unknown>;
@ViewChild('fileInput', { static: true }) private readonly _fileInput: ElementRef;
@ViewChild(TableComponent) private readonly _tableComponent: TableComponent<Dossier>;
private _fileAttributeConfigs: IFileAttributeConfig[];
readonly listingModes = ListingModes; readonly listingModes = ListingModes;
readonly circleButtonTypes = CircleButtonTypes; readonly circleButtonTypes = CircleButtonTypes;
readonly tableHeaderLabel = _('dossier-overview.table-header.title'); readonly tableHeaderLabel = _('dossier-overview.table-header.title');
collapsedDetails = false; collapsedDetails = false;
dossierAttributes: DossierAttributeWithValue[] = []; dossierAttributes: DossierAttributeWithValue[] = [];
tableColumnConfigs: readonly TableColumnConfig<File>[]; tableColumnConfigs: readonly TableColumnConfig<File>[];
@ -70,18 +77,13 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
readonly dossierId = getParam(DOSSIER_ID); readonly dossierId = getParam(DOSSIER_ID);
readonly workflowConfig: WorkflowConfig<File, WorkflowFileStatus>; readonly workflowConfig: WorkflowConfig<File, WorkflowFileStatus>;
readonly dossierAttributes$: Observable<DossierAttributeConfig[]>; readonly dossierAttributes$: Observable<DossierAttributeConfig[]>;
#dossier: Dossier;
@ViewChild('needsWorkFilterTemplate', { read: TemplateRef, static: true })
private readonly _needsWorkFilterTemplate: TemplateRef<unknown>;
@ViewChild('fileInput', { static: true }) private readonly _fileInput: ElementRef;
@ViewChild(TableComponent) private readonly _tableComponent: TableComponent<Dossier>;
private _fileAttributeConfigs: IFileAttributeConfig[];
constructor( constructor(
readonly configService: ConfigService, readonly configService: ConfigService,
private readonly _errorService: ErrorService, private readonly _errorService: ErrorService,
private readonly _filesService: FilesService, private readonly _filesService: FilesService,
readonly permissionsService: PermissionsService, readonly permissionsService: PermissionsService,
iqserPermissionsService: IqserPermissionsService,
private readonly _loadingService: LoadingService, private readonly _loadingService: LoadingService,
private readonly _fileMapService: FilesMapService, private readonly _fileMapService: FilesMapService,
private readonly _dossiersService: DossiersService, private readonly _dossiersService: DossiersService,
@ -97,7 +99,8 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
this.dossier$ = _dossiersService.getEntityChanged$(this.dossierId).pipe(tap(dossier => (this.#dossier = dossier))); this.dossier$ = _dossiersService.getEntityChanged$(this.dossierId).pipe(tap(dossier => (this.#dossier = dossier)));
this.dossierAttributes$ = this._dossierAttributesService.all$.pipe(tap(() => this.#updateDossierAttributes())); this.dossierAttributes$ = this._dossierAttributesService.all$.pipe(tap(() => this.#updateDossierAttributes()));
this.#dossier = _dossiersService.find(this.dossierId); this.#dossier = _dossiersService.find(this.dossierId);
this.workflowConfig = configService.workflowConfig(); const hasRss = iqserPermissionsService.has(Roles.getRss);
this.workflowConfig = hasRss ? configService.workflowConfigRss() : configService.workflowConfig();
this.files$ = merge(this.#files$, this.#dossierFilesChange$).pipe(shareLast()); this.files$ = merge(this.#files$, this.#dossierFilesChange$).pipe(shareLast());
this.#updateFileAttributes(); this.#updateFileAttributes();
} }

View File

@ -55,7 +55,7 @@ export class PermissionsService {
entities.length && entities.length &&
this.isAdmin() && this.isAdmin() &&
this._iqserPermissionsService.has(Roles.dictionaryTypes.delete) && this._iqserPermissionsService.has(Roles.dictionaryTypes.delete) &&
entities.reduce((acc, _entity) => this._canDeleteEntity(_entity) && acc, true) entities.reduce((acc, _entity) => this.#canDeleteEntity(_entity) && acc, true)
); );
} }
@ -94,7 +94,7 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
const sameState = new Set(files.map(f => f.excluded)).size === 1; const sameState = new Set(files.map(f => f.excluded)).size === 1;
return sameState && files.reduce((acc, _file) => this._canToggleAnalysis(_file, dossier) && acc, true); return sameState && files.reduce((acc, _file) => this.#canToggleAnalysis(_file, dossier) && acc, true);
} }
showToggleAnalysis(dossier: Dossier): boolean { showToggleAnalysis(dossier: Dossier): boolean {
@ -105,21 +105,21 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.files.reanalyze) && this._iqserPermissionsService.has(Roles.files.reanalyze) &&
files.reduce((acc, _file) => this._canReanalyseFile(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canReanalyseFile(_file, dossier) && acc, true)
); );
} }
canEnableAutoAnalysis(files: File[], dossier: Dossier): boolean { canEnableAutoAnalysis(files: File[], dossier: Dossier): boolean {
return ( return (
this._iqserPermissionsService.has(Roles.files.reanalyze) && this._iqserPermissionsService.has(Roles.files.reanalyze) &&
files.reduce((acc, _file) => this._canEnableAutoAnalysis(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canEnableAutoAnalysis(_file, dossier) && acc, true)
); );
} }
canDisableAutoAnalysis(files: File[], dossier: Dossier): boolean { canDisableAutoAnalysis(files: File[], dossier: Dossier): boolean {
return ( return (
this._iqserPermissionsService.has(Roles.files.reanalyze) && this._iqserPermissionsService.has(Roles.files.reanalyze) &&
files.reduce((acc, _file) => this._canDisableAutoAnalysis(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canDisableAutoAnalysis(_file, dossier) && acc, true)
); );
} }
@ -131,7 +131,7 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.files.delete) && this._iqserPermissionsService.has(Roles.files.delete) &&
files.reduce((acc, _file) => this._canSoftDeleteFile(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canSoftDeleteFile(_file, dossier) && acc, true)
); );
} }
@ -139,7 +139,7 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.files.delete) && this._iqserPermissionsService.has(Roles.files.delete) &&
files.reduce((acc, _file) => this._canRestoreFile(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canRestoreFile(_file, dossier) && acc, true)
); );
} }
@ -147,7 +147,7 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.files.delete) && this._iqserPermissionsService.has(Roles.files.delete) &&
files.reduce((acc, _file) => this._canHardDeleteFile(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canHardDeleteFile(_file, dossier) && acc, true)
); );
} }
@ -155,7 +155,7 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.files.reanalyze) && this._iqserPermissionsService.has(Roles.files.reanalyze) &&
files.reduce((acc, _file) => this._canOcrFile(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canOcrFile(_file, dossier) && acc, true)
); );
} }
@ -163,7 +163,7 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.setReviewer) && this._iqserPermissionsService.has(Roles.setReviewer) &&
files.reduce((acc, _file) => this._canAssignToSelf(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canAssignToSelf(_file, dossier) && acc, true)
); );
} }
@ -171,7 +171,7 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.setReviewer) && this._iqserPermissionsService.has(Roles.setReviewer) &&
files.reduce((acc, _file) => this._canAssignUser(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canAssignUser(_file, dossier) && acc, true)
); );
} }
@ -179,25 +179,25 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.setReviewer) && this._iqserPermissionsService.has(Roles.setReviewer) &&
files.reduce((acc, _file) => this._canUnassignUser(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canUnassignUser(_file, dossier) && acc, true)
); );
} }
canSetToNew(file: File | File[], dossier: Dossier): boolean { canSetToNew(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return files.reduce((acc, _file) => this._canSetToNew(_file, dossier) && acc, true); return files.reduce((acc, _file) => this.#canSetToNew(_file, dossier) && acc, true);
} }
canSetUnderReview(file: File | File[], dossier: Dossier): boolean { canSetUnderReview(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return files.reduce((acc, _file) => this._canSetUnderReview(_file, dossier) && acc, true); return files.reduce((acc, _file) => this.#canSetUnderReview(_file, dossier) && acc, true);
} }
canBeApproved(file: File | File[], dossier: Dossier): boolean { canBeApproved(file: File | File[], dossier: Dossier): boolean {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.files.setApproved) && this._iqserPermissionsService.has(Roles.files.setApproved) &&
files.reduce((acc, _file) => this._canBeApproved(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canBeApproved(_file, dossier) && acc, true)
); );
} }
@ -205,7 +205,7 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.files.setApproved) && this._iqserPermissionsService.has(Roles.files.setApproved) &&
files.reduce((acc, _file) => this._isReadyForApproval(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#isReadyForApproval(_file, dossier) && acc, true)
); );
} }
@ -213,7 +213,7 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.files.setUnderApproval) && this._iqserPermissionsService.has(Roles.files.setUnderApproval) &&
files.reduce((acc, _file) => this._canSetUnderApproval(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canSetUnderApproval(_file, dossier) && acc, true)
); );
} }
@ -247,7 +247,7 @@ export class PermissionsService {
const files = file instanceof File ? [file] : file; const files = file instanceof File ? [file] : file;
return ( return (
this._iqserPermissionsService.has(Roles.files.setApproved) && this._iqserPermissionsService.has(Roles.files.setApproved) &&
files.reduce((acc, _file) => this._canUndoApproval(_file, dossier) && acc, true) files.reduce((acc, _file) => this.#canUndoApproval(_file, dossier) && acc, true)
); );
} }
@ -354,11 +354,11 @@ export class PermissionsService {
return this._iqserPermissionsService.has(Roles.rules.write) && this.isAdmin(); return this._iqserPermissionsService.has(Roles.rules.write) && this.isAdmin();
} }
private _canDeleteEntity(entity: Dictionary): boolean { #canDeleteEntity(entity: Dictionary): boolean {
return !entity.systemManaged; return !entity.systemManaged;
} }
private _canOcrFile(file: File, dossier: Dossier): boolean { #canOcrFile(file: File, dossier: Dossier): boolean {
return ( return (
dossier.isActive && dossier.isActive &&
!!file.lastProcessed && !!file.lastProcessed &&
@ -367,7 +367,7 @@ export class PermissionsService {
); );
} }
private _canToggleAnalysis(file: File, dossier: Dossier): boolean { #canToggleAnalysis(file: File, dossier: Dossier): boolean {
return ( return (
dossier.isActive && dossier.isActive &&
!!file.lastProcessed && !!file.lastProcessed &&
@ -376,78 +376,86 @@ export class PermissionsService {
); );
} }
private _canSoftDeleteFile(file: File, dossier: Dossier): boolean { #canSoftDeleteFile(file: File, dossier: Dossier): boolean {
return dossier.isActive && (this.isAssigneeOrApprover(file, dossier) || (!file.assignee && this.isDossierMember(dossier))); return dossier.isActive && (this.isAssigneeOrApprover(file, dossier) || (!file.assignee && this.isDossierMember(dossier)));
} }
private _canRestoreFile(file: File, dossier: Dossier): boolean { #canRestoreFile(file: File, dossier: Dossier): boolean {
return this.isAssigneeOrApprover(file, dossier); return this.isAssigneeOrApprover(file, dossier);
} }
private _canHardDeleteFile(file: File, dossier: Dossier): boolean { #canHardDeleteFile(file: File, dossier: Dossier): boolean {
return this.isAssigneeOrApprover(file, dossier) || (!file.assignee && this.isDossierMember(dossier)); return this.isAssigneeOrApprover(file, dossier) || (!file.assignee && this.isDossierMember(dossier));
} }
private _canReanalyseFile(file: File, dossier: Dossier): boolean { #canReanalyseFile(file: File, dossier: Dossier): boolean {
return dossier.isActive && this.isAssigneeOrApprover(file, dossier) && file.analysisRequired; return dossier.isActive && this.isAssigneeOrApprover(file, dossier) && file.analysisRequired;
} }
private _canEnableAutoAnalysis(file: File, dossier: Dossier): boolean { #canEnableAutoAnalysis(file: File, dossier: Dossier): boolean {
return ( return (
dossier.isActive && file.excludedFromAutomaticAnalysis && this.isFileAssignee(file) && !file.isApproved && !!file.lastProcessed dossier.isActive && file.excludedFromAutomaticAnalysis && this.isFileAssignee(file) && !file.isApproved && !!file.lastProcessed
); );
} }
private _canDisableAutoAnalysis(file: File, dossier: Dossier): boolean { #canDisableAutoAnalysis(file: File, dossier: Dossier): boolean {
return ( return (
dossier.isActive && !file.excludedFromAutomaticAnalysis && this.isFileAssignee(file) && !file.isApproved && !!file.lastProcessed dossier.isActive && !file.excludedFromAutomaticAnalysis && this.isFileAssignee(file) && !file.isApproved && !!file.lastProcessed
); );
} }
/** UNDER_REVIEW => NEW */ /** UNDER_REVIEW => NEW */
private _canSetToNew(file: File, dossier: Dossier): boolean { #canSetToNew(file: File, dossier: Dossier): boolean {
return dossier.isActive && file.isUnderReview && this.isAssigneeOrApprover(file, dossier); return dossier.isActive && file.isUnderReview && this.isAssigneeOrApprover(file, dossier);
} }
/** UNDER_REVIEW => UNDER_APPROVAL */ /** UNDER_REVIEW => UNDER_APPROVAL */
private _canSetUnderApproval(file: File, dossier: Dossier): boolean { #canSetUnderApproval(file: File, dossier: Dossier): boolean {
return dossier.isActive && file.isUnderReview && this.isAssigneeOrApprover(file, dossier); const hasRss = this._iqserPermissionsService.has(Roles.getRss);
const fileCanBeSetUnderApproval = !hasRss && file.isUnderReview;
return dossier.isActive && fileCanBeSetUnderApproval && this.isAssigneeOrApprover(file, dossier);
} }
/** UNDER_APPROVAL => UNDER_REVIEW OR NEW => UNDER_REVIEW */ /** UNDER_APPROVAL => UNDER_REVIEW OR NEW => UNDER_REVIEW */
private _canSetUnderReview(file: File, dossier: Dossier): boolean { #canSetUnderReview(file: File, dossier: Dossier): boolean {
if (!dossier.isActive) { if (!dossier.isActive) {
return false; return false;
} }
return file.isUnderApproval && this.isAssigneeOrApprover(file, dossier); const hasRss = this._iqserPermissionsService.has(Roles.getRss);
const fileCanBeSetUnderReview = file.isUnderApproval || (hasRss && file.isApproved);
return fileCanBeSetUnderReview && this.isAssigneeOrApprover(file, dossier);
} }
/** UNDER_APPROVAL => APPROVED */ /** UNDER_APPROVAL => APPROVED */
private _canBeApproved(file: File, dossier: Dossier): boolean { #canBeApproved(file: File, dossier: Dossier): boolean {
return this._isReadyForApproval(file, dossier) && file.canBeApproved; return this.#isReadyForApproval(file, dossier) && file.canBeApproved;
} }
private _isReadyForApproval(file: File, dossier: Dossier): boolean { #isReadyForApproval(file: File, dossier: Dossier) {
return dossier.isActive && file.isUnderApproval && this.isAssigneeOrApprover(file, dossier); const hasRss = this._iqserPermissionsService.has(Roles.getRss);
const fileCanBeApproved = file.isUnderApproval || (hasRss && file.isUnderReview);
return dossier.isActive && fileCanBeApproved && this.isAssigneeOrApprover(file, dossier);
} }
/** APPROVED => UNDER_APPROVAL */ /** APPROVED => UNDER_APPROVAL */
private _canUndoApproval(file: File, dossier: Dossier): boolean { #canUndoApproval(file: File, dossier: Dossier): boolean {
return dossier.isActive && file.isApproved && this.isApprover(dossier); const hasRss = this._iqserPermissionsService.has(Roles.getRss);
const fileCanBeSetUnderApproval = !hasRss && file.isApproved;
return dossier.isActive && fileCanBeSetUnderApproval && this.isApprover(dossier);
} }
private _assignmentPrecondition(file: File, dossier: Dossier): boolean { #assignmentPrecondition(file: File, dossier: Dossier): boolean {
return dossier.isActive && !file.isError && !file.isProcessing && !!file.lastProcessed; return dossier.isActive && !file.isError && !file.isProcessing && !!file.lastProcessed;
} }
private _canAssignToSelf(file: File, dossier: Dossier): boolean { #canAssignToSelf(file: File, dossier: Dossier): boolean {
const precondition = this._assignmentPrecondition(file, dossier) && !this.isFileAssignee(file); const precondition = this.#assignmentPrecondition(file, dossier) && !this.isFileAssignee(file);
return precondition && (this.isApprover(dossier) || (this.isDossierMember(dossier) && (file.isNew || file.isUnderReview))); return precondition && (this.isApprover(dossier) || (this.isDossierMember(dossier) && (file.isNew || file.isUnderReview)));
} }
private _canAssignUser(file: File, dossier: Dossier) { #canAssignUser(file: File, dossier: Dossier) {
const precondition = this._assignmentPrecondition(file, dossier); const precondition = this.#assignmentPrecondition(file, dossier);
if (precondition) { if (precondition) {
if ((file.isNew || file.isUnderReview) && dossier.hasReviewers && this.isDossierMember(dossier)) { if ((file.isNew || file.isUnderReview) && dossier.hasReviewers && this.isDossierMember(dossier)) {
@ -460,7 +468,7 @@ export class PermissionsService {
return false; return false;
} }
private _canUnassignUser(file: File, dossier: Dossier) { #canUnassignUser(file: File, dossier: Dossier) {
return this._assignmentPrecondition(file, dossier) && !!file.assignee && this.isAssigneeOrApprover(file, dossier); return this.#assignmentPrecondition(file, dossier) && !!file.assignee && this.isAssigneeOrApprover(file, dossier);
} }
} }

View File

@ -1435,7 +1435,7 @@
}, },
"file-status": { "file-status": {
"analyse": "Analyzing", "analyse": "Analyzing",
"approved": "Approved", "approved": "Done",
"error": "Re-processing required", "error": "Re-processing required",
"figure-detection-analyzing": "", "figure-detection-analyzing": "",
"full-processing": "Processing", "full-processing": "Processing",
@ -1453,7 +1453,7 @@
"table-parsing-analyzing": "Table Parsing", "table-parsing-analyzing": "Table Parsing",
"unassigned": "Unassigned", "unassigned": "Unassigned",
"under-approval": "Under Approval", "under-approval": "Under Approval",
"under-review": "Under Review", "under-review": "In Progress",
"unprocessed": "Unprocessed" "unprocessed": "Unprocessed"
}, },
"file-upload": { "file-upload": {