Additional tweaks for EQMS-1317 (#7854)

* Additional tweaks for EZQMS-1317

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

* fixed tests

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

* v2

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

* fixed tests

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>

---------

Signed-off-by: Victor Ilyushchenko <alt13ri@gmail.com>
This commit is contained in:
Victor Ilyushchenko 2025-02-07 19:27:04 +03:00 committed by GitHub
parent 0be8b860fc
commit 3c822510cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 291 additions and 249 deletions

View File

@ -21,7 +21,7 @@
"Major": "Hlavní",
"Minor": "Vedlejší",
"Patch": "Oprava",
"DocumentApprovals": "Revize a schválení",
"ValidationWorkflow": "Ověřovací pracovní postup",
"ChangeOwner": "Změnit vlastníka dokumentu",
"ChangeOwnerHintBeginning": "Převést vlastnictví",
"ChangeOwnerHintEnd": "na jinou osobu.",

View File

@ -21,7 +21,7 @@
"Major": "Hauptversion",
"Minor": "Nebenversion",
"Patch": "Patch",
"DocumentApprovals": "Prüfungen und Genehmigungen",
"ValidationWorkflow": "Validierungsworkflow",
"ChangeOwner": "Dokumentenbesitzer ändern",
"ChangeOwnerHintBeginning": "Übertragen Sie den Besitz des",
"ChangeOwnerHintEnd": "an eine andere Person.",

View File

@ -21,7 +21,7 @@
"Major": "Major",
"Minor": "Minor",
"Patch": "Patch",
"DocumentApprovals": "Reviews and Approvals",
"ValidationWorkflow": "Validation workflow",
"ChangeOwner": "Change document owner",
"ChangeOwnerHintBeginning": "Transfer ownership of the",
"ChangeOwnerHintEnd": "to another person.",

View File

@ -21,7 +21,7 @@
"Major": "Majeur",
"Minor": "Mineur",
"Patch": "Correctif",
"DocumentApprovals": "Révisions et approbations",
"ValidationWorkflow": "Flux de validation",
"ChangeOwner": "Changer le propriétaire du document",
"ChangeOwnerHintBeginning": "Transférer la propriété du",
"ChangeOwnerHintEnd": "à une autre personne.",

View File

@ -21,7 +21,7 @@
"Major": "Maggiore",
"Minor": "Minore",
"Patch": "Patch",
"DocumentApprovals": "Revisioni e Approvazioni",
"ValidationWorkflow": "Flusso di convalida",
"ChangeOwner": "Cambia proprietario del documento",
"ChangeOwnerHintBeginning": "Trasferisci la proprietà del",
"ChangeOwnerHintEnd": "a un'altra persona.",

View File

@ -21,7 +21,7 @@
"Major": "Мажорная",
"Minor": "Минорная",
"Patch": "Патч",
"DocumentApprovals": "Рецензии и утверждения",
"ValidationWorkflow": "Процесс валидации",
"ChangeOwner": "Изменить владельца документа",
"ChangeOwnerHintBeginning": "Передайте права владельца документа",
"ChangeOwnerHintEnd": "другому лицу.",

View File

@ -21,7 +21,7 @@
"Major": "主要",
"Minor": "次要",
"Patch": "补丁",
"DocumentApprovals": "审查和批准",
"ValidationWorkflow": "验证工作流程",
"ChangeOwner": "更改文档所有者",
"ChangeOwnerHintBeginning": "转移所有权",
"ChangeOwnerHintEnd": "给其他人。",

View File

@ -36,6 +36,7 @@
ControlledDocument,
ControlledDocumentState,
DocumentRequest,
DocumentState,
Project
} from '@hcengineering/controlled-documents'
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
@ -163,10 +164,15 @@
return
}
const hierarchy = client.getHierarchy()
const isReviewed = $controlledDocument.controlledState === ControlledDocumentState.Reviewed
const isApprovalRequest = hierarchy.isDerived(requestClass, documents.class.DocumentApprovalRequest)
const teamPopupData: TeamPopupData = {
controlledDoc: $controlledDocument,
requestClass,
requireSignature: true
requireSignature: !(isReviewed && isApprovalRequest)
}
showPopup(TeamPopup, teamPopupData, 'center')

View File

