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

ProjectBuildHelper::doBuild()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 9
c 0
b 0
f 0
nc 8
nop 1
dl 0
loc 16
rs 9.6111
1
<?php
2
/*
3
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 *  Copyright (C) 2019 - 2023 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\Services\ProjectSystem;
22
23
use App\Entity\ProjectSystem\Project;
24
use App\Entity\ProjectSystem\ProjectBOMEntry;
25
use App\Helpers\Projects\ProjectBuildRequest;
26
use App\Services\Parts\PartLotWithdrawAddHelper;
27
28
class ProjectBuildHelper
29
{
30
    private PartLotWithdrawAddHelper $withdraw_add_helper;
31
32
    public function __construct(PartLotWithdrawAddHelper $withdraw_add_helper)
33
    {
34
        $this->withdraw_add_helper = $withdraw_add_helper;
35
    }
36
37
    /**
38
     * Returns the maximum buildable amount of the given BOM entry based on the stock of the used parts.
39
     * This function only works for BOM entries that are associated with a part.
40
     * @param  ProjectBOMEntry  $projectBOMEntry
41
     * @return int
42
     */
43
    public function getMaximumBuildableCountForBOMEntry(ProjectBOMEntry $projectBOMEntry): int
44
    {
45
        $part = $projectBOMEntry->getPart();
46
47
        if ($part === null) {
48
            throw new \InvalidArgumentException('This function cannot determine the maximum buildable count for a BOM entry without a part!');
49
        }
50
51
        if ($projectBOMEntry->getQuantity() <= 0) {
52
            throw new \RuntimeException('The quantity of the BOM entry must be greater than 0!');
53
        }
54
55
        $amount_sum = $part->getAmountSum();
56
57
        return (int) floor($amount_sum / $projectBOMEntry->getQuantity());
58
    }
59
60
    /**
61
     * Returns the maximum buildable amount of the given project, based on the stock of the used parts in the BOM.
62
     * @param  Project  $project
63
     * @return int
64
     */
65
    public function getMaximumBuildableCount(Project $project): int
66
    {
67
        $maximum_buildable_count = PHP_INT_MAX;
68
        foreach ($project->getBOMEntries() as $bom_entry) {
69
            //Skip BOM entries without a part (as we can not determine that)
70
            if (!$bom_entry->isPartBomEntry()) {
71
                continue;
72
            }
73
74
            //The maximum buildable count for the whole project is the minimum of all BOM entries
75
            $maximum_buildable_count = min($maximum_buildable_count, $this->getMaximumBuildableCountForBOMEntry($bom_entry));
76
        }
77
78
        return $maximum_buildable_count;
79
    }
80
81
    /**
82
     * Checks if the given project can be build with the current stock.
83
     * This means that the maximum buildable count is greater or equal than the requested $number_of_projects
84
     * @param  Project  $project
85
     * @parm int $number_of_builds
86
     * @return bool
87
     */
88
    public function isProjectBuildable(Project $project, int $number_of_builds = 1): bool
89
    {
90
        return $this->getMaximumBuildableCount($project) >= $number_of_builds;
91
    }
92
93
    /**
94
     * Check if the given BOM entry can be build with the current stock.
95
     * This means that the maximum buildable count is greater or equal than the requested $number_of_projects
96
     * @param  ProjectBOMEntry  $bom_entry
97
     * @param  int  $number_of_builds
98
     * @return bool
99
     */
100
    public function isBOMEntryBuildable(ProjectBOMEntry $bom_entry, int $number_of_builds = 1): bool
101
    {
102
        return $this->getMaximumBuildableCountForBOMEntry($bom_entry) >= $number_of_builds;
103
    }
104
105
    /**
106
     * Returns the project BOM entries for which parts are missing in the stock for the given number of builds
107
     * @param  Project  $project The project for which the BOM entries should be checked
108
     * @param  int  $number_of_builds How often should the project be build?
109
     * @return ProjectBOMEntry[]
110
     */
111
    public function getNonBuildableProjectBomEntries(Project $project, int $number_of_builds = 1): array
112
    {
113
        if ($number_of_builds < 1) {
114
            throw new \InvalidArgumentException('The number of builds must be greater than 0!');
115
        }
116
117
        $non_buildable_entries = [];
118
119
        foreach ($project->getBomEntries() as $bomEntry) {
120
            $part = $bomEntry->getPart();
121
122
            //Skip BOM entries without a part (as we can not determine that)
123
            if ($part === null) {
124
                continue;
125
            }
126
127
            $amount_sum = $part->getAmountSum();
128
129
            if ($amount_sum < $bomEntry->getQuantity() * $number_of_builds) {
130
                $non_buildable_entries[] = $bomEntry;
131
            }
132
        }
133
134
        return $non_buildable_entries;
135
    }
136
137
    /**
138
     * Withdraw the parts from the stock using the given ProjectBuildRequest and create the build parts entries, if needed.
139
     * The ProjectBuildRequest has to be validated before!!
140
     * You have to flush changes to DB afterwards
141
     * @param  ProjectBuildRequest  $buildRequest
142
     * @return void
143
     */
144
    public function doBuild(ProjectBuildRequest $buildRequest): void
145
    {
146
        $message = $buildRequest->getComment();
147
        $message .= ' (Project build: '.$buildRequest->getProject()->getName().')';
148
149
        foreach ($buildRequest->getPartBomEntries() as $bom_entry) {
150
            foreach ($buildRequest->getPartLotsForBOMEntry($bom_entry) as $part_lot) {
151
                $amount = $buildRequest->getLotWithdrawAmount($part_lot);
152
                if ($amount > 0) {
153
                    $this->withdraw_add_helper->withdraw($part_lot, $amount, $message);
154
                }
155
            }
156
        }
157
158
        if ($buildRequest->getAddBuildsToBuildsPart()) {
159
            $this->withdraw_add_helper->add($buildRequest->getBuildsPartLot(), $buildRequest->getNumberOfBuilds(), $message);
0 ignored issues
show
Bug introduced by
It seems like $buildRequest->getBuildsPartLot() can also be of type null; however, parameter $partLot of App\Services\Parts\PartLotWithdrawAddHelper::add() does only seem to accept App\Entity\Parts\PartLot, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

159
            $this->withdraw_add_helper->add(/** @scrutinizer ignore-type */ $buildRequest->getBuildsPartLot(), $buildRequest->getNumberOfBuilds(), $message);
Loading history...
160
        }
161
    }
162
}