Skip to content

Commit b2044c6

Browse files
authored
Merge pull request #6163 from LibreSign/refactor/move-status-validation-to-sequential-service
refactor: move status validation to sequential service
2 parents 9c6cebf + ede3fe8 commit b2044c6

File tree

2 files changed

+112
-3
lines changed

2 files changed

+112
-3
lines changed

lib/Service/RequestSignatureService.php

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ private function associateToSigners(array $data, int $fileId): array {
208208
foreach ($data['users'] as $user) {
209209
$userProvidedOrder = isset($user['signingOrder']) ? (int)$user['signingOrder'] : null;
210210
$signingOrder = $this->sequentialSigningService->determineSigningOrder($userProvidedOrder);
211+
$signerStatus = $user['status'] ?? null;
211212

212213
if (isset($user['identifyMethods'])) {
213214
foreach ($user['identifyMethods'] as $identifyMethod) {
@@ -221,6 +222,7 @@ private function associateToSigners(array $data, int $fileId): array {
221222
fileId: $fileId,
222223
signingOrder: $signingOrder,
223224
fileStatus: $fileStatus,
225+
signerStatus: $signerStatus,
224226
);
225227
}
226228
} else {
@@ -232,6 +234,7 @@ private function associateToSigners(array $data, int $fileId): array {
232234
fileId: $fileId,
233235
signingOrder: $signingOrder,
234236
fileStatus: $fileStatus,
237+
signerStatus: $signerStatus,
235238
);
236239
}
237240
}
@@ -254,6 +257,7 @@ private function associateToSigner(
254257
int $fileId,
255258
int $signingOrder = 0,
256259
?int $fileStatus = null,
260+
?int $signerStatus = null,
257261
): SignRequestEntity {
258262
$identifyMethodsIncances = $this->identifyMethod->getByUserData($identifyMethods);
259263
if (empty($identifyMethodsIncances)) {
@@ -272,8 +276,8 @@ private function associateToSigner(
272276
$currentStatus = $signRequest->getStatusEnum();
273277

274278
if ($isNewSignRequest || $currentStatus === \OCA\Libresign\Enum\SignRequestStatus::DRAFT) {
275-
$initialStatus = $this->determineInitialStatus($signingOrder, $fileStatus);
276-
$signRequest->setStatusEnum($initialStatus);
279+
$desiredStatus = $this->determineInitialStatus($signingOrder, $fileStatus, $signerStatus, $currentStatus, $fileId);
280+
$this->updateStatusIfAllowed($signRequest, $currentStatus, $desiredStatus, $isNewSignRequest);
277281
}
278282

279283
$this->saveSignRequest($signRequest);
@@ -288,13 +292,56 @@ private function associateToSigner(
288292
return $signRequest;
289293
}
290294

291-
private function determineInitialStatus(int $signingOrder, ?int $fileStatus = null): \OCA\Libresign\Enum\SignRequestStatus {
295+
private function updateStatusIfAllowed(
296+
SignRequestEntity $signRequest,
297+
\OCA\Libresign\Enum\SignRequestStatus $currentStatus,
298+
\OCA\Libresign\Enum\SignRequestStatus $desiredStatus,
299+
bool $isNewSignRequest,
300+
): void {
301+
if ($isNewSignRequest || $this->sequentialSigningService->isStatusUpgrade($currentStatus, $desiredStatus)) {
302+
$signRequest->setStatusEnum($desiredStatus);
303+
}
304+
}
305+
306+
private function determineInitialStatus(
307+
int $signingOrder,
308+
?int $fileStatus = null,
309+
?int $signerStatus = null,
310+
?\OCA\Libresign\Enum\SignRequestStatus $currentStatus = null,
311+
?int $fileId = null,
312+
): \OCA\Libresign\Enum\SignRequestStatus {
313+
if ($signerStatus !== null) {
314+
$desiredStatus = \OCA\Libresign\Enum\SignRequestStatus::from($signerStatus);
315+
if ($currentStatus !== null && !$this->sequentialSigningService->isStatusUpgrade($currentStatus, $desiredStatus)) {
316+
return $currentStatus;
317+
}
318+
319+
// Validate status transition based on signing order
320+
if ($fileId !== null) {
321+
return $this->sequentialSigningService->validateStatusByOrder($desiredStatus, $signingOrder, $fileId);
322+
}
323+
324+
return $desiredStatus;
325+
}
326+
292327
// If fileStatus is explicitly DRAFT (0), keep signer as DRAFT
293328
// This allows adding new signers in DRAFT mode even when file is not in DRAFT status
294329
if ($fileStatus === FileEntity::STATUS_DRAFT) {
295330
return \OCA\Libresign\Enum\SignRequestStatus::DRAFT;
296331
}
297332

333+
if ($fileStatus === FileEntity::STATUS_ABLE_TO_SIGN) {
334+
if ($this->sequentialSigningService->isOrderedNumericFlow()) {
335+
// In ordered flow, only first signer (order 1) should be ABLE_TO_SIGN
336+
// Others remain DRAFT until their turn
337+
return $signingOrder === 1
338+
? \OCA\Libresign\Enum\SignRequestStatus::ABLE_TO_SIGN
339+
: \OCA\Libresign\Enum\SignRequestStatus::DRAFT;
340+
}
341+
// In parallel flow, all can sign
342+
return \OCA\Libresign\Enum\SignRequestStatus::ABLE_TO_SIGN;
343+
}
344+
298345
if (!$this->sequentialSigningService->isOrderedNumericFlow()) {
299346
return \OCA\Libresign\Enum\SignRequestStatus::ABLE_TO_SIGN;
300347
}

lib/Service/SequentialSigningService.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,66 @@ private function getSignatureFlow(): SignatureFlow {
170170

171171
return SignatureFlow::from($value);
172172
}
173+
174+
/**
175+
* Check if there are signers with lower signing order that haven't signed yet
176+
*/
177+
public function hasPendingLowerOrderSigners(int $fileId, int $currentOrder): bool {
178+
$signRequests = $this->signRequestMapper->getByFileId($fileId);
179+
180+
foreach ($signRequests as $signRequest) {
181+
$order = $signRequest->getSigningOrder();
182+
$status = $signRequest->getStatusEnum();
183+
184+
// If a signer with lower order hasn't signed yet, return true
185+
if ($order < $currentOrder && $status !== SignRequestStatus::SIGNED) {
186+
return true;
187+
}
188+
}
189+
190+
return false;
191+
}
192+
193+
/**
194+
* Check if changing from currentStatus to desiredStatus is an upgrade (or same level)
195+
* Status hierarchy: DRAFT (0) < ABLE_TO_SIGN (1) < SIGNED (2)
196+
*/
197+
public function isStatusUpgrade(
198+
SignRequestStatus $currentStatus,
199+
SignRequestStatus $desiredStatus,
200+
): bool {
201+
return $desiredStatus->value >= $currentStatus->value;
202+
}
203+
204+
/**
205+
* Validate if a signer can transition to ABLE_TO_SIGN status based on signing order
206+
* In ordered numeric flow, prevents skipping ahead if lower-order signers haven't signed
207+
*
208+
* @param SignRequestStatus $desiredStatus The status being requested
209+
* @param int $signingOrder The signer's order
210+
* @param int $fileId The file ID
211+
* @return SignRequestStatus The validated status (may return DRAFT if validation fails)
212+
*/
213+
public function validateStatusByOrder(
214+
SignRequestStatus $desiredStatus,
215+
int $signingOrder,
216+
int $fileId,
217+
): SignRequestStatus {
218+
// Only validate for ordered numeric flow
219+
if (!$this->isOrderedNumericFlow()) {
220+
return $desiredStatus;
221+
}
222+
223+
// Only validate when trying to set ABLE_TO_SIGN and not the first signer
224+
if ($desiredStatus !== SignRequestStatus::ABLE_TO_SIGN || $signingOrder <= 1) {
225+
return $desiredStatus;
226+
}
227+
228+
// Check if any lower order signers haven't signed yet
229+
if ($this->hasPendingLowerOrderSigners($fileId, $signingOrder)) {
230+
return SignRequestStatus::DRAFT;
231+
}
232+
233+
return $desiredStatus;
234+
}
173235
}

0 commit comments

Comments
 (0)