@ -13,121 +13,68 @@
// limitations under the License.
-->
<script lang="ts">
import { Ref, SortingOrder } from '@hcengineering/core'
import { Label, Scroller } from '@hcengineering/ui'
import { createQuery } from '@hcengineering/presentation'
import documents, { DocumentApprovalRequest, DocumentReviewRequest } from '@hcengineering/controlled-documents'
import { employeeByIdStore } from '@hcengineering/contact-resources'
import { Employee, Person, formatName } from '@hcengineering/contact'
import { employeeByIdStore, personIdByAccountId } from '@hcengineering/contact-resources'
import documents, {
DocumentRequest,
emptyBundle,
extractValidationWorkflow
} from '@hcengineering/controlled-documents'
import { Ref } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { Label, Scroller } from '@hcengineering/ui'
import documentsRes from '../../plugin'
import { $controlledDocument as controlledDocument } from '../../stores/editors/document/editor'
import {
$controlledDocument as controlledDocument,
$documentSnapshots as documentSnapshots
} from '../../stores/editors/document/editor'
import { formatSignatureDate } from '../../utils'
interface Signer {
id?: Ref<Person>
role: 'author' | 'reviewer' | 'approver'
name: string
date: string
let requests: DocumentRequest[] = []
const client = getClient()
const hierarchy = client.getHierarchy()
$: doc = $controlledDocument
$: if (doc) {
void client.findAll(documents.class.DocumentRequest, { attachedTo: doc._id }).then((r) => {
requests = r
})
}
let signers: Signer[] = []
$: workflow = extractValidationWorkflow(
hierarchy,
{
...emptyBundle(),
ControlledDocument: doc ? [doc] : [],
DocumentRequest: requests,
DocumentSnapshot: $documentSnapshots
},
(ref) => $personIdByAccountId.get(ref)
)
let reviewRequest: DocumentReviewRequest
let approvalRequest: DocumentApprovalRequest
const reviewQuery = createQuery()
const approvalQuery = createQuery()
$: if ($controlledDocument !== undefined) {
reviewQuery.query(
documents.class.DocumentReviewRequest,
{
attachedTo: $controlledDocument?._id,
attachedToClass: $controlledDocument?._class
},
(res) => {
reviewRequest = res[0]
},
{
sort: { createdOn: SortingOrder.Descending },
limit: 1
$: state = (doc ? workflow?.get(doc._id) ?? [] : [])[0]
$: signers = (state?.approvals ?? [])
.filter((a) => a.state === 'approved')
.map((a) => {
return {
person: a.person,
role: a.role,
name: getNameByEmployeeId(a.person),
date: a.timestamp ? formatSignatureDate(a.timestamp) : ''
}
)
})
approvalQuery.query(
documents.class.DocumentApprovalRequest,
{
attachedTo: $controlledDocument?._id,
attachedToClass: $controlledDocument?._class
},
(res) => {
approvalRequest = res[0]
},
{
sort: { createdOn: SortingOrder.Descending },
limit: 1
}
)
} else {
reviewQuery.unsubscribe()
approvalQuery.unsubscribe()
}
function getNameByEmployeeId (id: Ref<Person> | undefined): string {
if (id === undefined) return ''
$: if ($controlledDocument !== null) {
const getNameByEmployeeId = (id: Ref<Person> | undefined): string => {
if (id === undefined) {
return ''
}
const employee = $employeeByIdStore.get(id as Ref<Employee>)
const rawName = employee?.name
const employee = $employeeByIdStore.get(id as Ref<Employee>)
const rawName = employee?.name
return rawName !== undefined ? formatName(rawName) : ''
}
const authorSignDate =
reviewRequest !== undefined
? reviewRequest.createdOn
: approvalRequest !== undefined
? approvalRequest.createdOn
: $controlledDocument.createdOn
signers = [
{
id: $controlledDocument.author,
role: 'author',
name: getNameByEmployeeId($controlledDocument.author),
date: authorSignDate !== undefined ? formatSignatureDate(authorSignDate) : ''
}
]
if (reviewRequest !== undefined) {
reviewRequest.approved.forEach((reviewer, idx) => {
const date = reviewRequest.approvedDates?.[idx]
signers.push({
id: reviewer,
role: 'reviewer',
name: getNameByEmployeeId(reviewer),
date: formatSignatureDate(date ?? reviewRequest.modifiedOn)
})
})
}
if (approvalRequest !== undefined) {
approvalRequest.approved.forEach((approver, idx) => {
const date = approvalRequest.approvedDates?.[idx]
signers.push({
id: approver,
role: 'approver',
name: getNameByEmployeeId(approver),
date: formatSignatureDate(date ?? approvalRequest.modifiedOn)
})
})
}
return rawName !== undefined ? formatName(rawName) : ''
}
function getSignerLabel (role: 'author' | 'reviewer' | 'approver'): IntlString {
@ -161,7 +108,7 @@
{signer.name}
</div>
<div class="code">
{signer.id}
{signer.person}
</div>
</div>
</div>

View File

@ -1,96 +1,34 @@
<script lang="ts">
import { slide } from 'svelte/transition'
import documents, { DocumentRequest } from '@hcengineering/controlled-documents'
import chunter from '@hcengineering/chunter'
import { type Person } from '@hcengineering/contact'
import { PersonRefPresenter, personAccountByIdStore } from '@hcengineering/contact-resources'
import { Ref } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { PersonRefPresenter } from '@hcengineering/contact-resources'
import { DocumentValidationState } from '@hcengineering/controlled-documents'
import { Chevron, Label, tooltip } from '@hcengineering/ui'
import { slide } from 'svelte/transition'
import { $documentSnapshots as documentSnapshots } from '../../../stores/editors/document'
import documentsRes from '../../../plugin'
import ApprovedIcon from '../../icons/Approved.svelte'
import RejectedIcon from '../../icons/Rejected.svelte'
import CancelledIcon from '../../icons/Cancelled.svelte'
import RejectedIcon from '../../icons/Rejected.svelte'
import WaitingIcon from '../../icons/Waiting.svelte'
import SignatureInfo from './SignatureInfo.svelte'
export let request: DocumentRequest
export let state: DocumentValidationState
export let initiallyExpanded: boolean = false
interface PersonalApproval {
person?: Ref<Person>
approved: 'approved' | 'rejected' | 'cancelled' | 'waiting'
timestamp?: number
}
const client = getClient()
const hierarchy = client.getHierarchy()
let expanded: boolean = initiallyExpanded
let rejectingMessage: string | undefined
let approvals: PersonalApproval[] = []
$: void getRequestData(request)
$: type = hierarchy.isDerived(request._class, documents.class.DocumentApprovalRequest)
? documents.string.Approval
: documents.string.Review
async function getRequestData (req: DocumentRequest): Promise<void> {
if (req == null) {
return
}
approvals = await getApprovals(req, $personAccountByIdStore)
const rejectingComment = await client.findOne(chunter.class.ChatMessage, {
attachedTo: req?._id,
attachedToClass: req?._class
})
rejectingMessage = rejectingComment?.message
}
async function getApprovals (
req: DocumentRequest,
accountById: typeof $personAccountByIdStore
): Promise<PersonalApproval[]> {
const rejectedBy: PersonalApproval[] =
req.rejected !== undefined
? [
{
person: req.rejected,
approved: 'rejected',
timestamp: req.modifiedOn
}
]
: []
const approvedBy: PersonalApproval[] = req.approved.map((id, idx) => ({
person: id,
approved: 'approved',
timestamp: req.approvedDates?.[idx] ?? req.modifiedOn
}))
const ignoredBy = req.requested
.filter((p) => p !== req?.rejected)
.filter((p) => !(req?.approved as string[]).includes(p))
.map(
(id): PersonalApproval => ({
person: id,
approved: req?.rejected !== undefined ? 'cancelled' : 'waiting'
})
)
return [...approvedBy, ...rejectedBy, ...ignoredBy]
}
$: snapshot = $documentSnapshots
.toReversed()
.find((s) => s.createdOn !== undefined && request.createdOn !== undefined && s.createdOn > request.createdOn)
const dtf = new Intl.DateTimeFormat('default', {
day: 'numeric',
month: 'short'
})
$: snapshot = state?.snapshot
$: approvals = state?.approvals ?? []
const roleString = {
author: documentsRes.string.Author,
reviewer: documentsRes.string.Reviewer,
approver: documentsRes.string.Approver
}
</script>
<button
@ -109,9 +47,7 @@
{/if}
</span>
<span></span>
<span><Label label={type} /></span>
<span></span>
<span class="date">{dtf.format(request?.modifiedOn)}</span>
<span class="date">{dtf.format(state?.modifiedOn)}</span>
<div class="chevron" class:visible={expanded}>
<Chevron outline {expanded} size={'small'} />
</div>
@ -119,37 +55,39 @@
</button>
{#if expanded}
<div class="section" transition:slide|local>
{#each approvals as approver}
{#each approvals as approval}
{@const messages = approval.messages ?? []}
<div class="approver">
<PersonRefPresenter value={approver.person} avatarSize="x-small" />
{#key approver.timestamp}
<!-- For some reason tooltip is not interactive w/o remount -->
<PersonRefPresenter value={approval.person} avatarSize="x-small" />
{#key approval.timestamp}
<span
use:tooltip={approver.timestamp !== undefined
class="flex gap-1"
use:tooltip={approval.timestamp !== undefined
? {
component: SignatureInfo,
props: {
id: approver.person,
timestamp: approver.timestamp
id: approval.person,
timestamp: approval.timestamp
}
}
: undefined}
>
{#if approver.approved === 'approved'}
<span><Label label={roleString[approval.role]} /></span>
{#if approval.state === 'approved'}
<ApprovedIcon size="medium" fill={'var(--theme-docs-accepted-color)'} />
{:else if approver.approved === 'rejected'}
{:else if approval.state === 'rejected'}
<RejectedIcon size="medium" fill={'var(--negative-button-default)'} />
{:else if approver.approved === 'cancelled'}
{:else if approval.state === 'cancelled'}
<CancelledIcon size="medium" />
{:else if approver.approved === 'waiting'}
{:else if approval.state === 'waiting'}
<WaitingIcon size="medium" />
{/if}
</span>
{/key}
</div>
{#if rejectingMessage !== undefined && approver.approved === 'rejected'}
<div class="reject-message">{rejectingMessage}</div>
{/if}
{#each messages as m}
<div class="approval-status-message">{m.message}</div>
{/each}
{/each}
</div>
{/if}
@ -199,7 +137,7 @@
flex-shrink: 0;
border-bottom: 1px solid var(--theme-divider-color);
.reject-message {
.approval-status-message {
font-weight: 400;
padding: 0.625rem 1rem 0 2rem;
}

View File

@ -2,56 +2,71 @@
import documents, {
ControlledDocumentState,
DocumentRequest,
DocumentState
DocumentState,
emptyBundle,
extractValidationWorkflow
} from '@hcengineering/controlled-documents'
import { SortingOrder } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Label, Scroller } from '@hcengineering/ui'
import { $controlledDocument as controlledDocument } from '../../../stores/editors/document'
import document from '../../../plugin'
import RightPanelTabHeader from './RightPanelTabHeader.svelte'
import DocumentApprovalItem from './DocumentApprovalItem.svelte'
import { personIdByAccountId } from '@hcengineering/contact-resources'
import documentsRes from '../../../plugin'
import {
$controlledDocument as controlledDocument,
$documentSnapshots as documentSnapshots
} from '../../../stores/editors/document'
import DocumentApprovalGuideItem from './DocumentApprovalGuideItem.svelte'
import DocumentApprovalItem from './DocumentApprovalItem.svelte'
import RightPanelTabHeader from './RightPanelTabHeader.svelte'
import chunter, { ChatMessage } from '@hcengineering/chunter'
const client = getClient()
const hierarchy = client.getHierarchy()
let requests: DocumentRequest[] = []
let approvals: DocumentRequest[] = []
let messages: ChatMessage[] = []
$: approvals = requests.filter((p) => hierarchy.isDerived(p._class, documents.class.DocumentApprovalRequest))
$: doc = $controlledDocument
const requestQuery = createQuery()
$: if (doc) {
requestQuery.query(documents.class.DocumentRequest, { attachedTo: doc._id }, (r) => {
requests = r
})
}
const query = createQuery()
$: query.query(
documents.class.DocumentRequest,
const messageQuery = createQuery()
$: if (doc) {
messageQuery.query(chunter.class.ChatMessage, { attachedTo: { $in: requests.map((r) => r._id) } }, (r) => {
messages = r
})
}
$: workflow = extractValidationWorkflow(
hierarchy,
{
_class: {
$in: [documents.class.DocumentApprovalRequest, documents.class.DocumentReviewRequest]
},
attachedTo: $controlledDocument?._id
...emptyBundle(),
ControlledDocument: doc ? [doc] : [],
DocumentRequest: requests,
DocumentSnapshot: $documentSnapshots,
ChatMessage: messages
},
(result) => {
requests = result
},
{
sort: { createdOn: SortingOrder.Descending }
}
(ref) => $personIdByAccountId.get(ref)
)
$: hasGuide =
$controlledDocument?.state === DocumentState.Draft &&
($controlledDocument?.controlledState == null ||
![
ControlledDocumentState.Approved,
ControlledDocumentState.Rejected,
ControlledDocumentState.InApproval
].includes($controlledDocument?.controlledState))
$: validationStates = ((doc ? workflow.get(doc._id) : []) ?? []).slice()
const noGuideStates: (ControlledDocumentState | undefined)[] = [
ControlledDocumentState.Approved,
ControlledDocumentState.Rejected,
ControlledDocumentState.InApproval
]
$: hasGuide = doc && doc.state === DocumentState.Draft && !noGuideStates.includes(doc.controlledState)
</script>
<RightPanelTabHeader>
<Label label={document.string.DocumentApprovals} />
<Label label={documentsRes.string.ValidationWorkflow} />
</RightPanelTabHeader>
<Scroller>
@ -60,13 +75,13 @@
<DocumentApprovalGuideItem />
</div>
{/if}
{#if requests.length > 0}
{#each requests as object, idx}
<DocumentApprovalItem request={object} initiallyExpanded={!hasGuide && idx === 0} />
{#if validationStates.length > 0}
{#each validationStates as state, idx}
<DocumentApprovalItem {state} initiallyExpanded={!hasGuide && idx === 0} />
{/each}
{/if}
{#if !hasGuide && approvals.length === 0}
<div class="no-approvals-message"><Label label={document.string.NoApprovalsDescription} /></div>
{#if !hasGuide && requests.length === 0}
<div class="no-approvals-message"><Label label={documentsRes.string.NoApprovalsDescription} /></div>
{/if}
</Scroller>

View File

@ -46,7 +46,7 @@ export default mergeIds(documentsId, documents, {
},
string: {
ID: '' as IntlString,
DocumentApprovals: '' as IntlString,
ValidationWorkflow: '' as IntlString,
Cancel: '' as IntlString,
NewDocumentDialogClose: '' as IntlString,
NewDocumentCloseNote: '' as IntlString,

View File

@ -366,7 +366,7 @@ export const $availableRightPanelTabs = combine($canViewDocumentComments, (canVi
tabs.push({
id: RightPanelTab.APPROVALS,
icon: plugin.icon.Approvals,
showTooltip: { label: plugin.string.DocumentApprovals }
showTooltip: { label: plugin.string.ValidationWorkflow }
})
return tabs

View File

@ -20,11 +20,13 @@ import {
Doc,
DocumentQuery,
DocumentUpdate,
Hierarchy,
getCurrentAccount,
Rank,
Ref,
SortingOrder,
Space,
Timestamp,
toIdMap,
TxOperations
} from '@hcengineering/core'
@ -35,7 +37,7 @@ import documents from './plugin'
import attachment, { Attachment } from '@hcengineering/attachment'
import chunter, { ChatMessage } from '@hcengineering/chunter'
import { Employee } from '@hcengineering/contact'
import { Person, PersonAccount, Employee } from '@hcengineering/contact'
import { makeRank } from '@hcengineering/rank'
import tags, { TagReference } from '@hcengineering/tags'
import {
@ -628,6 +630,139 @@ async function _transferDocuments (
return commit.result
}
export interface DocumentApprovalState {
person?: Ref<Person>
role: 'author' | 'reviewer' | 'approver'
state: 'approved' | 'rejected' | 'cancelled' | 'waiting'
timestamp?: Timestamp
messages?: ChatMessage[]
}
export interface DocumentValidationState {
requests: DocumentRequest[]
snapshot?: DocumentSnapshot
document: ControlledDocument
approvals: DocumentApprovalState[]
modifiedOn?: Timestamp
}
export function extractValidationWorkflow (
hierarchy: Hierarchy,
bundle: DocumentBundle,
accountIdToPerson: (ref: Ref<PersonAccount>) => Ref<Person> | undefined
): Map<Ref<ControlledDocument>, DocumentValidationState[]> {
const result: ReturnType<typeof extractValidationWorkflow> = new Map()
const getApprovalStates = (request: DocumentRequest | undefined): DocumentApprovalState[] => {
if (request === undefined) return []
const role = hierarchy.isDerived(request._class, documents.class.DocumentReviewRequest) ? 'reviewer' : 'approver'
const rejected: DocumentApprovalState[] =
request.rejected !== undefined
? [
{
person: request.rejected,
role,
state: 'rejected',
timestamp: request.modifiedOn
}
]
: []
const approved: DocumentApprovalState[] = request.approved.map((person, idx) => {
return {
person,
role,
state: 'approved',
timestamp: request.approvedDates?.[idx] ?? request.modifiedOn
}
})
const ignored: DocumentApprovalState[] = request.requested
.filter((person) => person !== request.rejected)
.filter((person) => !request.approved.includes(person))
.map((person) => {
return {
person,
role,
state: request.rejected !== undefined ? 'cancelled' : 'waiting'
}
})
const states = [...rejected, ...approved, ...ignored]
const messages = bundle.ChatMessage.filter((m) => m.attachedTo === request._id)
for (const state of states) {
state.messages = messages.filter((m) => accountIdToPerson(m.createdBy as Ref<PersonAccount>) === state.person)
}
return states
}
for (const document of bundle.ControlledDocument) {
const snapshots = bundle.DocumentSnapshot.filter((s) => s.attachedTo === document._id).sort(
(a, b) => (a.createdOn ?? 0) - (b.createdOn ?? 0)
)
const requests = bundle.DocumentRequest.filter((s) => s.attachedTo === document._id).sort(
(a, b) => (a.createdOn ?? 0) - (b.createdOn ?? 0)
)
const states: DocumentValidationState[] = [...snapshots, undefined].map((snapshot) => {
return {
requests: [],
snapshot,
document,
approvals: [],
messages: []
}
})
for (const request of requests) {
const state =
states.find((s) => (s.snapshot?.createdOn ?? 0) > (request.createdOn ?? 0)) ?? states[states.length - 1]
state.requests.push(request)
}
for (const state of states) {
const review = state.requests.findLast((r) =>
hierarchy.isDerived(r._class, documents.class.DocumentReviewRequest)
)
let approval = state.requests.findLast((r) =>
hierarchy.isDerived(r._class, documents.class.DocumentApprovalRequest)
)
if ((approval?.createdOn ?? 0) < (review?.createdOn ?? 0)) approval = undefined
const anchor = review ?? approval
const author =
anchor?.createdBy !== undefined
? accountIdToPerson?.(anchor.createdBy as Ref<PersonAccount>) ?? document.author
: document.author
state.approvals = [
{
person: author,
role: 'author',
state: anchor !== undefined ? 'approved' : 'waiting',
timestamp: anchor !== undefined ? anchor.createdOn ?? document.createdOn : undefined
},
...getApprovalStates(review),
...getApprovalStates(approval)
]
if (state.requests.length > 0) {
state.modifiedOn = Math.max(...state.requests.map((r) => r.modifiedOn ?? 0))
}
}
states.reverse()
result.set(document._id, states)
}
return result
}
/**
* @public
*/

View File

@ -950,7 +950,7 @@ test.describe('QMS. Documents tests', () => {
await test.step('9. Send for Approval', async () => {
await documentContentPage.buttonSendForApproval.click()
await documentContentPage.fillSelectApproversForm([reviewer])
await documentContentPage.fillSelectApproversForm([reviewer], true)
await documentContentPage.checkDocumentStatus(DocumentStatus.IN_APPROVAL)
await documentContentPage.checkDocument({
...documentDetails,

View File

@ -12,15 +12,16 @@ export class DocumentApprovalsPage extends DocumentCommonPage {
async checkRejectApproval (approvalName: string, message: string): Promise<void> {
await expect(
this.page
.locator('div.reject-message', { hasText: message })
.locator('div.approval-status-message', { hasText: message })
.locator('xpath=..')
.locator('div.approver span.ap-label')
.last()
).toHaveText(approvalName)
}
async checkSuccessApproval (approvalName: string): Promise<void> {
await expect(this.page.locator('svg[fill*="accepted"]').locator('xpath=../..').locator('span.ap-label')).toHaveText(
approvalName
)
await expect(
this.page.locator('svg[fill*="accepted"]').locator('xpath=../..').locator('span.ap-label').last()
).toHaveText(approvalName)
}
}

View File

@ -733,14 +733,14 @@ export class DocumentContentPage extends DocumentCommonPage {
await this.confirmSubmission()
}
async fillSelectApproversForm (approvers: Array<string>): Promise<void> {
async fillSelectApproversForm (approvers: Array<string>, skipConfirm: boolean = false): Promise<void> {
await this.buttonAddMembers.click()
for (const approver of approvers) {
await this.selectListItemWithSearch(this.page, approver)
}
await this.textSelectApproversPopup.click({ force: true })
await this.buttonSelectMemberSubmit.click()
await this.confirmSubmission()
if (!skipConfirm) await this.confirmSubmission()
}
async checkCurrentRights (right: DocumentRights): Promise<void> {