Passed
Push — master ( 3340d7...94d7f4 )
by Jan
07:25
created

ProjectController::build()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 40
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 25
c 0
b 0
f 0
nc 6
nop 4
dl 0
loc 40
rs 9.52
1
<?php
2
/*
3
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 *  Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
6
 *
7
 *  This program is free software: you can redistribute it and/or modify
8
 *  it under the terms of the GNU Affero General Public License as published
9
 *  by the Free Software Foundation, either version 3 of the License, or
10
 *  (at your option) any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU Affero General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU Affero General Public License
18
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
namespace App\Controller;
22
23
use App\DataTables\ProjectBomEntriesDataTable;
24
use App\Entity\Parts\Part;
25
use App\Entity\ProjectSystem\Project;
26
use App\Entity\ProjectSystem\ProjectBOMEntry;
27
use App\Form\ProjectSystem\ProjectBOMEntryCollectionType;
28
use App\Form\ProjectSystem\ProjectBuildType;
29
use App\Form\Type\StructuralEntityType;
30
use App\Helpers\Projects\ProjectBuildRequest;
31
use App\Services\ProjectSystem\ProjectBuildHelper;
32
use Doctrine\Common\Collections\ArrayCollection;
33
use Doctrine\ORM\EntityManagerInterface;
34
use Omines\DataTablesBundle\DataTableFactory;
35
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
36
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
37
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
38
use Symfony\Component\HttpFoundation\Request;
39
use Symfony\Component\HttpFoundation\Response;
40
use Symfony\Component\Routing\Annotation\Route;
41
use Symfony\Component\Validator\Constraints\NotNull;
42
43
/**
44
 * @Route("/project")
45
 */
46
class ProjectController extends AbstractController
47
{
48
    private DataTableFactory $dataTableFactory;
49
50
    public function __construct(DataTableFactory $dataTableFactory)
51
    {
52
        $this->dataTableFactory = $dataTableFactory;
53
    }
54
55
    /**
56
     * @Route("/{id}/info", name="project_info", requirements={"id"="\d+"})
57
     */
58
    public function info(Project $project, Request $request, ProjectBuildHelper $buildHelper)
59
    {
60
        $this->denyAccessUnlessGranted('read', $project);
61
62
        $table = $this->dataTableFactory->createFromType(ProjectBomEntriesDataTable::class, ['project' => $project])
63
            ->handleRequest($request);
64
65
        if ($table->isCallback()) {
66
            return $table->getResponse();
67
        }
68
69
        return $this->render('Projects/info/info.html.twig', [
70
            'buildHelper' => $buildHelper,
71
            'datatable' => $table,
72
            'project' => $project,
73
        ]);
74
    }
75
76
    /**
77
     * @Route("/{id}/build", name="project_build", requirements={"id"="\d+"})
78
     */
79
    public function build(Project $project, Request $request, ProjectBuildHelper $buildHelper, EntityManagerInterface $entityManager): Response
80
    {
81
        $this->denyAccessUnlessGranted('read', $project);
82
83
        //If no number of builds is given (or it is invalid), just assume 1
84
        $number_of_builds = $request->query->getInt('n', 1);
85
        if ($number_of_builds < 1) {
86
            $number_of_builds = 1;
87
        }
88
89
        $projectBuildRequest = new ProjectBuildRequest($project, $number_of_builds);
90
        $form = $this->createForm(ProjectBuildType::class, $projectBuildRequest);
91
92
        $form->handleRequest($request);
93
        if ($form->isSubmitted()) {
94
            if ($form->isValid()) {
95
                //Ensure that the user can withdraw stock from all parts
96
                $this->denyAccessUnlessGranted('@parts_stock.withdraw');
97
98
                //We have to do a flush already here, so that the newly created partLot gets an ID and can be logged to DB later.
99
                $entityManager->flush();
100
                $buildHelper->doBuild($projectBuildRequest);
101
                $entityManager->flush();
102
                $this->addFlash('success', 'project.build.flash.success');
103
104
                return $this->redirect(
105
                    $request->get('_redirect',
106
                        $this->generateUrl('project_info', ['id' => $project->getID()]
107
                        )));
108
            } else {
109
                $this->addFlash('error', 'project.build.flash.invalid_input');
110
            }
111
        }
112
113
        return $this->renderForm('Projects/build/build.html.twig', [
114
            'buildHelper' => $buildHelper,
115
            'project' => $project,
116
            'build_request' => $projectBuildRequest,
117
            'number_of_builds' => $number_of_builds,
118
            'form' => $form,
119
        ]);
120
    }
121
122
    /**
123
     * @Route("/add_parts", name="project_add_parts_no_id")
124
     * @Route("/{id}/add_parts", name="project_add_parts", requirements={"id"="\d+"})
125
     * @param  Request  $request
126
     * @param  Project|null  $project
127
     */
128
    public function addPart(Request $request, EntityManagerInterface $entityManager, ?Project $project): Response
129
    {
130
        if($project) {
131
            $this->denyAccessUnlessGranted('edit', $project);
132
        } else {
133
            $this->denyAccessUnlessGranted('@projects.edit');
134
        }
135
136
        $builder = $this->createFormBuilder();
137
        $builder->add('project', StructuralEntityType::class, [
138
            'class' => Project::class,
139
            'required' => true,
140
            'disabled' => $project !== null, //If a project is given, disable the field
141
            'data' => $project,
142
            'constraints' => [
143
                new NotNull()
144
            ]
145
        ]);
146
        $builder->add('bom_entries', ProjectBOMEntryCollectionType::class);
147
        $builder->add('submit', SubmitType::class, ['label' => 'save']);
148
        $form = $builder->getForm();
149
150
        //Preset the BOM entries with the selected parts, when the form was not submitted yet
151
        $preset_data = new ArrayCollection();
152
        foreach (explode(',', $request->get('parts', '')) as $part_id) {
153
            $part = $entityManager->getRepository(Part::class)->find($part_id);
154
            if (null !== $part) {
155
                //If there is already a BOM entry for this part, we use this one (we edit it then)
156
                $bom_entry = $entityManager->getRepository(ProjectBOMEntry::class)->findOneBy([
157
                    'project' => $project,
158
                    'part' => $part
159
                ]);
160
                if ($bom_entry) {
161
                    $preset_data->add($bom_entry);
162
                } else { //Otherwise create an empty one
163
                    $entry = new ProjectBOMEntry();
164
                    $entry->setProject($project);
165
                    $entry->setPart($part);
166
                    $preset_data->add($entry);
167
                }
168
            }
169
        }
170
        $form['bom_entries']->setData($preset_data);
171
172
        $form->handleRequest($request);
173
        if ($form->isSubmitted() && $form->isValid()) {
174
            $target_project = $project ?? $form->get('project')->getData();
175
176
            //Ensure that we really have acces to the selected project
177
            $this->denyAccessUnlessGranted('edit', $target_project);
178
179
            $data = $form->getData();
180
            $bom_entries = $data['bom_entries'];
181
            foreach ($bom_entries as $bom_entry){
182
                $target_project->addBOMEntry($bom_entry);
183
            }
184
            $entityManager->flush();
185
186
            //If a redirect query parameter is set, redirect to this page
187
            if ($request->query->get('_redirect')) {
188
                return $this->redirect($request->query->get('_redirect'));
189
            }
190
            //Otherwise just show the project info page
191
            return $this->redirectToRoute('project_info', ['id' => $target_project->getID()]);
192
        }
193
194
        return $this->renderForm('Projects/add_parts.html.twig', [
195
            'project' => $project,
196
            'form' => $form,
197
        ]);
198
    }
199
}