diff --git a/lib/Controller/NoticeOfDefaultController.php b/lib/Controller/NoticeOfDefaultController.php index 43dd6d2f8..d3634ca03 100644 --- a/lib/Controller/NoticeOfDefaultController.php +++ b/lib/Controller/NoticeOfDefaultController.php @@ -27,6 +27,7 @@ use DateTimeImmutable; use OCA\Dossiq\Service\NoticeOfDefaultService; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; @@ -41,6 +42,9 @@ * @psalm-suppress UnusedClass */ class NoticeOfDefaultController extends Controller { + + use SearchesObjects; + /** * Constructor. * @@ -150,12 +154,12 @@ public function show(string $id): JSONResponse { } try { - $row = $objectService->find($id, register: $register, schema: $schema); + $row = $this->findObjectAsArray(objectService: $objectService, register: $register, schema: $schema, id: $id); } catch (Throwable $e) { return new JSONResponse(['message' => 'Not found'], Http::STATUS_NOT_FOUND); } - if (is_array($row) === false) { + if ($row === null) { return new JSONResponse(['message' => 'Not found'], Http::STATUS_NOT_FOUND); } diff --git a/lib/Controller/VoorstelBesluitController.php b/lib/Controller/VoorstelBesluitController.php index f11ef160e..9fe769a8b 100644 --- a/lib/Controller/VoorstelBesluitController.php +++ b/lib/Controller/VoorstelBesluitController.php @@ -33,6 +33,7 @@ use OCA\Dossiq\AppInfo\Application; use OCA\Dossiq\Service\AdviceDelegationService; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoAdminRequired; @@ -48,6 +49,9 @@ * Controller for the voorstel besluit-registration delegation node. */ class VoorstelBesluitController extends Controller { + + use SearchesObjects; + /** * Constructor. * @@ -157,7 +161,12 @@ private function loadProposal(string $proposalId): ?array { } try { - $proposal = $objectService->find($proposalId, register: $register, schema: $proposalSchema); + return $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $proposalSchema, + id: $proposalId + ); } catch (Throwable $e) { $this->logger->warning( 'Dossiq: voorstel lookup failed during IDOR gate: ' . $e->getMessage(), @@ -165,12 +174,6 @@ private function loadProposal(string $proposalId): ?array { ); return null; } - - if (is_array($proposal) === true) { - return $proposal; - } - - return null; }//end loadVoorstel() /** diff --git a/lib/Listener/BeroepEscalationListener.php b/lib/Listener/BeroepEscalationListener.php index f2c5c81f5..a17746f74 100644 --- a/lib/Listener/BeroepEscalationListener.php +++ b/lib/Listener/BeroepEscalationListener.php @@ -36,6 +36,7 @@ use DateTimeImmutable; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCA\OpenRegister\Event\ObjectCreatedEvent; use OCA\OpenRegister\Event\ObjectUpdatedEvent; use OCP\EventDispatcher\Event; @@ -52,6 +53,9 @@ * @spec openspec/specs/beroep-escalation/spec.md */ class BeroepEscalationListener implements IEventListener { + + use SearchesObjects; + /** * Status values that close a bezwaar (terminal per bezwaar-lifecycle). * @@ -157,8 +161,13 @@ private function deriveDwingendStatus(Event $event): void { return; } - $objection = $objectService->find($sourceObjectionId, register: $register, schema: $objectionSchema); - if (is_array($objection) === false) { + $objection = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $objectionSchema, + id: $sourceObjectionId + ); + if ($objection === null) { return; } diff --git a/lib/Service/Bezwaar/AdvisoryCommitteeService.php b/lib/Service/Bezwaar/AdvisoryCommitteeService.php index 0413fca8f..f2cd758fc 100644 --- a/lib/Service/Bezwaar/AdvisoryCommitteeService.php +++ b/lib/Service/Bezwaar/AdvisoryCommitteeService.php @@ -45,6 +45,7 @@ use OCA\Dossiq\AppInfo\Application; use OCA\Dossiq\Service\AdviceDelegationService; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCA\Dossiq\Service\Transitions\GuardFailedException; use Psr\Log\LoggerInterface; use RuntimeException; @@ -56,6 +57,8 @@ */ class AdvisoryCommitteeService { + use SearchesObjects; + /** * Allowed advice-request lifecycle states. */ @@ -151,9 +154,15 @@ public function assignToCommittee( ); } - // Validate committee exists and is active. - $committee = $objectService->find($commissieId, register: $register, schema: $committeeSchema); - if (is_array($committee) === false) { + // Validate committee exists and is active. find() returns an + // ObjectEntity (never an array), so go through the array bridge. + $committee = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $committeeSchema, + id: $commissieId + ); + if ($committee === null) { throw new RuntimeException('Committee not found'); } @@ -202,7 +211,7 @@ public function assignToCommittee( ); try { - return $objectService->saveObject(object: $record, register: $register, schema: $requestSchema); + return ($this->saveObjectAsArray(objectService: $objectService, register: $register, schema: $requestSchema, object: $record) ?? $record); } catch (\Throwable $e) { $this->logger->error( 'Dossiq BAC: failed to create advice request: ' . $e->getMessage() @@ -249,8 +258,13 @@ public function transitionAdviceStatus( key: 'bac_advice_request_schema' ); - $current = $objectService->find($requestId, register: $register, schema: $requestSchema); - if (is_array($current) === false) { + $current = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $requestSchema, + id: $requestId + ); + if ($current === null) { throw new RuntimeException('Advice request not found'); } @@ -293,12 +307,13 @@ public function transitionAdviceStatus( ); try { - return $objectService->saveObject( - object: $update, + return ($this->saveObjectAsArray( + objectService: $objectService, register: $register, schema: $requestSchema, + object: $update, uuid: (string)$requestId - ); + ) ?? array_merge($current, $update)); } catch (\Throwable $e) { $this->logger->error( 'Dossiq BAC: failed to transition advice request ' @@ -376,8 +391,13 @@ public function recordCouncilDeviation( ); try { - $current = $objectService->find($requestId, register: $register, schema: $requestSchema); - if (is_array($current) === false) { + $current = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $requestSchema, + id: $requestId + ); + if ($current === null) { return; } diff --git a/lib/Service/Bezwaar/BeroepService.php b/lib/Service/Bezwaar/BeroepService.php index d46a1ab56..7abf3d598 100644 --- a/lib/Service/Bezwaar/BeroepService.php +++ b/lib/Service/Bezwaar/BeroepService.php @@ -58,6 +58,7 @@ use DateTimeImmutable; use OCA\Dossiq\Service\SettingsService; use OCA\Dossiq\Service\StatusTransitionService; +use OCA\Dossiq\Service\Support\SearchesObjects; use Psr\Log\LoggerInterface; use RuntimeException; use Throwable; @@ -68,6 +69,9 @@ * @spec openspec/specs/beroep-escalation/spec.md */ class BeroepService { + + use SearchesObjects; + /* * NO VALID_OUTCOMES HERE — it was the whitelist `recordJudgment()` validated * against, and that method is gone (see the note further down). The list of @@ -178,8 +182,13 @@ public function register( ); } - $contested = $objectService->find($contestedDecisionId, register: $register, schema: $appealDecisionSchema); - if (is_array($contested) === false) { + $contested = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $appealDecisionSchema, + id: $contestedDecisionId + ); + if ($contested === null) { throw new RuntimeException('Contested beslissing not found'); } @@ -207,7 +216,7 @@ public function register( ); try { - return $objectService->saveObject(object: $record, register: $register, schema: $appealSchema); + return ($this->saveObjectAsArray(objectService: $objectService, register: $register, schema: $appealSchema, object: $record) ?? $record); } catch (Throwable $e) { $this->logger->error( 'Dossiq beroep: failed to register: ' . $e->getMessage() @@ -249,8 +258,13 @@ public function addFileInspectionRequest( key: 'beroep_schema' ); - $current = $objectService->find($appealId, register: $register, schema: $appealSchema); - if (is_array($current) === false) { + $current = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $appealSchema, + id: $appealId + ); + if ($current === null) { throw new RuntimeException('Beroep not found'); } @@ -271,12 +285,13 @@ public function addFileInspectionRequest( $requests[] = $entry; try { - return $objectService->saveObject( - object: ['fileInspectionRequests' => $requests], + return ($this->saveObjectAsArray( + objectService: $objectService, register: $register, schema: $appealSchema, + object: ['fileInspectionRequests' => $requests], uuid: (string)$appealId - ); + ) ?? array_merge($current, ['fileInspectionRequests' => $requests])); } catch (Throwable $e) { $this->logger->error( 'Dossiq beroep: failed to add file-inspection request: ' @@ -343,8 +358,13 @@ public function executeCascade(string $appealId, string $action): array { key: 'bezwaar_schema' ); - $current = $objectService->find($appealId, register: $register, schema: $appealSchema); - if (is_array($current) === false) { + $current = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $appealSchema, + id: $appealId + ); + if ($current === null) { throw new RuntimeException('Beroep not found'); } @@ -382,12 +402,13 @@ public function executeCascade(string $appealId, string $action): array { } try { - return $objectService->saveObject( - object: $patch, + return ($this->saveObjectAsArray( + objectService: $objectService, register: $register, schema: $appealSchema, + object: $patch, uuid: (string)$appealId - ); + ) ?? array_merge($current, $patch)); } catch (Throwable $e) { $this->logger->error( 'Dossiq beroep: failed to persist cascade: ' . $e->getMessage() @@ -423,8 +444,13 @@ private function reopenSourceObjectionCase( return null; } - $sourceObjection = $objectService->find($sourceObjectionId, register: $register, schema: $objectionSchema); - if (is_array($sourceObjection) === false) { + $sourceObjection = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $objectionSchema, + id: $sourceObjectionId + ); + if ($sourceObjection === null) { return null; } diff --git a/lib/Service/Bezwaar/BezwaarCreationHook.php b/lib/Service/Bezwaar/BezwaarCreationHook.php index 055242acb..f9a860a49 100644 --- a/lib/Service/Bezwaar/BezwaarCreationHook.php +++ b/lib/Service/Bezwaar/BezwaarCreationHook.php @@ -40,6 +40,7 @@ namespace OCA\Dossiq\Service\Bezwaar; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCP\IUserSession; use Psr\Log\LoggerInterface; use RuntimeException; @@ -51,6 +52,9 @@ * @spec openspec/specs/bezwaar-beroep-workflow/spec.md */ class BezwaarCreationHook { + + use SearchesObjects; + /** * Constructor. * @@ -103,12 +107,13 @@ public function onBezwaarCreated( $schemas = $this->resolveSchemas(); - $decision = $objectService->find( - $contestedDecisionId, + $decision = $this->findObjectAsArray( + objectService: $objectService, register: $schemas['register'], - schema: $schemas['decision'] + schema: $schemas['decision'], + id: $contestedDecisionId ); - if (is_array($decision) === false) { + if ($decision === null) { throw new RuntimeException('Contested decision not found'); } @@ -220,12 +225,13 @@ private function linkRelatedCase( string $objectionCaseId, string $relatedCaseId, ): void { - $case = $objectService->find( - $objectionCaseId, + $case = $this->findObjectAsArray( + objectService: $objectService, register: $register, - schema: $caseSchema + schema: $caseSchema, + id: $objectionCaseId ); - if (is_array($case) === false) { + if ($case === null) { throw new RuntimeException('Bezwaar case not found'); } diff --git a/lib/Service/Bezwaar/DecisionService.php b/lib/Service/Bezwaar/DecisionService.php index 0aad42478..ec32a2701 100644 --- a/lib/Service/Bezwaar/DecisionService.php +++ b/lib/Service/Bezwaar/DecisionService.php @@ -57,6 +57,7 @@ use OCA\Dossiq\Service\BezwaarDecisionDelegationService; use OCA\Dossiq\Service\SettingsService; use OCA\Dossiq\Service\StatusTransitionService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCP\IUserSession; use Psr\Log\LoggerInterface; use RuntimeException; @@ -69,6 +70,9 @@ * @spec openspec/specs/bezwaar-decision/spec.md */ class DecisionService { + + use SearchesObjects; + /** * Canonical Awb art. 7:11 disposition values (REQ-BD-2). * @@ -170,11 +174,12 @@ public function draft(string $objectionId, array $payload): array { unset($record['publishedAt'], $record['notifiedRecipients']); try { - return $objectService->saveObject( - object: $record, + return ($this->saveObjectAsArray( + objectService: $objectService, register: $register, - schema: $decisionSchema - ); + schema: $decisionSchema, + object: $record + ) ?? $record); } catch (Throwable $e) { $this->logger->error( 'Dossiq bezwaar-decision: failed to draft: ' . $e->getMessage() @@ -224,8 +229,8 @@ public function publish(string $decisionId): array { ); } - $current = $objectService->find($decisionId, register: $register, schema: $decisionSchema); - if (is_array($current) === false) { + $current = $this->findObjectAsArray(objectService: $objectService, register: $register, schema: $decisionSchema, id: $decisionId); + if ($current === null) { throw new RuntimeException('BezwaarDecision not found'); } @@ -286,12 +291,13 @@ public function publish(string $decisionId): array { } try { - $saved = $objectService->saveObject( - object: $patch, + $saved = ($this->saveObjectAsArray( + objectService: $objectService, register: $register, schema: $decisionSchema, + object: $patch, uuid: (string)$decisionId - ); + ) ?? array_merge($current, $patch)); } catch (Throwable $e) { $this->logger->error( 'Dossiq bezwaar-decision: failed to persist decisionRef: ' @@ -332,8 +338,13 @@ public function applyToBezwaar(string $objectionId, string $decisionId): void { return; } - $objection = $objectService->find($objectionId, register: $register, schema: $objectionSchema); - if (is_array($objection) === false) { + $objection = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $objectionSchema, + id: $objectionId + ); + if ($objection === null) { return; } diff --git a/lib/Service/Bezwaar/HearingService.php b/lib/Service/Bezwaar/HearingService.php index 6a2542ce3..d72d84cfb 100644 --- a/lib/Service/Bezwaar/HearingService.php +++ b/lib/Service/Bezwaar/HearingService.php @@ -210,7 +210,7 @@ public function schedule( ); try { - return $objectService->saveObject(object: $record, register: $register, schema: $schema); + return ($this->saveObjectAsArray(objectService: $objectService, register: $register, schema: $schema, object: $record) ?? $record); } catch (\Throwable $e) { $this->logger->error( 'Dossiq hearing: failed to schedule hearing: ' . $e->getMessage() @@ -289,7 +289,7 @@ public function waive( ); try { - return $objectService->saveObject(object: $record, register: $register, schema: $schema); + return ($this->saveObjectAsArray(objectService: $objectService, register: $register, schema: $schema, object: $record) ?? $record); } catch (\Throwable $e) { $this->logger->error( 'Dossiq hearing: failed to record waiver: ' . $e->getMessage() @@ -358,8 +358,13 @@ public function recordAttendance( key: 'hearing_session_schema' ); - $current = $objectService->find($sessionId, register: $register, schema: $schema); - if (is_array($current) === false) { + $current = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $schema, + id: $sessionId + ); + if ($current === null) { throw new RuntimeException('Hearing session not found'); } @@ -397,12 +402,13 @@ public function recordAttendance( ]; try { - return $objectService->saveObject( - object: $update, + return ($this->saveObjectAsArray( + objectService: $objectService, register: $register, schema: $schema, + object: $update, uuid: (string)$sessionId - ); + ) ?? array_merge($current, $update)); } catch (\Throwable $e) { $this->logger->error( 'Dossiq hearing: failed to record attendance: ' . $e->getMessage() @@ -441,8 +447,13 @@ public function addMinutes( key: 'hearing_session_schema' ); - $current = $objectService->find($sessionId, register: $register, schema: $schema); - if (is_array($current) === false) { + $current = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $schema, + id: $sessionId + ); + if ($current === null) { throw new RuntimeException('Hearing session not found'); } @@ -489,12 +500,13 @@ public function addMinutes( ); try { - return $objectService->saveObject( - object: $update, + return ($this->saveObjectAsArray( + objectService: $objectService, register: $register, schema: $schema, + object: $update, uuid: (string)$sessionId - ); + ) ?? array_merge($current, $update)); } catch (\Throwable $e) { $this->logger->error( 'Dossiq hearing: failed to add minutes: ' . $e->getMessage() @@ -606,8 +618,13 @@ private function resolveCaseIdFromObjection(string $objectionId): string { } try { - $objection = $objectService->find($objectionId, register: $register, schema: $objectionSchema); - if (is_array($objection) === true) { + $objection = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $objectionSchema, + id: $objectionId + ); + if ($objection !== null) { $candidate = (string)($objection['case'] ?? ''); if ($candidate !== '') { return $candidate; diff --git a/lib/Service/Bezwaar/PanelIndependenceChecker.php b/lib/Service/Bezwaar/PanelIndependenceChecker.php index cc590934e..c817302c9 100644 --- a/lib/Service/Bezwaar/PanelIndependenceChecker.php +++ b/lib/Service/Bezwaar/PanelIndependenceChecker.php @@ -181,8 +181,13 @@ private function resolveContestedDecisionAuthor( // to treating the input as the case id. $caseId = $objectionId; if ($legacyObjSchema !== '') { - $bezwaar = $objectService->find($objectionId, register: $register, schema: $legacyObjSchema); - if (is_array($bezwaar) === true) { + $bezwaar = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $legacyObjSchema, + id: $objectionId + ); + if ($bezwaar !== null) { $caseId = (string)($bezwaar['case'] ?? $objectionId); } } @@ -207,8 +212,13 @@ private function resolveContestedDecisionAuthor( return ''; } - $decision = $objectService->find($contestedId, register: $register, schema: $decisionSchema); - if (is_array($decision) === false) { + $decision = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $decisionSchema, + id: $contestedId + ); + if ($decision === null) { return ''; } diff --git a/lib/Service/DossierCompiler.php b/lib/Service/DossierCompiler.php index 3ac0b999a..f0a72f5dd 100644 --- a/lib/Service/DossierCompiler.php +++ b/lib/Service/DossierCompiler.php @@ -38,6 +38,7 @@ namespace OCA\Dossiq\Service; +use OCA\Dossiq\Service\Support\SearchesObjects; use Psr\Log\LoggerInterface; use RuntimeException; @@ -47,6 +48,9 @@ * @spec openspec/specs/bezwaar-beroep-workflow/spec.md */ class DossierCompiler { + + use SearchesObjects; + /** * AWB-conventional ordering of dossier document categories. Keys are * normalised (lower-case) document-type fragments; the value is the @@ -123,8 +127,13 @@ public function compile(string $caseId): array { throw new RuntimeException('Case or document schema is not configured'); } - $case = $objectService->find($caseId, register: $register, schema: $caseSchema); - if (is_array($case) === false) { + $case = $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $caseSchema, + id: $caseId + ); + if ($case === null) { throw new RuntimeException('Case not found'); } diff --git a/lib/Service/Email/CaseEmailRepository.php b/lib/Service/Email/CaseEmailRepository.php index 25503efc9..2e1bc7d1b 100644 --- a/lib/Service/Email/CaseEmailRepository.php +++ b/lib/Service/Email/CaseEmailRepository.php @@ -86,12 +86,12 @@ public function findTemplate(string $templateId): ?array { return null; } - $result = $objectService->find($templateId, register: $register, schema: $schema); - if (is_array($result) === true) { - return $result; - } - - return null; + return $this->findObjectAsArray( + objectService: $objectService, + register: $register, + schema: $schema, + id: $templateId + ); }//end findTemplate() /** diff --git a/lib/Service/InspectionChecklistService.php b/lib/Service/InspectionChecklistService.php index 20c13b9fb..0ee9d22cf 100644 --- a/lib/Service/InspectionChecklistService.php +++ b/lib/Service/InspectionChecklistService.php @@ -374,19 +374,19 @@ private function assertItemPhotoRequirement( mixed $itemRef, ): void { try { - $item = $objectService->find( - $itemRef, + // The find() call returns an ObjectEntity whose data lives in + // protected properties, so get_object_vars() from out here read + // an EMPTY array and the photo requirement never fired. The + // array bridge goes through jsonSerialize(), which exposes the + // real fields. + $item = $this->findObjectAsArray( + objectService: $objectService, register: $register, - schema: 'checklistItem' + schema: 'checklistItem', + id: (string)$itemRef ); - // The find() call may return an OpenRegister entity or an - // array; normalise to an array so fotoRequired is readable. - if (is_object($item) === true) { - $item = get_object_vars(object: $item); - } - - if (is_array($item) === true && ($item['photoRequired'] ?? false) === true) { + if ($item !== null && ($item['photoRequired'] ?? false) === true) { throw new RuntimeException( 'Photo required for non-conformant checklist item ' . $itemRef ); diff --git a/lib/Service/Subsidie/BeschikkingService.php b/lib/Service/Subsidie/BeschikkingService.php index 13837c659..aa6b0742e 100644 --- a/lib/Service/Subsidie/BeschikkingService.php +++ b/lib/Service/Subsidie/BeschikkingService.php @@ -31,6 +31,7 @@ use DateInterval; use DateTimeImmutable; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\IUserSession; use Psr\Log\LoggerInterface; @@ -44,6 +45,9 @@ * @spec openspec/changes/subsidieverlening-keten/specs.md */ class BeschikkingService { + + use SearchesObjects; + /** * Bezwaartermijn (objection window) in weeks (AWB 6:7). */ @@ -134,7 +138,7 @@ public function createDraft(string $requestId, array $payload, int $sequence): a unset($record['signedBy'], $record['signedOn'], $record['publicationDate']); try { - return $objectService->saveObject(object: $record, register: $register, schema: $schema); + return ($this->saveObjectAsArray(objectService: $objectService, register: $register, schema: $schema, object: $record) ?? $record); } catch (Throwable $e) { $this->logger->error('Dossiq subsidie: createDraft beschikking failed: ' . $e->getMessage()); throw new OCSBadRequestException('Kon beschikking niet aanmaken'); @@ -168,7 +172,13 @@ public function sign(string $decisionId): array { ]; try { - return $objectService->saveObject(object: $patch, register: $register, schema: $schema, uuid: (string)$decisionId); + return ($this->saveObjectAsArray( + objectService: $objectService, + register: $register, + schema: $schema, + object: $patch, + uuid: (string)$decisionId + ) ?? $patch); } catch (Throwable $e) { $this->logger->error('Dossiq subsidie: sign beschikking failed: ' . $e->getMessage()); throw new OCSBadRequestException('Kon beschikking niet ondertekenen'); @@ -196,13 +206,13 @@ public function publish(string $decisionId): array { // message no caller could ever receive. sign(), directly below, has // always wrapped its call and answers a clean 400; this now matches it. try { - $current = $objectService->find($decisionId, register: $register, schema: $schema); + $current = $this->findObjectAsArray(objectService: $objectService, register: $register, schema: $schema, id: $decisionId); } catch (Throwable $e) { $this->logger->error('Dossiq subsidie: publish beschikking lookup failed: ' . $e->getMessage()); throw new OCSBadRequestException('Beschikking niet gevonden'); } - if (is_array($current) === false) { + if ($current === null) { throw new OCSBadRequestException('Beschikking niet gevonden'); } @@ -218,7 +228,13 @@ public function publish(string $decisionId): array { ]; try { - return $objectService->saveObject(object: $patch, register: $register, schema: $schema, uuid: (string)$decisionId); + return ($this->saveObjectAsArray( + objectService: $objectService, + register: $register, + schema: $schema, + object: $patch, + uuid: (string)$decisionId + ) ?? array_merge($current, $patch)); } catch (Throwable $e) { $this->logger->error('Dossiq subsidie: publish beschikking failed: ' . $e->getMessage()); throw new OCSBadRequestException('Kon beschikking niet publiceren'); diff --git a/lib/Service/Subsidie/BewijsstukService.php b/lib/Service/Subsidie/BewijsstukService.php index 3e7b342fe..992c05986 100644 --- a/lib/Service/Subsidie/BewijsstukService.php +++ b/lib/Service/Subsidie/BewijsstukService.php @@ -32,6 +32,7 @@ use DateInterval; use DateTimeImmutable; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCP\AppFramework\OCS\OCSBadRequestException; use Psr\Log\LoggerInterface; use Throwable; @@ -44,6 +45,9 @@ * @spec openspec/changes/subsidieverlening-keten/specs.md */ class BewijsstukService { + + use SearchesObjects; + /** * Allowed bewijsstuk types per source phase (REQ-SUB-007). * @@ -188,7 +192,7 @@ public function create(array $payload, ?string $contents = null, ?int $regelingR } try { - return $objectService->saveObject(object: $record, register: $register, schema: $schema); + return ($this->saveObjectAsArray(objectService: $objectService, register: $register, schema: $schema, object: $record) ?? $record); } catch (Throwable $e) { $this->logger->error('Dossiq subsidie: bewijsstuk create failed: ' . $e->getMessage()); throw new OCSBadRequestException('Kon bewijsstuk niet opslaan'); diff --git a/lib/Service/Subsidie/SubsidieService.php b/lib/Service/Subsidie/SubsidieService.php index 3bd2710c3..b378400a9 100644 --- a/lib/Service/Subsidie/SubsidieService.php +++ b/lib/Service/Subsidie/SubsidieService.php @@ -33,6 +33,7 @@ use DateInterval; use DateTimeImmutable; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCP\AppFramework\OCS\OCSBadRequestException; use Psr\Log\LoggerInterface; use Throwable; @@ -49,6 +50,9 @@ * @spec openspec/changes/subsidieverlening-keten/specs.md */ class SubsidieService { + + use SearchesObjects; + /** * Canonical aanvraag status values. * @@ -240,7 +244,7 @@ public function createAanvraag(array $payload, int $termWeken = self::DEFAULT_AA } try { - return $objectService->saveObject(object: $record, register: $register, schema: $schema); + return ($this->saveObjectAsArray(objectService: $objectService, register: $register, schema: $schema, object: $record) ?? $record); } catch (Throwable $e) { $this->logger->error('Dossiq subsidie: createAanvraag failed: ' . $e->getMessage()); throw new OCSBadRequestException('Kon subsidieaanvraag niet aanmaken'); @@ -266,8 +270,8 @@ public function transitionAanvraag(string $id, string $toStatus): array { throw new OCSBadRequestException('Onbekende status: ' . $toStatus); } - $current = $objectService->find($id, register: $register, schema: $schema); - if (is_array($current) === false) { + $current = $this->findObjectAsArray(objectService: $objectService, register: $register, schema: $schema, id: (string)$id); + if ($current === null) { throw new OCSBadRequestException('Subsidieaanvraag niet gevonden'); } @@ -277,7 +281,13 @@ public function transitionAanvraag(string $id, string $toStatus): array { } try { - return $objectService->saveObject(object: ['status' => $toStatus], register: $register, schema: $schema, uuid: (string)$id); + return ($this->saveObjectAsArray( + objectService: $objectService, + register: $register, + schema: $schema, + object: ['status' => $toStatus], + uuid: (string)$id + ) ?? array_merge($current, ['status' => $toStatus])); } catch (Throwable $e) { $this->logger->error('Dossiq subsidie: transitionAanvraag failed: ' . $e->getMessage()); throw new OCSBadRequestException('Kon status niet bijwerken'); diff --git a/lib/Service/Subsidie/TerugvorderingService.php b/lib/Service/Subsidie/TerugvorderingService.php index ac67e068a..0e9a310a5 100644 --- a/lib/Service/Subsidie/TerugvorderingService.php +++ b/lib/Service/Subsidie/TerugvorderingService.php @@ -31,6 +31,7 @@ use DateInterval; use DateTimeImmutable; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCP\AppFramework\OCS\OCSBadRequestException; use Psr\Log\LoggerInterface; use Throwable; @@ -43,6 +44,9 @@ * @spec openspec/changes/subsidieverlening-keten/specs.md */ class TerugvorderingService { + + use SearchesObjects; + /** * Default bezwaartermijn (objection window) in weeks (AWB 6:7). */ @@ -177,7 +181,7 @@ public function createClawbackCase(string $uitvoeringId, float $amount, ?DateTim ]; try { - return $objectService->saveObject(object: $record, register: $register, schema: $schema); + return ($this->saveObjectAsArray(objectService: $objectService, register: $register, schema: $schema, object: $record) ?? $record); } catch (Throwable $e) { $this->logger->error('Dossiq subsidie: createClawbackCase failed: ' . $e->getMessage()); throw new OCSBadRequestException('Kon terugvordering niet aanmaken'); diff --git a/lib/Service/Subsidie/TussenrapportageService.php b/lib/Service/Subsidie/TussenrapportageService.php index 26ec2eb48..6525cb838 100644 --- a/lib/Service/Subsidie/TussenrapportageService.php +++ b/lib/Service/Subsidie/TussenrapportageService.php @@ -33,6 +33,7 @@ use DateInterval; use DateTimeImmutable; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\IUserSession; use Psr\Log\LoggerInterface; @@ -46,6 +47,9 @@ * @spec openspec/changes/subsidieverlening-keten/specs.md */ class TussenrapportageService { + + use SearchesObjects; + /** * Valid report status values. * @@ -143,7 +147,7 @@ public function createExpected(string $uitvoeringId, array $payload): array { ); try { - return $objectService->saveObject(object: $record, register: $register, schema: $schema); + return ($this->saveObjectAsArray(objectService: $objectService, register: $register, schema: $schema, object: $record) ?? $record); } catch (Throwable $e) { $this->logger->error('Dossiq subsidie: createExpected tussenrapportage failed: ' . $e->getMessage()); throw new OCSBadRequestException('Kon tussenrapportage niet aanmaken'); @@ -188,7 +192,13 @@ public function approveReport(string $reportId, ?string $beoordelingsoordeel = n } try { - return $objectService->saveObject(object: $patch, register: $register, schema: $schema, uuid: (string)$reportId); + return ($this->saveObjectAsArray( + objectService: $objectService, + register: $register, + schema: $schema, + object: $patch, + uuid: (string)$reportId + ) ?? $patch); } catch (Throwable $e) { $this->logger->error('Dossiq subsidie: approveReport failed: ' . $e->getMessage()); throw new OCSBadRequestException('Kon tussenrapportage niet goedkeuren'); @@ -231,7 +241,13 @@ public function partialApprove(string $reportId, string $correctionRequest, int ]; try { - return $objectService->saveObject(object: $patch, register: $register, schema: $schema, uuid: (string)$reportId); + return ($this->saveObjectAsArray( + objectService: $objectService, + register: $register, + schema: $schema, + object: $patch, + uuid: (string)$reportId + ) ?? $patch); } catch (Throwable $e) { $this->logger->error('Dossiq subsidie: partialApprove failed: ' . $e->getMessage()); throw new OCSBadRequestException('Kon tussenrapportage niet gedeeltelijk goedkeuren'); diff --git a/lib/Service/Subsidie/VaststellingService.php b/lib/Service/Subsidie/VaststellingService.php index 978c3f90e..9c9b9c1bc 100644 --- a/lib/Service/Subsidie/VaststellingService.php +++ b/lib/Service/Subsidie/VaststellingService.php @@ -36,6 +36,7 @@ namespace OCA\Dossiq\Service\Subsidie; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\Support\SearchesObjects; use OCP\AppFramework\OCS\OCSBadRequestException; use Psr\Log\LoggerInterface; use Throwable; @@ -48,6 +49,9 @@ * @spec openspec/changes/subsidieverlening-keten/specs.md */ class VaststellingService { + + use SearchesObjects; + /** * Constructor. * @@ -163,12 +167,18 @@ public function finalize( ]; try { - $current = $objectService->find($determinationId, register: $register, schema: $schema); - if (is_array($current) === false) { + $current = $this->findObjectAsArray(objectService: $objectService, register: $register, schema: $schema, id: $determinationId); + if ($current === null) { throw new OCSBadRequestException('Vaststelling niet gevonden'); } - $saved = $objectService->saveObject(object: $patch, register: $register, schema: $schema, uuid: (string)$determinationId); + $saved = ($this->saveObjectAsArray( + objectService: $objectService, + register: $register, + schema: $schema, + object: $patch, + uuid: (string)$determinationId + ) ?? array_merge($current, $patch)); } catch (OCSBadRequestException $e) { throw $e; } catch (Throwable $e) { diff --git a/tests/Unit/Controller/VoorstelBesluitControllerContractTest.php b/tests/Unit/Controller/VoorstelBesluitControllerContractTest.php index 840b5da94..481cd9d5c 100644 --- a/tests/Unit/Controller/VoorstelBesluitControllerContractTest.php +++ b/tests/Unit/Controller/VoorstelBesluitControllerContractTest.php @@ -43,6 +43,8 @@ use OCA\Dossiq\Controller\VoorstelBesluitController; use OCA\Dossiq\Service\AdviceDelegationService; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Tests\Unit\Service\FakeStoredObject; +use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\IGroupManager; use OCP\IRequest; @@ -63,15 +65,17 @@ */ interface VoorstelBesluitControllerContractObjectService { /** - * Find a single object. + * Find a single object — real ObjectService argument order. * * @param int|string $id The object id. - * @param string $register The register slug. - * @param string $schema The schema slug. + * @param array|null $_extend Relations to expand. + * @param bool $files Include file metadata. + * @param string|int|null $register The register slug. + * @param string|int|null $schema The schema slug. * - * @return mixed The object. + * @return mixed The object (an entity-shaped object, never an array). */ - public function find(int|string $id, string $register = '', string $schema = ''): mixed; + public function find(int|string $id, ?array $_extend = [], bool $files = false, string|int|null $register = null, string|int|null $schema = null): mixed; }//end interface /** @@ -430,13 +434,24 @@ private function signIn(string $uid): void { /** * Install an ObjectService that resolves the voorstel to the given record. * - * @param array|null $proposal The voorstel record, or null. + * Pinned to the real contract: find() answers an entity-shaped object + * (never an array) and THROWS DoesNotExistException for a missing + * voorstel — the shapes the live ObjectService actually produces. + * + * @param array|null $proposal The voorstel record, or null for a miss. * * @return void */ private function withProposal(?array $proposal): void { $objectService = $this->createMock(VoorstelBesluitControllerContractObjectService::class); - $objectService->method('find')->willReturn($proposal); + if ($proposal === null) { + $objectService->method('find')->willThrowException( + new DoesNotExistException('Object does not exist') + ); + } else { + $objectService->method('find')->willReturn(new FakeStoredObject($proposal)); + } + $this->settingsService->method('getObjectService')->willReturn($objectService); }//end withProposal() diff --git a/tests/Unit/Service/Bezwaar/BeroepServiceOrContractTest.php b/tests/Unit/Service/Bezwaar/BeroepServiceOrContractTest.php new file mode 100644 index 000000000..0678dec4b --- /dev/null +++ b/tests/Unit/Service/Bezwaar/BeroepServiceOrContractTest.php @@ -0,0 +1,203 @@ + + * @copyright 2026 Conduction B.V. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * SPDX-License-Identifier: EUPL-1.2 + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * + * @version GIT: + * + * @link https://conduction.nl + */ + +declare(strict_types=1); + +namespace OCA\Dossiq\Tests\Unit\Service\Bezwaar; + +use OCA\Dossiq\Service\Bezwaar\BeroepService; +use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Service\StatusTransitionService; +use OCA\Dossiq\Tests\Unit\Service\FakeTermijnStore; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use RuntimeException; + +/** + * Unit tests for BeroepService against the honest OpenRegister fake. + * + * @covers \OCA\Dossiq\Service\Bezwaar\BeroepService + */ +class BeroepServiceOrContractTest extends TestCase { + + /** + * The entity-shaped in-memory ObjectService fake. + * + * @var FakeTermijnStore + */ + private FakeTermijnStore $store; + + /** + * The service under test. + * + * @var BeroepService + */ + private BeroepService $service; + + /** + * The status-transition engine mock (cascade tests). + * + * @var StatusTransitionService|\PHPUnit\Framework\MockObject\MockObject + */ + private StatusTransitionService $transitions; + + /** + * Set up the service with the honest fake and slug-mapped config. + * + * @return void + */ + protected function setUp(): void { + $this->store = new FakeTermijnStore(); + + $settings = $this->createMock(SettingsService::class); + $settings->method('getObjectService')->willReturn($this->store); + $settings->method('getConfigValue')->willReturnCallback( + static fn (string $key): string => match ($key) { + 'register' => 'dossiq', + 'beroep_schema' => 'beroep', + 'appeal_decision_schema' => 'appealDecision', + 'bezwaar_schema' => 'bezwaar', + default => '', + } + ); + + $this->transitions = $this->createMock(StatusTransitionService::class); + + $this->service = new BeroepService( + settingsService: $settings, + transitions: $this->transitions, + logger: $this->createMock(LoggerInterface::class), + ); + }//end setUp() + + /** + * register() resolves an EXISTING contested beslissing (the pre-fix + * is_array() guard threw 'Contested beslissing not found' here) and + * persists a beroep with the Awb 6:7 deadline derived from it. + * + * @return void + */ + public function testRegisterResolvesTheContestedDecisionAndPersists(): void { + $this->store->seed('appealDecision', [ + 'id' => 'decision-1', + 'effectiveDate' => '2026-01-01', + ]); + + $result = $this->service->register( + caseId: 'case-1', + sourceObjectionId: 'objection-1', + contestedDecisionId: 'decision-1', + filingDate: '2026-03-01', + ); + + // 6 weeks after 2026-01-01, and 2026-03-01 is past it. + $this->assertSame('2026-02-12', $result['filingDeadline']); + $this->assertTrue($result['latefilingNotice']); + $this->assertSame('decision-1', $result['contestedDecision']); + + // The record actually landed in the store. + $stored = $this->store->get('beroep', (string)$result['id']); + $this->assertNotNull($stored); + $this->assertSame('case-1', $stored['case']); + }//end testRegisterResolvesTheContestedDecisionAndPersists() + + /** + * register() still refuses a contested beslissing that truly does not + * exist — the fake throws DoesNotExistException exactly like live. + * + * @return void + */ + public function testRegisterThrowsForAMissingContestedDecision(): void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Contested beslissing not found'); + + $this->service->register( + caseId: 'case-1', + sourceObjectionId: 'objection-1', + contestedDecisionId: 'decision-missing', + filingDate: '2026-03-01', + ); + }//end testRegisterThrowsForAMissingContestedDecision() + + /** + * addFileInspectionRequest() reads the EXISTING beroep (pre-fix: always + * 'Beroep not found') and appends the Awb 8:42 entry with its 28-day + * deadline. + * + * @return void + */ + public function testAddFileInspectionRequestAppendsToTheExistingBeroep(): void { + $this->store->seed('beroep', [ + 'id' => 'beroep-1', + 'fileInspectionRequests' => [], + ]); + + $result = $this->service->addFileInspectionRequest( + appealId: 'beroep-1', + requestedAt: '2026-04-01', + ); + + $requests = $result['fileInspectionRequests']; + $this->assertCount(1, $requests); + $this->assertSame('2026-04-01', $requests[0]['requestedAt']); + $this->assertSame('2026-04-29', $requests[0]['deadline']); + + $stored = $this->store->get('beroep', 'beroep-1'); + $this->assertCount(1, $stored['fileInspectionRequests']); + }//end testAddFileInspectionRequestAppendsToTheExistingBeroep() + + /** + * executeCascade() with reopen_objection resolves the EXISTING source + * bezwaar (pre-fix: the dead guard skipped the reopen silently) and + * triggers the beroep-reopen transition on its case. + * + * @return void + */ + public function testExecuteCascadeReopensTheSourceObjectionCase(): void { + $this->store->seed('beroep', [ + 'id' => 'beroep-1', + 'sourceObjection' => 'objection-1', + ]); + $this->store->seed('bezwaar', [ + 'id' => 'objection-1', + 'case' => 'case-7', + ]); + + $this->transitions->expects($this->once()) + ->method('execute') + ->with('case-7', 'beroep-reopen', 'Reopened via beroep beroep-1'); + + $result = $this->service->executeCascade( + appealId: 'beroep-1', + action: 'reopen_objection', + ); + + $this->assertSame('reopen_objection', $result['cascadeAction']); + $this->assertSame('case-7', $result['cascadeObjectionCase']); + }//end testExecuteCascadeReopensTheSourceObjectionCase() +}//end class diff --git a/tests/Unit/Service/Bezwaar/BezwaarCreationHookTest.php b/tests/Unit/Service/Bezwaar/BezwaarCreationHookTest.php index b3d4194c0..685a3fbd6 100644 --- a/tests/Unit/Service/Bezwaar/BezwaarCreationHookTest.php +++ b/tests/Unit/Service/Bezwaar/BezwaarCreationHookTest.php @@ -24,6 +24,8 @@ use OCA\Dossiq\Service\Bezwaar\BezwaarCreationHook; use OCA\Dossiq\Service\SettingsService; +use OCA\Dossiq\Tests\Unit\Service\FakeStoredObject; +use OCP\AppFramework\Db\DoesNotExistException; use OCP\IUserSession; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -114,14 +116,16 @@ public function testLinksPrimairBesluitAndCreatesObjection(): void { public ?array $savedObjection = null; /** - * @return array + * Entity-shaped find, mirroring the real ObjectService contract. + * + * @return FakeStoredObject */ - public function find(string $id, string $register = '', string $schema = ''): array { + public function find(int|string $id, ?array $_extend = [], bool $files = false, string|int|null $register = null, string|int|null $schema = null): FakeStoredObject { if ($id === 'decision-1') { - return ['case' => 'primair-1']; + return new FakeStoredObject(['case' => 'primair-1']); } - return ['relatedCases' => ['other-9']]; + return new FakeStoredObject(['relatedCases' => ['other-9']]); } /** @@ -133,18 +137,18 @@ public function find(string $id, string $register = '', string $schema = ''): ar * @param string|null $schema Schema id. * @param string|null $uuid Optional object uuid. * - * @return array + * @return FakeStoredObject */ - public function saveObject(array $object, array $extend = [], ?string $register = null, ?string $schema = null, ?string $uuid = null): array { - if ($schema === '10') { + public function saveObject(array $object, ?array $extend = [], string|int|null $register = null, string|int|null $schema = null, ?string $uuid = null): FakeStoredObject { + if ((string)$schema === '10') { $this->savedCase = $object; } - if ($schema === '30') { + if ((string)$schema === '30') { $this->savedObjection = $object; } - return $object; + return new FakeStoredObject($object); }//end saveObject() }; @@ -179,10 +183,12 @@ public function testCreatesObjectionWhenDecisionHasNoParentCase(): void { public int $caseWrites = 0; /** - * @return array + * Entity-shaped find, mirroring the real ObjectService contract. + * + * @return FakeStoredObject */ - public function find(string $id, string $register = '', string $schema = ''): array { - return ['case' => '']; + public function find(int|string $id, ?array $_extend = [], bool $files = false, string|int|null $register = null, string|int|null $schema = null): FakeStoredObject { + return new FakeStoredObject(['case' => '']); } /** @@ -194,14 +200,14 @@ public function find(string $id, string $register = '', string $schema = ''): ar * @param string|null $schema Schema id. * @param string|null $uuid Optional object uuid. * - * @return array + * @return FakeStoredObject */ - public function saveObject(array $object, array $extend = [], ?string $register = null, ?string $schema = null, ?string $uuid = null): array { - if ($schema === '10') { + public function saveObject(array $object, ?array $extend = [], string|int|null $register = null, string|int|null $schema = null, ?string $uuid = null): FakeStoredObject { + if ((string)$schema === '10') { $this->caseWrites++; } - return $object; + return new FakeStoredObject($object); }//end saveObject() }; @@ -221,8 +227,14 @@ public function saveObject(array $object, array $extend = [], ?string $register public function testThrowsWhenDecisionNotFound(): void { $objectService = new class { - public function find(string $id, string $register = '', string $schema = ''): mixed { - return null; + /** + * A miss THROWS DoesNotExistException, exactly like the live + * ObjectService — a null return is a shape live never produces. + * + * @throws DoesNotExistException Always. + */ + public function find(int|string $id, ?array $_extend = [], bool $files = false, string|int|null $register = null, string|int|null $schema = null): FakeStoredObject { + throw new DoesNotExistException('Object ' . $id . ' does not exist'); } /** @@ -234,10 +246,10 @@ public function find(string $id, string $register = '', string $schema = ''): mi * @param string|null $schema Schema id. * @param string|null $uuid Optional object uuid. * - * @return array + * @return FakeStoredObject */ - public function saveObject(array $object, array $extend = [], ?string $register = null, ?string $schema = null, ?string $uuid = null): array { - return $object; + public function saveObject(array $object, ?array $extend = [], string|int|null $register = null, string|int|null $schema = null, ?string $uuid = null): FakeStoredObject { + return new FakeStoredObject($object); }//end saveObject() }; diff --git a/tests/Unit/Service/DossierCompilerTest.php b/tests/Unit/Service/DossierCompilerTest.php index c6fff5c52..1c6ebe186 100644 --- a/tests/Unit/Service/DossierCompilerTest.php +++ b/tests/Unit/Service/DossierCompilerTest.php @@ -139,8 +139,11 @@ public function __construct( ) { } - public function find(string $id, string $register = '', string $schema = ''): array { - return $this->case; + /** + * Entity-shaped find, mirroring the real ObjectService contract. + */ + public function find(int|string $id, ?array $_extend = [], bool $files = false, string|int|null $register = null, string|int|null $schema = null): FakeStoredObject { + return new FakeStoredObject($this->case); } /** @@ -190,8 +193,11 @@ public function testCompileToleratesDocumentListingFailure(): void { $objectService = new class { - public function find(string $id, string $register = '', string $schema = ''): array { - return ['relatedCases' => []]; + /** + * Entity-shaped find, mirroring the real ObjectService contract. + */ + public function find(int|string $id, ?array $_extend = [], bool $files = false, string|int|null $register = null, string|int|null $schema = null): FakeStoredObject { + return new FakeStoredObject(['relatedCases' => []]); } /** diff --git a/tests/Unit/Service/Subsidie/VaststellingServiceTest.php b/tests/Unit/Service/Subsidie/VaststellingServiceTest.php index 897107cfa..5057260c3 100644 --- a/tests/Unit/Service/Subsidie/VaststellingServiceTest.php +++ b/tests/Unit/Service/Subsidie/VaststellingServiceTest.php @@ -29,15 +29,19 @@ use OCA\Dossiq\Service\SettingsService; use OCA\Dossiq\Service\Subsidie\TerugvorderingService; use OCA\Dossiq\Service\Subsidie\VaststellingService; +use OCA\Dossiq\Tests\Unit\Service\FakeStoredObject; +use OCP\AppFramework\Db\DoesNotExistException; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; /** - * In-memory ObjectService fake for VaststellingServiceTest: plain - * find(id, register, schema) / saveObject(object, register, schema, uuid) - * over a schema-keyed store. saveObject merges the given fields onto any - * existing row (matching finalize()'s own pre-existing partial-patch call - * for the vaststelling object itself). + * In-memory ObjectService fake for VaststellingServiceTest, pinned to + * OpenRegister's REAL contract like the shared FakeTermijnStore: find() + * declares the real argument order, returns an entity-shaped object and + * THROWS DoesNotExistException on a miss; saveObject() declares the real + * signature and returns an entity-shaped object, never an array. It + * still merges the given fields onto any existing row (matching + * finalize()'s partial-patch call for the vaststelling object itself). */ class VaststellingFakeObjectService { @@ -49,35 +53,48 @@ class VaststellingFakeObjectService { public array $store = []; /** - * Find one object by id within a schema. + * Find one object by id within a schema — entity-shaped return, + * DoesNotExistException on a miss, exactly like live. * - * @param string $id Object id. - * @param string $register Ignored (single in-memory register). - * @param string $schema Schema slug. + * @param int|string $id Object id. + * @param array|null $_extend Relations to expand (ignored). + * @param bool $files Include file metadata (ignored). + * @param string|int|null $register Ignored (single in-memory register). + * @param string|int|null $schema Schema slug. * - * @return array|null + * @return FakeStoredObject + * + * @throws DoesNotExistException When the id is unknown. */ - public function find(string $id, string $register, string $schema): ?array { - return ($this->store[$schema][$id] ?? null); + public function find(int|string $id, ?array $_extend = [], bool $files = false, string|int|null $register = null, string|int|null $schema = null): FakeStoredObject { + $row = ($this->store[(string)$schema][(string)$id] ?? null); + if ($row === null) { + throw new DoesNotExistException('Object ' . $id . ' does not exist'); + } + + return new FakeStoredObject($row); }//end find() /** - * Save (merge) an object into the store. + * Save (merge) an object into the store — real signature, entity-shaped + * return. * * @param array $object Fields to merge. - * @param string $register Ignored. - * @param string $schema Schema slug. + * @param array|null $extend Relations to expand (ignored). + * @param string|int|null $register Ignored. + * @param string|int|null $schema Schema slug. * @param string|null $uuid Object id (null = generate one). * - * @return array The merged row. + * @return FakeStoredObject The merged row. */ - public function saveObject(array $object, string $register, string $schema, ?string $uuid = null): array { + public function saveObject(array $object, ?array $extend = [], string|int|null $register = null, string|int|null $schema = null, ?string $uuid = null): FakeStoredObject { + $schema = (string)$schema; $uuid = ($uuid ?? ('generated-' . count($this->store[$schema] ?? []))); $existing = ($this->store[$schema][$uuid] ?? []); $merged = array_merge($existing, $object, ['id' => $uuid]); $this->store[$schema][$uuid] = $merged; - return $merged; + return new FakeStoredObject($merged); }//end saveObject() }//end class