Skip to content

Commit

Permalink
imp: Allow to create a contract from the global list of contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
marien-probesys committed Dec 11, 2024
1 parent e73f9b6 commit 1e2dc34
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 0 deletions.
27 changes: 27 additions & 0 deletions src/Controller/ContractsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Translation\TranslatableMessage;

class ContractsController extends BaseController
{
Expand Down Expand Up @@ -52,6 +53,32 @@ public function index(
]);
}

#[Route('/contracts/new', name: 'new contract')]
public function new(
Request $request,
): Response {
$this->denyAccessUnlessGranted('orga:manage:contracts', 'any');

$form = $this->createNamedForm('contract', Form\Organization\SelectForm::class, options: [
'permission' => 'orga:manage:contracts',
'submit_label' => new TranslatableMessage('contracts.new.open_contract'),
]);

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$organization = $form->getData()['organization'];

return $this->redirectToRoute('new organization contract', [
'uid' => $organization->getUid(),
]);
}

return $this->render('contracts/new.html.twig', [
'form' => $form,
]);
}

#[Route('/contracts/{uid:contract}', name: 'contract', methods: ['GET', 'HEAD'])]
public function show(Entity\Contract $contract): Response
{
Expand Down
48 changes: 48 additions & 0 deletions src/Form/Organization/SelectForm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

// This file is part of Bileto.
// Copyright 2022-2024 Probesys
// SPDX-License-Identifier: AGPL-3.0-or-later

namespace App\Form\Organization;

use App\Form\Type as AppType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Translation\TranslatableMessage;

class SelectForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('organization', AppType\OrganizationType::class, [
'permission' => $options['permission'],
'required' => true,
'label' => new TranslatableMessage('forms.organization.select.choose'),
]);

if ($options['submit_label'] !== '') {
$builder->add('submit', Type\SubmitType::class, [
'label' => $options['submit_label'],
]);
}
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'csrf_token_id' => 'organization select',
'csrf_message' => 'csrf.invalid',
'attr' => [
'class' => 'form--standard',
],
'permission' => '',
'submit_label' => '',
]);

$resolver->setAllowedTypes('permission', 'string');
$resolver->setAllowedTypes('submit_label', ['string', TranslatableMessage::class]);
}
}
13 changes: 13 additions & 0 deletions templates/contracts/index.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@
with_context = false
) }}
</div>

{% if is_granted('orga:manage:contracts', 'any') %}
<button
class="button button--primary button--uppercase"
type="button"
data-controller="modal-opener"
data-action="modal-opener#fetch"
data-modal-opener-href-value="{{ path('new contract') }}"
>
{{ icon('plus') }}
{{ 'contracts.index.new_contract' | trans }}
</button>
{% endif %}
</div>

{% if contractsPagination.count > 0 %}
Expand Down
13 changes: 13 additions & 0 deletions templates/contracts/new.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{#
# This file is part of Bileto.
# Copyright 2022-2024 Probesys
# SPDX-License-Identifier: AGPL-3.0-or-later
#}

{% extends 'modal.html.twig' %}

{% block title %}{{ 'contracts.new.title' | trans }}{% endblock %}

{% block body %}
{{ form(form, { action: path('new contract') }) }}
{% endblock %}
43 changes: 43 additions & 0 deletions tests/Controller/ContractsControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,49 @@ public function testGetIndexFailsIfAccessIsForbidden(): void
$client->request(Request::METHOD_GET, '/contracts');
}

public function testGetNewRendersCorrectly(): void
{
$client = static::createClient();
$user = Factory\UserFactory::createOne();
$client->loginUser($user->_real());
$this->grantOrga($user->_real(), ['orga:manage:contracts']);

$client->request(Request::METHOD_GET, '/contracts/new');

$this->assertResponseIsSuccessful();
$this->assertSelectorTextContains('h1', 'New contract');
}

public function testGetNewFailsIfAccessIsForbidden(): void
{
$this->expectException(AccessDeniedException::class);

$client = static::createClient();
$user = Factory\UserFactory::createOne();
$client->loginUser($user->_real());

$client->catchExceptions(false);
$client->request(Request::METHOD_GET, '/contracts/new');
}

public function testPostNewRedirectsToTheSelectedOrganization(): void
{
$client = static::createClient();
$user = Factory\UserFactory::createOne();
$client->loginUser($user->_real());
$organization = Factory\OrganizationFactory::createOne();
$this->grantOrga($user->_real(), ['orga:manage:contracts'], $organization->_real());

$client->request(Request::METHOD_POST, '/contracts/new', [
'contract' => [
'_token' => $this->generateCsrfToken($client, 'organization select'),
'organization' => $organization->getId(),
],
]);

$this->assertResponseRedirects("/organizations/{$organization->getUid()}/contracts/new", 302);
}

public function testGetEditRendersCorrectly(): void
{
$client = static::createClient();
Expand Down
2 changes: 2 additions & 0 deletions translations/messages+intl-icu.en_GB.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ contracts.index.no_contracts: 'No contracts'
contracts.index.number: '{count, plural, =0 {No contracts} one {1 contract} other {# contracts}}'
contracts.index.tickets: '{count, plural, =0 {No tickets} one {1 ticket} other {# tickets}}'
contracts.index.title: Contracts
contracts.new.open_contract: 'Open a contract'
contracts.new.title: 'New contract'
contracts.notes: 'Private notes'
contracts.show.alert: Alert
Expand Down Expand Up @@ -171,6 +172,7 @@ forms.input_texts.remove: Remove
forms.max_chars: '(max. {number} characters)'
forms.optional: (optional)
forms.optional_max_chars: '(optional, max. {number} characters)'
forms.organization.select.choose: 'Choose an organization'
forms.read_only: '(read only)'
forms.save_changes: 'Save the changes'
forms.search.advanced_label: 'Search using keywords or by using the advanced syntax'
Expand Down
2 changes: 2 additions & 0 deletions translations/messages+intl-icu.fr_FR.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ contracts.index.no_contracts: 'Aucun contrat'
contracts.index.number: '{count, plural, =0 {Aucun contrat} one {1 contrat} other {# contrats}}'
contracts.index.tickets: '{count, plural, =0 {Aucun ticket} one {1 ticket} other {# tickets}}'
contracts.index.title: Contrats
contracts.new.open_contract: 'Ouvrir un contrat'
contracts.new.title: 'Nouveau contrat'
contracts.notes: 'Notes privées'
contracts.show.alert: Alerte
Expand Down Expand Up @@ -171,6 +172,7 @@ forms.input_texts.remove: Retirer
forms.max_chars: '(max. {number} caractères)'
forms.optional: (optionnel)
forms.optional_max_chars: '(optionnel, max. {number} caractères)'
forms.organization.select.choose: 'Choisir une organisation'
forms.read_only: '(lecture seule)'
forms.save_changes: 'Enregistrer les modifications'
forms.search.advanced_label: 'Rechercher par mots-clés ou avec la syntaxe avancée'
Expand Down

0 comments on commit 1e2dc34

Please sign in to comment.