diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/emailCopy.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/emailCopy.js index 5b3840f79d5c78f1d6abedfb513c89b086f06737..b6f8c7752688e0c4ff709f3a4637e4791eed0d0f 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/emailCopy.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/emailCopy.js @@ -4,6 +4,7 @@ const getEmailCopy = ({ titleText, comments = '', targetUserName = '', + eicName = 'Editor in Chief', }) => { let paragraph let hasLink = true @@ -80,6 +81,11 @@ const getEmailCopy = ({ case 'eqa-manuscript-request-for-approval': paragraph = `Manuscript ID ${customId} has passed peer-review and is now ready for EQA. Please click on the link below to either approve or return the manuscript to the Editor in Chief:` break + case 'eqa-manuscript-published': + hasLink = false + paragraph = `${titleText} has been accepted for publication by ${eicName}. <br/><br/> + Please approve QA screening in MTS system, so that manuscript can be published in Hindawi on hindawi.com` + break default: throw new Error(`The ${emailType} email type is not defined.`) } diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js index c86bbd35a818229fbc82d1992ee27cbe9e91b00e..a84b5541b3768d11c3784e7287a2009882aa7a3c 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js @@ -51,84 +51,87 @@ module.exports = { }, }) - if ( - !hasEQA && - isEditorInChief && - newRecommendation.recommendation === 'publish' - ) { + // the EiC recommends to publish so an email to the EQA needs to be sent, + // one requesting approval or one informing them that the manuscript has been published + if (isEditorInChief && newRecommendation.recommendation === 'publish') { sendEQAEmail({ email, eicName, baseUrl, + titleText, collection, subjectBaseText, }) - } else { - let comments - if (isEditorInChief) { - const eicComments = chain(newRecommendation) - .get('comments') - .find(comm => !comm.public) - .get('content') - .value() - - comments = eicComments - } + } - const hasPeerReview = (collection = {}) => - !isEmpty(collection.handlingEditor) + let comments + if (isEditorInChief) { + const eicComments = chain(newRecommendation) + .get('comments') + .find(comm => !comm.public) + .get('content') + .value() + + comments = eicComments + } + + const hasPeerReview = (collection = {}) => + !isEmpty(collection.handlingEditor) + + if ( + (isEditorInChief || newRecommendation.recommendationType === 'review') && + hasPeerReview(collection) + ) { + // the request came from either the Editor in Chief or a reviewer, so the HE needs to be notified + sendHandlingEditorEmail({ + email, + hasEQA, + baseUrl, + eicName, + comments, + titleText, + targetUserName, + subjectBaseText, + handlingEditor: get(collection, 'handlingEditor', {}), + recommendation: newRecommendation.recommendation, + recommendationType: newRecommendation.recommendationType, + }) + } - if ( - (isEditorInChief || - newRecommendation.recommendationType === 'review') && - hasPeerReview(collection) - ) { - // the request came from either the Editor in Chief or a reviewer, so the HE needs to be notified - sendHandlingEditorEmail({ + if ( + newRecommendation.recommendationType !== 'review' && + newRecommendation.recommendation !== 'return-to-handling-editor' + ) { + if (isEditorInChief || collection.status === 'revisionRequested') { + sendAuthorsEmail({ email, - eicName: await userHelper.getEiCName(), + hasEQA, baseUrl, - comments, titleText, - targetUserName, + parsedFragment, + fragmentAuthors, + isEditorInChief, subjectBaseText, - handlingEditor: get(collection, 'handlingEditor', {}), - recommendation: newRecommendation.recommendation, - recommendationType: newRecommendation.recommendationType, + newRecommendation, + handlingEditorName: get(collection, 'handlingEditor.name', eicName), }) } - if ( - newRecommendation.recommendationType !== 'review' && - newRecommendation.recommendation !== 'return-to-handling-editor' - ) { - if (isEditorInChief || collection.status === 'revisionRequested') { - sendAuthorsEmail({ - email, - baseUrl, - titleText, - parsedFragment, - fragmentAuthors, - isEditorInChief, - subjectBaseText, - newRecommendation, - handlingEditorName: get(collection, 'handlingEditor.name', eicName), - }) - } - - if (hasPeerReview(collection)) { - sendReviewersEmail({ - email, - baseUrl, - UserModel, - titleText, - fragmentHelper, - isEditorInChief, - subjectBaseText, - recommendation: newRecommendation.recommendation, - handlingEditorName: get(collection, 'handlingEditor.name', eicName), - }) + if (hasPeerReview(collection)) { + sendReviewersEmail({ + email, + hasEQA, + baseUrl, + UserModel, + titleText, + fragmentHelper, + isEditorInChief, + subjectBaseText, + recommendation: newRecommendation.recommendation, + handlingEditorName: get(collection, 'handlingEditor.name', eicName), + }) + if (!isEditorInChief) { sendEiCsEmail({ email, baseUrl, @@ -145,6 +148,7 @@ module.exports = { const sendHandlingEditorEmail = ({ email, + hasEQA, eicName, baseUrl, comments, @@ -161,6 +165,13 @@ const sendHandlingEditorEmail = ({ emailType = 'he-review-submitted' } else { email.content.subject = `${subjectBaseText} Decision` + + if (recommendation === 'publish' && !hasEQA) { + // the EiC recommended to publish but the manuscript has not passed through the EQA process + // so the HE should not receive any email + return + } + switch (recommendation) { case 'return-to-handling-editor': emailType = 'he-manuscript-return-with-comments' @@ -199,6 +210,7 @@ const sendHandlingEditorEmail = ({ const sendAuthorsEmail = async ({ email, + hasEQA, baseUrl, titleText, isEditorInChief, @@ -222,6 +234,11 @@ const sendAuthorsEmail = async ({ } if (newRecommendation.recommendation === 'publish') { + if (!hasEQA) { + // the manuscript did not pass through the EQA process + return + } + emailType = 'author-manuscript-published' email.content.subject = `${subjectBaseText} Published` } else { @@ -283,6 +300,7 @@ const sendAuthorsEmail = async ({ const sendReviewersEmail = async ({ email, baseUrl, + hasEQA, eicName, titleText, UserModel, @@ -300,6 +318,12 @@ const sendReviewersEmail = async ({ ? 'submitted-reviewers-after-publish' : 'submitted-reviewers-after-reject' + if (recommendation === 'publish' && !hasEQA) { + // the manuscript has not passed through the EQA process + // so the reviewers should not receive any email + return + } + reviewers = (await fragmentHelper.getReviewers({ UserModel, type: 'submitted', @@ -416,10 +440,35 @@ const sendEQAEmail = ({ email, eicName, baseUrl, + titleText, collection, subjectBaseText, }) => { - const emailType = 'eqa-manuscript-request-for-approval' + let emailType + switch (collection.status) { + case 'accepted': + emailType = 'eqa-manuscript-published' + email.content.subject = `${subjectBaseText} Decision` + break + case 'inQA': + emailType = 'eqa-manuscript-request-for-approval' + email.content.subject = `${subjectBaseText} Request for EQA Approval` + email.content.ctaLink = services.createUrl( + baseUrl, + config.get('eqa-decision.url'), + { + collectionId: collection.id, + customId: collection.customId, + token: collection.technicalChecks.token, + }, + ) + email.content.ctaText = 'MAKE DECISION' + break + default: + throw new Error( + `Cannot send EQA email when collection status is ${collection.status} `, + ) + } email.toUser = { email: editorialAssistantEmail, @@ -428,20 +477,11 @@ const sendEQAEmail = ({ email.content.unsubscribeLink = baseUrl email.content.signatureName = eicName - email.content.subject = `${subjectBaseText} Request for EQA Approval` - email.content.ctaLink = services.createUrl( - baseUrl, - config.get('eqa-decision.url'), - { - collectionId: collection.id, - customId: collection.customId, - token: collection.technicalChecks.token, - }, - ) - email.content.ctaText = 'MAKE DECISION' const { html, text } = email.getBody({ body: getEmailCopy({ + eicName, + titleText, emailType, customId: collection.customId, }), diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js index f67754387a6deef1b7dd8f2d93f31221c7bd52d5..4e7a82189d0996f4b5b61da58807143aecabe348 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js @@ -77,8 +77,8 @@ module.exports = models => async (req, res) => { } const technicalChecks = get(collection, 'technicalChecks', {}) - const { hasEQA } = technicalChecks - + const hasEQA = has(technicalChecks, 'eqa') + // the manuscript has not yet passed through the EQA process so we need to upload it to the FTP server if (isEditorInChief && recommendation === 'publish' && !hasEQA) { const { journal, xmlParser, ftp } = mtsConfig const MTS = new MTSService(journal, xmlParser, s3Config, ftp) @@ -94,7 +94,7 @@ module.exports = models => async (req, res) => { collection.status = 'inQA' set(collection, 'technicalChecks.token', v4()) - set(collection, 'technicalChecks.hasEQA', false) + set(collection, 'technicalChecks.eqa', false) await collection.save() } @@ -105,7 +105,7 @@ module.exports = models => async (req, res) => { if ( isEditorInChief && recommendation === 'return-to-handling-editor' && - has(collection.technicalChecks, 'hasEQA') + hasEQA ) { collection.technicalChecks = {} await collection.save() diff --git a/packages/component-manuscript-manager/src/routes/technicalChecks/patch.js b/packages/component-manuscript-manager/src/routes/technicalChecks/patch.js index 14e68a7cc4b745752a5ddd2d23a733876f0f9a85..453ae84c760f63ce2534a6712ae127eab445f57a 100644 --- a/packages/component-manuscript-manager/src/routes/technicalChecks/patch.js +++ b/packages/component-manuscript-manager/src/routes/technicalChecks/patch.js @@ -37,8 +37,8 @@ module.exports = ({ Collection, Fragment, User }) => async (req, res) => { } delete collection.technicalChecks.token - if (step === TECHNICAL_STEPS.EQA) { - collection.technicalChecks.hasEQA = true + if (step === TECHNICAL_STEPS.EQA && agree) { + collection.technicalChecks.eqa = true } collection.status = setNewStatus(step, agree) await collection.save() diff --git a/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/post.test.js b/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/post.test.js index 686124cb9518df27b9c722fae0f645c764a4d415..5489b35a75f8d70fba86992ab2369029248cd2e7 100644 --- a/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/post.test.js +++ b/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/post.test.js @@ -14,7 +14,7 @@ jest.mock('@pubsweet/component-send-email', () => ({ jest.mock('pubsweet-component-mts-package') const reqBody = { - recommendation: 'accept', + recommendation: 'publish', comments: [ { content: chance.paragraph(), @@ -262,8 +262,8 @@ describe('Post fragments recommendations route handler', () => { const data = JSON.parse(res._getData()) expect(collection.status).toBe('inQA') - expect(collection.technicalChecks).toHaveProperty('hasEQA') - expect(collection.technicalChecks.hasEQA).toBeFalsy() + expect(collection.technicalChecks).toHaveProperty('eqa') + expect(collection.technicalChecks.eqa).toBeFalsy() expect(data.userId).toEqual(editorInChief.id) expect(data.recommendation).toBe('publish') }) @@ -274,7 +274,7 @@ describe('Post fragments recommendations route handler', () => { body.recommendation = 'publish' body.recommendationType = 'editorRecommendation' - collection.technicalChecks.hasEQA = true + collection.technicalChecks.eqa = true const res = await requests.sendRequest({ body, @@ -308,7 +308,7 @@ describe('Post fragments recommendations route handler', () => { delete fragment.invitations delete collection.invitations delete collection.handlingEditor - collection.technicalChecks.hasEQA = false + collection.technicalChecks.eqa = false const res = await requests.sendRequest({ body, @@ -327,7 +327,7 @@ describe('Post fragments recommendations route handler', () => { expect(collection.status).toBe('reviewCompleted') expect(collection.technicalChecks).not.toHaveProperty('token') - expect(collection.technicalChecks).not.toHaveProperty('hasEQA') + expect(collection.technicalChecks).not.toHaveProperty('eqa') expect(data.userId).toEqual(editorInChief.id) expect(data.recommendation).toBe('return-to-handling-editor') diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js b/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js index 9a9281fa22b59b1e76db7dcb8cc8ee00b6170641..f436f0f3d76697ef8d53338fbc53a3b38ef5ff86 100644 --- a/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js +++ b/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js @@ -17,9 +17,9 @@ module.exports = { user, baseUrl, fragment, + reqUserId, UserModel, collection, - reqUser, }) { const fragmentHelper = new Fragment({ fragment }) const { title } = await fragmentHelper.getFragmentData({ @@ -55,7 +55,7 @@ module.exports = { }) } - const requestUser = await UserModel.find(reqUser) + const requestUser = await UserModel.find(reqUserId) if (requestUser.id !== user.id) { sendAddedToManuscriptEmail({ email, diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/post.js b/packages/component-user-manager/src/routes/fragmentsUsers/post.js index fa0b2b2f352cd5a03988d3b537dd86b37cd87370..f4f0608f01eeaf8b849b636963197cd2a80a4495 100644 --- a/packages/component-user-manager/src/routes/fragmentsUsers/post.js +++ b/packages/component-user-manager/src/routes/fragmentsUsers/post.js @@ -97,7 +97,7 @@ module.exports = models => async (req, res) => { baseUrl, fragment, collection, - reqUser: req.user, + reqUserId: req.user, UserModel: models.User, }) @@ -136,6 +136,7 @@ module.exports = models => async (req, res) => { fragment, collection, user: newUser, + reqUserId: req.user, UserModel: models.User, }) diff --git a/packages/xpub-faraday/config/validations.js b/packages/xpub-faraday/config/validations.js index 25f2501d38bb6ffaa7619b683bcb3825e28e0883..cc8177ab05dcc13822008b3720b0ec2f674739d4 100644 --- a/packages/xpub-faraday/config/validations.js +++ b/packages/xpub-faraday/config/validations.js @@ -13,7 +13,7 @@ module.exports = { handlingEditor: Joi.object(), technicalChecks: Joi.object({ token: Joi.string(), - hasEQA: Joi.boolean(), + eqa: Joi.boolean(), }), }, fragment: [