diff --git a/lib/Service/RequestSignatureService.php b/lib/Service/RequestSignatureService.php index 65d821dc46..8baafeb6da 100644 --- a/lib/Service/RequestSignatureService.php +++ b/lib/Service/RequestSignatureService.php @@ -612,13 +612,15 @@ public function validateSigners(array $data): void { } } - public function unassociateToUser(int $fileId, int $signRequestId): void { + public function unassociateToUser(int $fileId, int $signRequestId, bool $notify = true): void { $file = $this->fileMapper->getById($fileId); $signRequest = $this->signRequestMapper->getByFileIdAndSignRequestId($fileId, $signRequestId); $deletedOrder = $signRequest->getSigningOrder(); $groupedIdentifyMethods = $this->identifyMethod->getIdentifyMethodsFromSignRequestId($signRequestId); - $this->dispatchCancellationEventIfNeeded($signRequest, $file, $groupedIdentifyMethods); + if ($notify) { + $this->dispatchCancellationEventIfNeeded($signRequest, $file, $groupedIdentifyMethods); + } try { $this->signRequestMapper->delete($signRequest); @@ -632,7 +634,7 @@ public function unassociateToUser(int $fileId, int $signRequestId): void { ->setFile($file) ->reorderAfterDeletion($file->getId(), $deletedOrder); - $this->propagateSignerDeletionToChildren($file, $signRequest); + $this->propagateSignerDeletionToChildren($file, $groupedIdentifyMethods); $this->revertStatusToDraftIfNoSignersRemain($file); } catch (\Throwable) { } @@ -647,18 +649,20 @@ private function revertStatusToDraftIfNoSignersRemain(FileEntity $file): void { } } - private function propagateSignerDeletionToChildren(FileEntity $envelope, SignRequestEntity $deletedSignRequest): void { + /** + * @param array> $identifyMethods + */ + private function propagateSignerDeletionToChildren(FileEntity $envelope, array $identifyMethods): void { if ($envelope->getNodeType() !== 'envelope') { return; } - $children = $this->fileMapper->getChildrenFiles($envelope->getId()); - - $identifyMethods = $this->identifyMethod->getIdentifyMethodsFromSignRequestId($deletedSignRequest->getId()); if (empty($identifyMethods)) { return; } + $children = $this->fileMapper->getChildrenFiles($envelope->getId()); + foreach ($children as $child) { try { $this->identifyMethod->clearCache(); @@ -668,7 +672,7 @@ private function propagateSignerDeletionToChildren(FileEntity $envelope, SignReq ); if ($childSignRequest->getId()) { - $this->unassociateToUser($child->getId(), $childSignRequest->getId()); + $this->unassociateToUser($child->getId(), $childSignRequest->getId(), notify: false); } } catch (\Throwable) { continue; diff --git a/tests/integration/features/sign/cancel.feature b/tests/integration/features/sign/cancel.feature index 92a8108189..1aea021764 100644 --- a/tests/integration/features/sign/cancel.feature +++ b/tests/integration/features/sign/cancel.feature @@ -12,12 +12,14 @@ Feature: sign-request-cancel And sending "get" to ocs "/apps/libresign/api/v1/file/list?details=1" And fetch field "(FILE_ID)ocs.data.data.0.id" from previous JSON response And fetch field "(SIGN_REQUEST_ID)ocs.data.data.0.signers.0.signRequestId" from previous JSON response + And reset notifications of user "signer1" When sending "delete" to ocs "/apps/libresign/api/v1/sign/file_id//" Then the response should have a status code 200 When as user "signer1" And sending "get" to ocs "/apps/notifications/api/v2/notifications" Then the response should be a JSON array with the following mandatory values | key | value | + | (jq).ocs.data \| length | 1 | | (jq).ocs.data[0].subject | admin canceled the signature request for document | Scenario: Delete draft request does not send cancellation notification @@ -79,3 +81,178 @@ Feature: sign-request-cancel Then the response should be a JSON array with the following mandatory values | key | value | | (jq).ocs.data.data[0].status | 0 | + + Scenario: Delete envelope signer removes only that signer from both documents + Given as user "admin" + And user "signer1" exists + And user "signer2" exists + And run the command "libresign:configure:openssl --cn test" with result code 0 + And sending "post" to ocs "/apps/libresign/api/v1/request-signature" + | files | [{"base64":"","name":"First.pdf"},{"base64":"","name":"Second.pdf"}] | + | signers | [{"identifyMethods":[{"method":"account","value":"signer1"}]},{"identifyMethods":[{"method":"account","value":"signer2"}]}] | + | name | Envelope cancellation | + | status | 0 | + And the response should have a status code 200 + And fetch field "(ENVELOPE_UUID)ocs.data.uuid" from previous JSON response + And sending "patch" to ocs "/apps/libresign/api/v1/request-signature" + | uuid | | + | signers | [{"identifyMethods":[{"method":"account","value":"signer1"}]},{"identifyMethods":[{"method":"account","value":"signer2"}]}] | + | status | 1 | + And the response should have a status code 200 + And sending "get" to ocs "/apps/libresign/api/v1/file/validate/uuid/" + And the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data.nodeType | envelope | + | (jq).ocs.data.files \| length | 2 | + | (jq)[.ocs.data, .ocs.data.files[]] \| map(.status) | [1,1,1] | + | (jq)[.ocs.data, .ocs.data.files[]] \| map([.signers[].identifyMethods[] \| select(.method == "account").value] \| sort) | [["signer1","signer2"],["signer1","signer2"],["signer1","signer2"]] | + And fetch field "(ENVELOPE_ID)ocs.data.id" from previous JSON response + And fetch field '(SIGN_REQUEST_ID)(jq).ocs.data.signers[] | select(any(.identifyMethods[]; .method == "account" and .value == "signer1")) | .signRequestId' from previous JSON response + And fetch field "(FIRST_UUID)ocs.data.files.0.uuid" from previous JSON response + And fetch field "(SECOND_UUID)ocs.data.files.1.uuid" from previous JSON response + And reset notifications of user "signer1" + And reset activity of user "signer1" + And set the email of user "signer1" to "signer1@domain.test" + And activity preference "notify_email_libresign_sign_request_canceled" for user "signer1" is set to "1" + And my inbox is empty + When sending "delete" to ocs "/apps/libresign/api/v1/sign/file_id//" + Then the response should have a status code 200 + When sending "get" to ocs "/apps/libresign/api/v1/file/validate/uuid/" + Then the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data.files \| length | 2 | + | (jq)[.ocs.data, .ocs.data.files[]] \| map(.status) | [1,1,1] | + | (jq)[.ocs.data, .ocs.data.files[]] \| map(.signers \| length) | [1,1,1] | + | (jq)[.ocs.data, .ocs.data.files[]] \| map([.signers[].identifyMethods[] \| select(.method == "account").value]) | [["signer2"],["signer2"],["signer2"]] | + When sending "get" to ocs "/apps/libresign/api/v1/file/validate/uuid/" + Then the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data.signers \| length | 1 | + | (jq)[.ocs.data.signers[].identifyMethods[] \| select(.method == "account").value] | ["signer2"] | + When sending "get" to ocs "/apps/libresign/api/v1/file/validate/uuid/" + Then the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data.signers \| length | 1 | + | (jq)[.ocs.data.signers[].identifyMethods[] \| select(.method == "account").value] | ["signer2"] | + When as user "signer1" + And sending "get" to ocs "/apps/notifications/api/v2/notifications" + Then the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data \| length | 1 | + | (jq).ocs.data[0].subject | admin canceled the signature request for Envelope cancellation | + And there should be 1 emails in my inbox + When sending "get" to ocs "/apps/activity/api/v2/activity/libresign?since=0" + Then the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data \| length | 1 | + + Scenario: Delete last envelope signer reverts both documents to draft without orphan requests + Given as user "admin" + And user "signer1" exists + And run the command "libresign:configure:openssl --cn test" with result code 0 + And sending "post" to ocs "/apps/libresign/api/v1/request-signature" + | files | [{"base64":"","name":"First.pdf"},{"base64":"","name":"Second.pdf"}] | + | signers | [{"identifyMethods":[{"method":"account","value":"signer1"}]}] | + | name | Last envelope signer | + | status | 0 | + And the response should have a status code 200 + And fetch field "(ENVELOPE_UUID)ocs.data.uuid" from previous JSON response + And sending "patch" to ocs "/apps/libresign/api/v1/request-signature" + | uuid | | + | signers | [{"identifyMethods":[{"method":"account","value":"signer1"}]}] | + | status | 1 | + And the response should have a status code 200 + And sending "get" to ocs "/apps/libresign/api/v1/file/validate/uuid/" + And the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data.nodeType | envelope | + | (jq).ocs.data.files \| length | 2 | + | (jq)[.ocs.data, .ocs.data.files[]] \| map(.status) | [1,1,1] | + | (jq)[.ocs.data, .ocs.data.files[]] \| map([.signers[].identifyMethods[] \| select(.method == "account").value]) | [["signer1"],["signer1"],["signer1"]] | + And fetch field "(ENVELOPE_ID)ocs.data.id" from previous JSON response + And fetch field '(SIGN_REQUEST_ID)(jq).ocs.data.signers[] | select(any(.identifyMethods[]; .method == "account" and .value == "signer1")) | .signRequestId' from previous JSON response + And fetch field "(FIRST_UUID)ocs.data.files.0.uuid" from previous JSON response + And fetch field "(SECOND_UUID)ocs.data.files.1.uuid" from previous JSON response + And reset notifications of user "signer1" + And reset activity of user "signer1" + And set the email of user "signer1" to "signer1@domain.test" + And activity preference "notify_email_libresign_sign_request_canceled" for user "signer1" is set to "1" + And my inbox is empty + When sending "delete" to ocs "/apps/libresign/api/v1/sign/file_id//" + Then the response should have a status code 200 + When sending "get" to ocs "/apps/libresign/api/v1/file/validate/uuid/" + Then the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data.files \| length | 2 | + | (jq)[.ocs.data, .ocs.data.files[]] \| map(.signers) | [[],[],[]] | + | (jq)[.ocs.data, .ocs.data.files[]] \| map(.status) | [0,0,0] | + When sending "get" to ocs "/apps/libresign/api/v1/file/validate/uuid/" + Then the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data.signers | [] | + | (jq).ocs.data.status | 0 | + When sending "get" to ocs "/apps/libresign/api/v1/file/validate/uuid/" + Then the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data.signers | [] | + | (jq).ocs.data.status | 0 | + When as user "signer1" + And sending "get" to ocs "/apps/notifications/api/v2/notifications" + Then the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data \| length | 1 | + | (jq).ocs.data[0].subject | admin canceled the signature request for Last envelope signer | + And there should be 1 emails in my inbox + When sending "get" to ocs "/apps/activity/api/v2/activity/libresign?since=0" + Then the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data \| length | 1 | + + Scenario: Directly deleting a child signer still sends its cancellation notification + Given as user "admin" + And user "signer1" exists + And run the command "libresign:configure:openssl --cn test" with result code 0 + And sending "post" to ocs "/apps/libresign/api/v1/request-signature" + | files | [{"base64":"","name":"First.pdf"},{"base64":"","name":"Second.pdf"}] | + | signers | [{"identifyMethods":[{"method":"account","value":"signer1"}]}] | + | name | Direct child cancellation | + | status | 0 | + And the response should have a status code 200 + And fetch field "(ENVELOPE_UUID)ocs.data.uuid" from previous JSON response + And sending "patch" to ocs "/apps/libresign/api/v1/request-signature" + | uuid | | + | signers | [{"identifyMethods":[{"method":"account","value":"signer1"}]}] | + | status | 1 | + And the response should have a status code 200 + And sending "get" to ocs "/apps/libresign/api/v1/file/validate/uuid/" + And the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq)[.ocs.data, .ocs.data.files[]] \| map(.status) | [1,1,1] | + And fetch field "(CHILD_ID)ocs.data.files.0.id" from previous JSON response + And fetch field '(SIGN_REQUEST_ID)(jq).ocs.data.files[0].signers[] | select(any(.identifyMethods[]; .method == "account" and .value == "signer1")) | .signRequestId' from previous JSON response + And reset notifications of user "signer1" + When sending "delete" to ocs "/apps/libresign/api/v1/sign/file_id//" + Then the response should have a status code 200 + When sending "get" to ocs "/apps/libresign/api/v1/file/validate/uuid/" + Then the response should have a status code 200 + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq)[.ocs.data, .ocs.data.files[]] \| map(.signers \| length) | [1,0,1] | + | (jq)[.ocs.data, .ocs.data.files[]] \| map(.status) | [1,0,1] | + When as user "signer1" + And sending "get" to ocs "/apps/notifications/api/v2/notifications" + Then the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data \| length | 1 | + | (jq).ocs.data[0].subject | admin canceled the signature request for First |