diff --git a/app/api/module/Api/config/query-map.config.php b/app/api/module/Api/config/query-map.config.php index 6f2d8b242d..2274744bf2 100644 --- a/app/api/module/Api/config/query-map.config.php +++ b/app/api/module/Api/config/query-map.config.php @@ -100,6 +100,7 @@ // Licence TransferQuery\Licence\BusinessDetails::class => QueryHandler\Licence\BusinessDetails::class, + TransferQuery\Licence\ExistsWithOperatorAdmin::class => QueryHandler\Licence\ExistsWithOperatorAdmin::class, TransferQuery\Licence\Licence::class => QueryHandler\Licence\Licence::class, TransferQuery\Licence\LicenceWithCorrespondenceCd::class => QueryHandler\Licence\LicenceWithCorrespondenceCd::class, TransferQuery\Licence\LicenceByNumber::class => QueryHandler\Licence\LicenceByNumber::class, diff --git a/app/api/module/Api/config/validation-map/licence.config.php b/app/api/module/Api/config/validation-map/licence.config.php index 589e796458..7437d7afdb 100644 --- a/app/api/module/Api/config/validation-map/licence.config.php +++ b/app/api/module/Api/config/validation-map/licence.config.php @@ -9,6 +9,7 @@ QueryHandler\Licence\Addresses::class => Misc\CanAccessLicenceWithId::class, QueryHandler\Licence\BusinessDetails::class => Misc\CanAccessLicenceWithId::class, QueryHandler\Licence\ConditionUndertaking::class => Misc\CanAccessLicenceWithId::class, + QueryHandler\Licence\ExistsWithOperatorAdmin::class => Misc\NoValidationRequired::class, QueryHandler\Licence\Licence::class => Misc\CanAccessLicenceWithId::class, QueryHandler\Licence\LicenceWithCorrespondenceCd::class => Misc\CanAccessLicenceWithId::class, QueryHandler\Licence\LicenceByNumber::class => Misc\CanAccessLicenceWithLicNo::class, diff --git a/app/api/module/Api/src/Domain/QueryHandler/Licence/ExistsWithOperatorAdmin.php b/app/api/module/Api/src/Domain/QueryHandler/Licence/ExistsWithOperatorAdmin.php new file mode 100644 index 0000000000..ea98ed75c9 --- /dev/null +++ b/app/api/module/Api/src/Domain/QueryHandler/Licence/ExistsWithOperatorAdmin.php @@ -0,0 +1,39 @@ +getRepo(); + + try { + $licence = $repo->fetchByLicNoWithoutAdditionalData($query->getLicNo()); + $licenceExists = true; + $hasOperatorAdmin = $licence->getOrganisation()->hasOperatorAdmin(); + } catch (NotFoundException) { + $licenceExists = false; + $hasOperatorAdmin = false; + } + + return [ + 'licenceExists' => $licenceExists, + 'hasOperatorAdmin' => $hasOperatorAdmin, + ]; + } +} diff --git a/app/api/module/Api/src/Domain/QueryHandler/Organisation/Organisation.php b/app/api/module/Api/src/Domain/QueryHandler/Organisation/Organisation.php index 264174436b..e84a5be688 100644 --- a/app/api/module/Api/src/Domain/QueryHandler/Organisation/Organisation.php +++ b/app/api/module/Api/src/Domain/QueryHandler/Organisation/Organisation.php @@ -1,21 +1,10 @@ - */ - namespace Dvsa\Olcs\Api\Domain\QueryHandler\Organisation; use Dvsa\Olcs\Api\Domain\QueryHandler\AbstractQueryHandler; use Dvsa\Olcs\Transfer\Query\QueryInterface; -/** - * Organisation - * - * @author Rob Caiger - */ class Organisation extends AbstractQueryHandler { protected $repoServiceName = 'Organisation'; @@ -37,6 +26,7 @@ public function handleQuery(QueryInterface $query) 'isDisqualified' => $organisation->getDisqualifications()->count() > 0, 'taValueOptions' => $this->getTrafficAreaValueOptions($allowedOperatorLocation), 'allowedOperatorLocation' => $allowedOperatorLocation, + 'hasOperatorAdmin' => $organisation->hasOperatorAdmin() ] ); } diff --git a/app/api/module/Api/src/Entity/Organisation/Organisation.php b/app/api/module/Api/src/Entity/Organisation/Organisation.php index 7032b765ef..0dfbc58bfb 100644 --- a/app/api/module/Api/src/Entity/Organisation/Organisation.php +++ b/app/api/module/Api/src/Entity/Organisation/Organisation.php @@ -529,6 +529,20 @@ public function getAdministratorUsers() return $this->organisationUsers->matching($criteria); } + public function hasOperatorAdmin(): bool + { + $administratorUsers = $this->getAdministratorUsers(); + + /** @var OrganisationUserEntity $orgUser */ + foreach ($administratorUsers as $orgUser) { + if ($orgUser->getUser()->isOperatorAdministrator()) { + return true; + } + } + + return false; + } + /** * can only delete operator admin if more than one exists */ diff --git a/app/api/test/module/Api/src/Domain/QueryHandler/Licence/ExistsWithOperatorAdminTest.php b/app/api/test/module/Api/src/Domain/QueryHandler/Licence/ExistsWithOperatorAdminTest.php new file mode 100644 index 0000000000..ca2971255e --- /dev/null +++ b/app/api/test/module/Api/src/Domain/QueryHandler/Licence/ExistsWithOperatorAdminTest.php @@ -0,0 +1,73 @@ +sut = new QueryHandler(); + $this->mockRepo('Licence', Repo::class); + + parent::setUp(); + } + + public function testHandleQueryNotFound(): void + { + $licNo = 'PB2141421'; + $query = Query::create(['licNo' => $licNo]); + + $this->repoMap['Licence']->expects('fetchByLicNoWithoutAdditionalData')->with($licNo)->andThrow(NotFoundException::class); + + $expectedResult = [ + 'licenceExists' => false, + 'hasOperatorAdmin' => false, + ]; + + $this->assertEquals($expectedResult, $this->sut->handleQuery($query)); + } + + /** + * @dataProvider dpHandleQuery + */ + public function testHandleQuery(bool $isOperatorAdmin): void + { + $licNo = 'PB2141421'; + $query = Query::create(['licNo' => $licNo]); + + $organisation = m::mock(Organisation::class); + $organisation->expects('hasOperatorAdmin')->andReturn($isOperatorAdmin); + + $licence = m::mock(LicenceEntity::class); + $licence->expects('getOrganisation')->andReturn($organisation); + + + $this->repoMap['Licence']->expects('fetchByLicNoWithoutAdditionalData')->with($licNo)->andReturn($licence); + + $expectedResult = [ + 'licenceExists' => true, + 'hasOperatorAdmin' => $isOperatorAdmin, + ]; + + $this->assertEquals($expectedResult, $this->sut->handleQuery($query)); + } + + public function dpHandleQuery(): array + { + return [ + [true], + [false], + ]; + } +} diff --git a/app/api/test/module/Api/src/Domain/QueryHandler/Organisation/OrganisationTest.php b/app/api/test/module/Api/src/Domain/QueryHandler/Organisation/OrganisationTest.php index 407b63aba1..fda35d7279 100644 --- a/app/api/test/module/Api/src/Domain/QueryHandler/Organisation/OrganisationTest.php +++ b/app/api/test/module/Api/src/Domain/QueryHandler/Organisation/OrganisationTest.php @@ -1,10 +1,6 @@ - */ +declare(strict_types=1); namespace Dvsa\OlcsTest\Api\Domain\QueryHandler\Organisation; @@ -14,13 +10,7 @@ use Dvsa\Olcs\Api\Domain\Repository\TrafficArea as TrafficAreaRepo; use Dvsa\Olcs\Transfer\Query\Organisation\Organisation as Qry; use Mockery as m; -use SAML2\Utilities\ArrayCollection; -/** - * Organisation Test - * - * @author Rob Caiger - */ class OrganisationTest extends QueryHandlerTestCase { public function setUp(): void @@ -32,7 +22,7 @@ public function setUp(): void parent::setUp(); } - public function testHandleQueryDisqualified() + public function testHandleQueryDisqualified(): void { $query = Qry::create(['id' => 111]); @@ -40,6 +30,7 @@ public function testHandleQueryDisqualified() $mockOrganisation->shouldReceive('serialize')->andReturn(['foo' => 'bar']); $mockOrganisation->shouldReceive('getDisqualifications->count')->andReturn(2); $mockOrganisation->shouldReceive('getAllowedOperatorLocation')->andReturn('GB')->once(); + $mockOrganisation->expects('hasOperatorAdmin')->withNoArgs()->andReturnTrue(); $mockTa = m::mock() ->shouldReceive('getId') @@ -64,13 +55,14 @@ public function testHandleQueryDisqualified() 'foo' => 'bar', 'isDisqualified' => true, 'allowedOperatorLocation' => 'GB', + 'hasOperatorAdmin' => true, 'taValueOptions' => [1 => 'foo'], ]; $this->assertEquals($expected, $this->sut->handleQuery($query)->serialize()); } - public function testHandleQueryNotDisqualified() + public function testHandleQueryNotDisqualified(): void { $query = Qry::create(['id' => 111]); @@ -78,6 +70,7 @@ public function testHandleQueryNotDisqualified() $mockOrganisation->shouldReceive('serialize')->andReturn(['foo' => 'bar']); $mockOrganisation->shouldReceive('getDisqualifications->count')->andReturn(0); $mockOrganisation->shouldReceive('getAllowedOperatorLocation')->andReturn('GB')->once(); + $mockOrganisation->expects('hasOperatorAdmin')->withNoArgs()->andReturnFalse(); $mockTa = m::mock() ->shouldReceive('getId') @@ -102,6 +95,7 @@ public function testHandleQueryNotDisqualified() 'foo' => 'bar', 'isDisqualified' => false, 'allowedOperatorLocation' => 'GB', + 'hasOperatorAdmin' => false, 'taValueOptions' => [1 => 'foo'], ]; diff --git a/app/api/test/module/Api/src/Entity/Organisation/OrganisationEntityTest.php b/app/api/test/module/Api/src/Entity/Organisation/OrganisationEntityTest.php index 7daca0f9ea..85379a1211 100644 --- a/app/api/test/module/Api/src/Entity/Organisation/OrganisationEntityTest.php +++ b/app/api/test/module/Api/src/Entity/Organisation/OrganisationEntityTest.php @@ -689,6 +689,50 @@ public function testCanDeleteOperatorAdminFalse(): void $this->assertFalse($entity->canDeleteOperatorAdmin()); } + /** + * first user not op admin, 2nd user is op admin, 3rd user doesn't need to be checked + */ + public function testHasOperatorAdminTrue(): void + { + $entity = new Entity(); + + $user1 = m::mock(OrganisationUser::class)->makePartial(); + $user1->setIsAdministrator('Y'); + $user1->expects('getUser->isOperatorAdministrator')->withNoArgs()->andReturnFalse(); + + $user2 = m::mock(OrganisationUser::class)->makePartial(); + $user2->setIsAdministrator('Y'); + $user2->expects('getUser->isOperatorAdministrator')->withNoArgs()->andReturnTrue(); + + $user3 = m::mock(OrganisationUser::class)->makePartial(); + $user3->setIsAdministrator('Y'); + $user3->expects('getUser->isOperatorAdministrator')->never(); + + $entity->setOrganisationUsers(new ArrayCollection([$user1, $user2, $user3])); + + $this->assertTrue($entity->hasOperatorAdmin()); + } + + /** + * no operator admins are found + */ + public function testHasOperatorAdminFalse(): void + { + $entity = new Entity(); + + $user1 = m::mock(OrganisationUser::class)->makePartial(); + $user1->setIsAdministrator('Y'); + $user1->expects('getUser->isOperatorAdministrator')->withNoArgs()->andReturnFalse(); + + $user2 = m::mock(OrganisationUser::class)->makePartial(); + $user2->setIsAdministrator('Y'); + $user2->expects('getUser->isOperatorAdministrator')->withNoArgs()->andReturnFalse(); + + $entity->setOrganisationUsers(new ArrayCollection([$user1, $user2])); + + $this->assertFalse($entity->hasOperatorAdmin()); + } + /** * test if org user not found due to soft delete */ diff --git a/app/selfserve/module/Olcs/src/Controller/ConsultantRegistrationController.php b/app/selfserve/module/Olcs/src/Controller/ConsultantRegistrationController.php index 18b2a5afc6..4d25242696 100644 --- a/app/selfserve/module/Olcs/src/Controller/ConsultantRegistrationController.php +++ b/app/selfserve/module/Olcs/src/Controller/ConsultantRegistrationController.php @@ -8,6 +8,7 @@ use Common\Service\Helper\TranslationHelperService; use Common\Service\Helper\UrlHelperService; use Common\Service\Script\ScriptFactory; +use Dvsa\Olcs\Transfer\Query\Licence\ExistsWithOperatorAdmin; use Dvsa\Olcs\Transfer\Command\User\RegisterConsultantAndOperator; use Dvsa\Olcs\Utils\Translation\NiTextTranslation; use Laminas\Http\Response; @@ -26,16 +27,17 @@ class ConsultantRegistrationController extends AbstractController { public function __construct( - NiTextTranslation $niTextTranslationUtil, - AuthorizationService $authService, - protected FormHelperService $formHelper, - protected ScriptFactory $scriptFactory, - protected TranslationHelperService $translationHelper, - protected UrlHelperService $urlHelper, + NiTextTranslation $niTextTranslationUtil, + AuthorizationService $authService, + protected FormHelperService $formHelper, + protected ScriptFactory $scriptFactory, + protected TranslationHelperService $translationHelper, + protected UrlHelperService $urlHelper, protected FlashMessengerHelperService $flashMessengerHelper, - protected ConsultantRegistration $consultantRegistrationSession, - protected CreateAccountMapper $formatDataMapper - ) { + protected ConsultantRegistration $consultantRegistrationSession, + protected CreateAccountMapper $formatDataMapper + ) + { parent::__construct($niTextTranslationUtil, $authService); } @@ -58,8 +60,17 @@ public function addAction() if ($form->isValid()) { $formData = $form->getData(); - if(($formData['fields']['existingOperatorLicence'] ?? null) === 'Y') { - $this->redirect()->toRoute('user-registration/contact-your-administrator'); + if (($formData['fields']['existingOperatorLicence'] ?? null) === 'Y') { + $licenceNumber = $formData['fields']['licenceContent']['licenceNumber']; + $checks = $this->licenseHasAdmin($licenceNumber); + + if (!$checks['licenceExists'] ?? false) { + $form->setMessages(['fields' => ['licenceContent' => ['licenceNumber' => ['record-not-found']]]]); + } elseif (!$checks['hasOperatorAdmin'] ?? false) { + $this->redirect()->toRoute('user-registration/operator'); + } else { + $this->redirect()->toRoute('user-registration/contact-your-administrator'); + } } elseif (($formData['fields']['existingOperatorLicence'] ?? null) === 'N') { $this->redirect()->toRoute('user-registration/operator-representation'); } @@ -72,6 +83,15 @@ public function addAction() ]); } + private function licenseHasAdmin(string $licenceNumber): array + { + $response = $this->handleQuery(ExistsWithOperatorAdmin::create(['licNo' => $licenceNumber])); + if ($response->isOk()) { + return $response->getResult(); + } + return []; + } + /** * @return Response|ViewModel */ @@ -197,6 +217,7 @@ private function registerConsultantAndOperator($consultantFormData) $this->redirect()->toRoute('user-registration'); } + private function alterForm($form) { // inject link into terms agreed label diff --git a/app/selfserve/module/Olcs/src/Form/Model/Fieldset/ExistingOperatorLicence.php b/app/selfserve/module/Olcs/src/Form/Model/Fieldset/ExistingOperatorLicence.php index e6cddfb6d4..d02b1b85d2 100644 --- a/app/selfserve/module/Olcs/src/Form/Model/Fieldset/ExistingOperatorLicence.php +++ b/app/selfserve/module/Olcs/src/Form/Model/Fieldset/ExistingOperatorLicence.php @@ -5,23 +5,45 @@ use Laminas\Form\Annotation as Form; /** - * @Form\Name("ExistingOperatorLicence") - * @Form\Attributes({"method":"post"}) - * @Form\Options({"prefer_form_input_filter": true}) + * @Form\Type("\Common\Form\Elements\Types\RadioVertical") + * @Form\Name("existing-operator-licence") + * @Form\Options({ + * "radio-element": "existingOperatorLicence" + * }) */ class ExistingOperatorLicence { /** - * @Form\Name("existingOperatorLicence") + * + * @Form\Attributes({ + * "radios_wrapper_attributes": {"class": "govuk-radios", "data-module":"govuk-radios"} + * }) * @Form\Options({ - * "label": "user-registration.field.existing-operator-licence.label", - * "hint": "user-registration.field.existing-operator-licence.hint", - * "value_options":{"N":"select-option-no", "Y":"select-option-yes"}, - * "label_attributes": {"class": "form-control form-control--radio form-control--inline"} + * "value_options":{ + * "NoLicence":{ + * "label":"select-option-no", + * "value":"N", + * + * + * }, + * "licence":{ + * "label":"select-option-yes", + * "attributes": {"data-aira-controls": "conditional-existingOperatorLicenceApplication"}, + * "value":"Y" + * } + * }, + * "label_attributes": { + * "class":"form-control form-control--radio form-control--advanced" + * }, * }) * @Form\Required(true) - * @Form\Attributes({"id":"existingOperatorLicence", "placeholder":"", "required":false}) - * @Form\Type("Radio") + * @Form\Type("\Common\Form\Elements\Types\Radio") */ public $existingOperatorLicence = null; + + + /** + * @Form\ComposedObject("Olcs\Form\Model\Fieldset\ExistingOperatorLicenceApplication") + */ + public $licenceContent = null; } diff --git a/app/selfserve/module/Olcs/src/Form/Model/Fieldset/ExistingOperatorLicenceApplication.php b/app/selfserve/module/Olcs/src/Form/Model/Fieldset/ExistingOperatorLicenceApplication.php new file mode 100644 index 0000000000..efe51cd94c --- /dev/null +++ b/app/selfserve/module/Olcs/src/Form/Model/Fieldset/ExistingOperatorLicenceApplication.php @@ -0,0 +1,29 @@ +sut = m::mock(ConsultantRegistrationController::class) + ->makePartial(); + } + + + public function testContactYourAdministratorAction() : void + { + // Arrange + $expectedViewName = 'olcs/user-registration/contact-your-administrato'; + $viewModel = new ViewModel(); + $viewModel->setTemplate($expectedViewName); + + $this->sut->shouldReceive('contactYourAdministratorAction') + ->andReturn($viewModel); + $result = $this->sut->contactYourAdministratorAction(); + $this->assertInstanceOf(ViewModel::class, $result); + $this->assertEquals($expectedViewName, $result->getTemplate()); + $this->assertEmpty($result->getVariable('pageTitle')); + + } + + +}