mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-12 21:29:33 +00:00
Projekt BOM-Konfiguration um Assemblies bereinigen.
Assembly BOM-Konfiguration um Projektauswahl erweitern (APS-3, APS-4)
This commit is contained in:
parent
b046d41b68
commit
65706d1d0b
24 changed files with 60 additions and 1096 deletions
|
|
@ -1,77 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\DataTables\Helpers;
|
||||
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
use App\Entity\Attachments\Attachment;
|
||||
use App\Services\Attachments\AssemblyPreviewGenerator;
|
||||
use App\Services\Attachments\AttachmentURLGenerator;
|
||||
use App\Services\EntityURLGenerator;
|
||||
|
||||
/**
|
||||
* A helper service which contains common code to render columns for assembly related tables
|
||||
*/
|
||||
class AssemblyDataTableHelper
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EntityURLGenerator $entityURLGenerator,
|
||||
private readonly AssemblyPreviewGenerator $previewGenerator,
|
||||
private readonly AttachmentURLGenerator $attachmentURLGenerator
|
||||
) {
|
||||
}
|
||||
|
||||
public function renderName(Assembly $context): string
|
||||
{
|
||||
$icon = '';
|
||||
|
||||
return sprintf(
|
||||
'<a href="%s">%s%s</a>',
|
||||
$this->entityURLGenerator->infoURL($context),
|
||||
$icon,
|
||||
htmlspecialchars($context->getName())
|
||||
);
|
||||
}
|
||||
|
||||
public function renderPicture(Assembly $context): string
|
||||
{
|
||||
$preview_attachment = $this->previewGenerator->getTablePreviewAttachment($context);
|
||||
if (!$preview_attachment instanceof Attachment) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$title = htmlspecialchars($preview_attachment->getName());
|
||||
if ($preview_attachment->getFilename()) {
|
||||
$title .= ' ('.htmlspecialchars($preview_attachment->getFilename()).')';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<img alt="%s" src="%s" data-thumbnail="%s" class="%s" data-title="%s" data-controller="elements--hoverpic">',
|
||||
'Assembly image',
|
||||
$this->attachmentURLGenerator->getThumbnailURL($preview_attachment),
|
||||
$this->attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_md'),
|
||||
'hoverpic assembly-table-image',
|
||||
$title
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -108,7 +108,6 @@ class Project extends AbstractStructuralDBElement
|
|||
#[Groups(['extended', 'full', 'import'])]
|
||||
#[ORM\OneToMany(mappedBy: 'project', targetEntity: ProjectBOMEntry::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
|
||||
#[UniqueObjectCollection(message: 'project.bom_entry.part_already_in_bom', fields: ['part'])]
|
||||
#[UniqueObjectCollection(message: 'project.bom_entry.assembly_already_in_bom', fields: ['assembly'])]
|
||||
#[UniqueObjectCollection(message: 'project.bom_entry.name_already_in_bom', fields: ['name'])]
|
||||
protected Collection $bom_entries;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ use ApiPlatform\Metadata\Post;
|
|||
use ApiPlatform\OpenApi\Model\Operation;
|
||||
use ApiPlatform\Serializer\Filter\PropertyFilter;
|
||||
use App\ApiPlatform\Filter\LikeFilter;
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
use App\Entity\Contracts\TimeStampableInterface;
|
||||
use App\Repository\DBElementRepository;
|
||||
use App\Validator\UniqueValidatableInterface;
|
||||
|
|
@ -105,10 +104,7 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
|
|||
/**
|
||||
* @var string|null An optional name describing this BOM entry (useful for non-part entries)
|
||||
*/
|
||||
#[Assert\Expression(
|
||||
'this.getPart() !== null or this.getAssembly() !== null or (this.getName() !== null and this.getName() != "")',
|
||||
message: 'validator.project.bom_entry.part_or_assembly_needed'
|
||||
)]
|
||||
#[Assert\Expression('this.getPart() !== null or this.getName() !== null', message: 'validator.project.bom_entry.name_or_part_needed')]
|
||||
#[ORM\Column(type: Types::STRING, nullable: true)]
|
||||
#[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'simple', 'extended', 'full'])]
|
||||
protected ?string $name = null;
|
||||
|
|
@ -136,18 +132,6 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
|
|||
#[Groups(['bom_entry:read', 'bom_entry:write', 'full'])]
|
||||
protected ?Part $part = null;
|
||||
|
||||
/**
|
||||
* @var Assembly|null The associated assembly
|
||||
*/
|
||||
#[Assert\Expression(
|
||||
'(this.getPart() === null or this.getAssembly() === null) and (this.getName() === null or (this.getName() != null and this.getName() != ""))',
|
||||
message: 'validator.project.bom_entry.only_part_or_assembly_allowed'
|
||||
)]
|
||||
#[ORM\ManyToOne(targetEntity: Assembly::class, inversedBy: 'assembly_bom_entries')]
|
||||
#[ORM\JoinColumn(name: 'id_assembly')]
|
||||
#[Groups(['bom_entry:read', 'bom_entry:write', ])]
|
||||
protected ?Assembly $assembly = null;
|
||||
|
||||
/**
|
||||
* @var BigDecimal|null The price of this non-part BOM entry
|
||||
*/
|
||||
|
|
@ -240,16 +224,6 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getAssembly(): ?Assembly
|
||||
{
|
||||
return $this->assembly;
|
||||
}
|
||||
|
||||
public function setAssembly(?Assembly $assembly): void
|
||||
{
|
||||
$this->assembly = $assembly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the price of this BOM entry, if existing.
|
||||
* Prices are only valid on non-Part BOM entries.
|
||||
|
|
@ -287,15 +261,6 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
|
|||
return $this->part instanceof Part;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this BOM entry is a assembly associated BOM entry or not.
|
||||
* @return bool True if this BOM entry is a assembly associated BOM entry, false otherwise.
|
||||
*/
|
||||
public function isAssemblyBomEntry(): bool
|
||||
{
|
||||
return $this->assembly instanceof Assembly;
|
||||
}
|
||||
|
||||
#[Assert\Callback]
|
||||
public function validate(ExecutionContextInterface $context, $payload): void
|
||||
{
|
||||
|
|
@ -357,7 +322,6 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
|
|||
return [
|
||||
'name' => $this->getName(),
|
||||
'part' => $this->getPart()?->getID(),
|
||||
'assembly' => $this->getAssembly()?->getID(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ class ProjectAddPartsType extends AbstractType
|
|||
],
|
||||
'constraints' => [
|
||||
new UniqueObjectCollection(message: 'project.bom_entry.part_already_in_bom', fields: ['part']),
|
||||
new UniqueObjectCollection(message: 'project.bom_entry.assembly_already_in_bom', fields: ['assembly']),
|
||||
new UniqueObjectCollection(message: 'project.bom_entry.name_already_in_bom', fields: ['name']),
|
||||
]
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -1,124 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
use App\Entity\Attachments\Attachment;
|
||||
use App\Services\Attachments\AssemblyPreviewGenerator;
|
||||
use App\Services\Attachments\AttachmentURLGenerator;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\ChoiceList\ChoiceList;
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
use Symfony\Component\Form\Event\PreSetDataEvent;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
class AssemblySelectType extends AbstractType implements DataMapperInterface
|
||||
{
|
||||
public function __construct(private readonly UrlGeneratorInterface $urlGenerator, private readonly EntityManagerInterface $em, private readonly AssemblyPreviewGenerator $previewGenerator, private readonly AttachmentURLGenerator $attachmentURLGenerator)
|
||||
{
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
//At initialization, we have to fill the form element with our selected data, so the user can see it
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) {
|
||||
$form = $event->getForm();
|
||||
$config = $form->getConfig()->getOptions();
|
||||
$data = $event->getData() ?? [];
|
||||
|
||||
$config['compound'] = false;
|
||||
$config['choices'] = is_iterable($data) ? $data : [$data];
|
||||
$config['error_bubbling'] = true;
|
||||
|
||||
$form->add('autocomplete', EntityType::class, $config);
|
||||
});
|
||||
|
||||
//After form submit, we have to add the selected element as choice, otherwise the form will not accept this element
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
|
||||
$data = $event->getData();
|
||||
$form = $event->getForm();
|
||||
$options = $form->get('autocomplete')->getConfig()->getOptions();
|
||||
|
||||
|
||||
if (!isset($data['autocomplete']) || '' === $data['autocomplete'] || empty($data['autocomplete'])) {
|
||||
$options['choices'] = [];
|
||||
} else {
|
||||
//Extract the ID from the submitted data
|
||||
$id = $data['autocomplete'];
|
||||
//Find the element in the database
|
||||
$element = $this->em->find($options['class'], $id);
|
||||
|
||||
//Add the element as choice
|
||||
$options['choices'] = [$element];
|
||||
$options['error_bubbling'] = true;
|
||||
$form->add('autocomplete', EntityType::class, $options);
|
||||
}
|
||||
});
|
||||
|
||||
$builder->setDataMapper($this);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'class' => Assembly::class,
|
||||
'choice_label' => 'name',
|
||||
'compound' => true,
|
||||
'error_bubbling' => false,
|
||||
]);
|
||||
|
||||
error_log($this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__']));
|
||||
|
||||
$resolver->setDefaults([
|
||||
'attr' => [
|
||||
'data-controller' => 'elements--assembly-select',
|
||||
'data-autocomplete' => $this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__']),
|
||||
'autocomplete' => 'off',
|
||||
],
|
||||
]);
|
||||
|
||||
$resolver->setDefaults([
|
||||
//Prefill the selected choice with the needed data, so the user can see it without an additional Ajax request
|
||||
'choice_attr' => ChoiceList::attr($this, function (?Assembly $assembly) {
|
||||
if($assembly instanceof Assembly) {
|
||||
//Determine the picture to show:
|
||||
$preview_attachment = $this->previewGenerator->getTablePreviewAttachment($assembly);
|
||||
if ($preview_attachment instanceof Attachment) {
|
||||
$preview_url = $this->attachmentURLGenerator->getThumbnailURL($preview_attachment,
|
||||
'thumbnail_sm');
|
||||
} else {
|
||||
$preview_url = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $assembly instanceof Assembly ? [
|
||||
'data-description' => $assembly->getDescription() ? mb_strimwidth($assembly->getDescription(), 0, 127, '...') : '',
|
||||
'data-category' => '',
|
||||
'data-footprint' => '',
|
||||
'data-image' => $preview_url,
|
||||
] : [];
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
public function mapDataToForms($data, \Traversable $forms): void
|
||||
{
|
||||
$form = current(iterator_to_array($forms, false));
|
||||
$form->setData($data);
|
||||
}
|
||||
|
||||
public function mapFormsToData(\Traversable $forms, &$data): void
|
||||
{
|
||||
$form = current(iterator_to_array($forms, false));
|
||||
$data = $form->getData();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,13 +22,10 @@ declare(strict_types=1);
|
|||
*/
|
||||
namespace App\Helpers\Projects;
|
||||
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
use App\Entity\AssemblySystem\AssemblyBOMEntry;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Entity\Parts\PartLot;
|
||||
use App\Entity\ProjectSystem\Project;
|
||||
use App\Entity\ProjectSystem\ProjectBOMEntry;
|
||||
use App\Helpers\Assemblies\AssemblyBuildRequest;
|
||||
use App\Validator\Constraints\ProjectSystem\ValidProjectBuildRequest;
|
||||
|
||||
/**
|
||||
|
|
@ -82,7 +79,7 @@ final class ProjectBuildRequest
|
|||
//Completely reset the array
|
||||
$this->withdraw_amounts = [];
|
||||
|
||||
//Now create an array for each part BOM entry
|
||||
//Now create an array for each BOM entry
|
||||
foreach ($this->getPartBomEntries() as $bom_entry) {
|
||||
$remaining_amount = $this->getNeededAmountForBOMEntry($bom_entry);
|
||||
foreach($this->getPartLotsForBOMEntry($bom_entry) as $lot) {
|
||||
|
|
@ -91,21 +88,6 @@ final class ProjectBuildRequest
|
|||
$remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]);
|
||||
}
|
||||
}
|
||||
|
||||
//Now create an array for each assembly BOM entry
|
||||
foreach ($this->getAssemblyBomEntries() as $assemblyBomEntry) {
|
||||
$assemblyBuildRequest = new AssemblyBuildRequest($assemblyBomEntry->getAssembly(), $this->number_of_builds);
|
||||
|
||||
//Add fields for assembly bom entries
|
||||
foreach ($assemblyBuildRequest->getPartBomEntries() as $partBomEntry) {
|
||||
$remaining_amount = $assemblyBuildRequest->getNeededAmountForBOMEntry($partBomEntry) * $assemblyBomEntry->getQuantity();
|
||||
|
||||
foreach ($assemblyBuildRequest->getPartLotsForBOMEntry($partBomEntry) as $lot) {
|
||||
$this->withdraw_amounts[$lot->getID()] = min($remaining_amount, $lot->getAmount());
|
||||
$remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -248,77 +230,12 @@ final class ProjectBuildRequest
|
|||
{
|
||||
$this->ensureBOMEntryValid($projectBOMEntry);
|
||||
|
||||
if (!$projectBOMEntry->getPart() instanceof Part && !$projectBOMEntry->getAssembly() instanceof Assembly) {
|
||||
if (!$projectBOMEntry->getPart() instanceof Part) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//Filter out all lots which have unknown instock
|
||||
if ($projectBOMEntry->getPart() instanceof Part) {
|
||||
return $projectBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->toArray();
|
||||
} elseif ($projectBOMEntry->getAssembly() instanceof Assembly) {
|
||||
$assemblyBuildRequest = new AssemblyBuildRequest($projectBOMEntry->getAssembly(), $this->number_of_builds);
|
||||
|
||||
//Add fields for assembly bom entries
|
||||
$result = [];
|
||||
foreach ($assemblyBuildRequest->getPartBomEntries() as $assemblyBOMEntry) {
|
||||
$tmp = $assemblyBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->toArray();
|
||||
$result = array_merge($result, $tmp);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all available assembly BOM-entries with no part assigned.
|
||||
* @return AssemblyBOMEntry[]|null Returns null if no entries found
|
||||
*/
|
||||
public function getAssemblyBomEntriesWithoutPart(ProjectBOMEntry $projectBOMEntry): ?array
|
||||
{
|
||||
$this->ensureBOMEntryValid($projectBOMEntry);
|
||||
|
||||
if (!$projectBOMEntry->getAssembly() instanceof Assembly) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$assemblyBuildRequest = new AssemblyBuildRequest($projectBOMEntry->getAssembly(), $this->number_of_builds);
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($assemblyBuildRequest->getBomEntries() as $assemblyBOMEntry) {
|
||||
if ($assemblyBOMEntry->getPart() === null) {
|
||||
$result[] = $assemblyBOMEntry;
|
||||
}
|
||||
}
|
||||
|
||||
return count($result) > 0 ? $result : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all available assembly BOM-entries with no part assigned.
|
||||
* @return AssemblyBOMEntry[]|null Returns null if no entries found
|
||||
*/
|
||||
public function getAssemblyBomEntriesWithPartNoStock(ProjectBOMEntry $projectBOMEntry): ?array
|
||||
{
|
||||
$this->ensureBOMEntryValid($projectBOMEntry);
|
||||
|
||||
if (!$projectBOMEntry->getAssembly() instanceof Assembly) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$assemblyBuildRequest = new AssemblyBuildRequest($projectBOMEntry->getAssembly(), $this->number_of_builds);
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($assemblyBuildRequest->getBomEntries() as $assemblyBOMEntry) {
|
||||
if ($assemblyBOMEntry->getPart() instanceof Part && $assemblyBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->count() === 0) {
|
||||
$result[] = $assemblyBOMEntry;
|
||||
}
|
||||
}
|
||||
|
||||
return count($result) > 0 ? $result : null;
|
||||
return $projectBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -349,15 +266,6 @@ final class ProjectBuildRequest
|
|||
return $this->project->getBomEntries()->filter(fn(ProjectBOMEntry $entry) => $entry->isPartBomEntry())->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the all assembly bom entries that have to be built.
|
||||
* @return ProjectBOMEntry[]
|
||||
*/
|
||||
public function getAssemblyBomEntries(): array
|
||||
{
|
||||
return $this->project->getBomEntries()->filter(fn(ProjectBOMEntry $entry) => $entry->isAssemblyBomEntry())->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns which project should be build
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
|
||||
/**
|
||||
* @template TEntityClass of Assembly
|
||||
* @extends DBElementRepository<TEntityClass>
|
||||
*/
|
||||
class AssemblyRepository extends StructuralDBElementRepository
|
||||
{
|
||||
/**
|
||||
* @return Assembly[]
|
||||
*/
|
||||
public function autocompleteSearch(string $query, int $max_limits = 50): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('assembly');
|
||||
$qb->select('assembly')
|
||||
->where('ILIKE(assembly.name, :query) = TRUE')
|
||||
->orWhere('ILIKE(assembly.description, :query) = TRUE');
|
||||
|
||||
$qb->setParameter('query', '%'.$query.'%');
|
||||
|
||||
$qb->setMaxResults($max_limits);
|
||||
$qb->orderBy('NATSORT(assembly.name)', 'ASC');
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Attachments;
|
||||
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
use App\Entity\Attachments\Attachment;
|
||||
|
||||
class AssemblyPreviewGenerator
|
||||
{
|
||||
public function __construct(protected AttachmentManager $attachmentHelper)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of attachments that can be used for previewing the assembly ordered by priority.
|
||||
*
|
||||
* @param Assembly $assembly the assembly for which the attachments should be determined
|
||||
*
|
||||
* @return (Attachment|null)[]
|
||||
*
|
||||
* @psalm-return list<Attachment|null>
|
||||
*/
|
||||
public function getPreviewAttachments(Assembly $assembly): array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
//Master attachment has top priority
|
||||
$attachment = $assembly->getMasterPictureAttachment();
|
||||
if ($this->isAttachmentValidPicture($attachment)) {
|
||||
$list[] = $attachment;
|
||||
}
|
||||
|
||||
//Then comes the other images of the assembly
|
||||
foreach ($assembly->getAttachments() as $attachment) {
|
||||
//Dont show the master attachment twice
|
||||
if ($this->isAttachmentValidPicture($attachment) && $attachment !== $assembly->getMasterPictureAttachment()) {
|
||||
$list[] = $attachment;
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines what attachment should be used for previewing a assembly (especially in assembly table).
|
||||
* The returned attachment is guaranteed to be existing and be a picture.
|
||||
*
|
||||
* @param Assembly $assembly The assembly for which the attachment should be determined
|
||||
*/
|
||||
public function getTablePreviewAttachment(Assembly $assembly): ?Attachment
|
||||
{
|
||||
$attachment = $assembly->getMasterPictureAttachment();
|
||||
if ($this->isAttachmentValidPicture($attachment)) {
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a attachment is exising and a valid picture.
|
||||
*
|
||||
* @param Attachment|null $attachment the attachment that should be checked
|
||||
*
|
||||
* @return bool true if the attachment is valid
|
||||
*/
|
||||
protected function isAttachmentValidPicture(?Attachment $attachment): bool
|
||||
{
|
||||
return $attachment instanceof Attachment
|
||||
&& $attachment->isPicture()
|
||||
&& $this->attachmentHelper->isFileExisting($attachment);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,13 +22,10 @@ declare(strict_types=1);
|
|||
*/
|
||||
namespace App\Services\ProjectSystem;
|
||||
|
||||
use App\Entity\AssemblySystem\AssemblyBOMEntry;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Entity\ProjectSystem\Project;
|
||||
use App\Entity\ProjectSystem\ProjectBOMEntry;
|
||||
use App\Helpers\Assemblies\AssemblyBuildRequest;
|
||||
use App\Helpers\Projects\ProjectBuildRequest;
|
||||
use App\Services\AssemblySystem\AssemblyBuildHelper;
|
||||
use App\Services\Parts\PartLotWithdrawAddHelper;
|
||||
|
||||
/**
|
||||
|
|
@ -36,10 +33,8 @@ use App\Services\Parts\PartLotWithdrawAddHelper;
|
|||
*/
|
||||
class ProjectBuildHelper
|
||||
{
|
||||
public function __construct(
|
||||
private readonly PartLotWithdrawAddHelper $withdrawAddHelper,
|
||||
private readonly AssemblyBuildHelper $assemblyBuildHelper
|
||||
) {
|
||||
public function __construct(private readonly PartLotWithdrawAddHelper $withdraw_add_helper)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -71,16 +66,12 @@ class ProjectBuildHelper
|
|||
$maximum_buildable_count = PHP_INT_MAX;
|
||||
foreach ($project->getBomEntries() as $bom_entry) {
|
||||
//Skip BOM entries without a part (as we can not determine that)
|
||||
if (!$bom_entry->isPartBomEntry() && $bom_entry->getAssembly() === null) {
|
||||
if (!$bom_entry->isPartBomEntry()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//The maximum buildable count for the whole project is the minimum of all BOM entries
|
||||
if ($bom_entry->getPart() !== null) {
|
||||
$maximum_buildable_count = min($maximum_buildable_count, $this->getMaximumBuildableCountForBOMEntry($bom_entry));
|
||||
} elseif ($bom_entry->getAssembly() !== null) {
|
||||
$maximum_buildable_count = min($maximum_buildable_count, $this->assemblyBuildHelper->getMaximumBuildableCount($bom_entry->getAssembly()));
|
||||
}
|
||||
$maximum_buildable_count = min($maximum_buildable_count, $this->getMaximumBuildableCountForBOMEntry($bom_entry));
|
||||
}
|
||||
|
||||
return $maximum_buildable_count;
|
||||
|
|
@ -106,10 +97,10 @@ class ProjectBuildHelper
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the project or assembly BOM entries for which parts are missing in the stock for the given number of builds
|
||||
* Returns the project BOM entries for which parts are missing in the stock for the given number of builds
|
||||
* @param Project $project The project for which the BOM entries should be checked
|
||||
* @param int $number_of_builds How often should the project be build?
|
||||
* @return ProjectBOMEntry[]|AssemblyBOMEntry[]
|
||||
* @return ProjectBOMEntry[]
|
||||
*/
|
||||
public function getNonBuildableProjectBomEntries(Project $project, int $number_of_builds = 1): array
|
||||
{
|
||||
|
|
@ -117,29 +108,24 @@ class ProjectBuildHelper
|
|||
throw new \InvalidArgumentException('The number of builds must be greater than 0!');
|
||||
}
|
||||
|
||||
$nonBuildableEntries = [];
|
||||
$non_buildable_entries = [];
|
||||
|
||||
foreach ($project->getBomEntries() as $bomEntry) {
|
||||
$part = $bomEntry->getPart();
|
||||
|
||||
//Skip BOM entries without a part (as we can not determine that)
|
||||
if (!$part instanceof Part && $bomEntry->getAssembly() === null) {
|
||||
if (!$part instanceof Part) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($bomEntry->getPart() !== null) {
|
||||
$amount_sum = $part->getAmountSum();
|
||||
$amount_sum = $part->getAmountSum();
|
||||
|
||||
if ($amount_sum < $bomEntry->getQuantity() * $number_of_builds) {
|
||||
$nonBuildableEntries[] = $bomEntry;
|
||||
}
|
||||
} elseif ($bomEntry->getAssembly() !== null) {
|
||||
$nonBuildableAssemblyEntries = $this->assemblyBuildHelper->getNonBuildableAssemblyBomEntries($bomEntry->getAssembly(), $number_of_builds);
|
||||
$nonBuildableEntries = array_merge($nonBuildableEntries, $nonBuildableAssemblyEntries);
|
||||
if ($amount_sum < $bomEntry->getQuantity() * $number_of_builds) {
|
||||
$non_buildable_entries[] = $bomEntry;
|
||||
}
|
||||
}
|
||||
|
||||
return $nonBuildableEntries;
|
||||
return $non_buildable_entries;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -147,37 +133,22 @@ class ProjectBuildHelper
|
|||
* The ProjectBuildRequest has to be validated before!!
|
||||
* You have to flush changes to DB afterward
|
||||
*/
|
||||
public function doBuild(ProjectBuildRequest $projectBuildRequest): void
|
||||
public function doBuild(ProjectBuildRequest $buildRequest): void
|
||||
{
|
||||
$message = $projectBuildRequest->getComment();
|
||||
$message .= ' (Project build: '.$projectBuildRequest->getProject()->getName().')';
|
||||
$message = $buildRequest->getComment();
|
||||
$message .= ' (Project build: '.$buildRequest->getProject()->getName().')';
|
||||
|
||||
foreach ($projectBuildRequest->getPartBomEntries() as $bomEntry) {
|
||||
foreach ($projectBuildRequest->getPartLotsForBOMEntry($bomEntry) as $partLot) {
|
||||
$amount = $projectBuildRequest->getLotWithdrawAmount($partLot);
|
||||
foreach ($buildRequest->getPartBomEntries() as $bom_entry) {
|
||||
foreach ($buildRequest->getPartLotsForBOMEntry($bom_entry) as $part_lot) {
|
||||
$amount = $buildRequest->getLotWithdrawAmount($part_lot);
|
||||
if ($amount > 0) {
|
||||
$this->withdrawAddHelper->withdraw($partLot, $amount, $message);
|
||||
$this->withdraw_add_helper->withdraw($part_lot, $amount, $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($projectBuildRequest->getAssemblyBomEntries() as $bomEntry) {
|
||||
$assemblyBuildRequest = new AssemblyBuildRequest($bomEntry->getAssembly(), $projectBuildRequest->getNumberOfBuilds());
|
||||
|
||||
//Add fields for assembly bom entries
|
||||
foreach ($assemblyBuildRequest->getPartBomEntries() as $partBomEntry) {
|
||||
foreach ($assemblyBuildRequest->getPartLotsForBOMEntry($partBomEntry) as $partLot) {
|
||||
//Read amount from build configuration of the projectBuildRequest
|
||||
$amount = $projectBuildRequest->getLotWithdrawAmount($partLot);
|
||||
if ($amount > 0) {
|
||||
$this->withdrawAddHelper->withdraw($partLot, $amount, $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($projectBuildRequest->getAddBuildsToBuildsPart()) {
|
||||
$this->withdrawAddHelper->add($projectBuildRequest->getBuildsPartLot(), $projectBuildRequest->getNumberOfBuilds(), $message);
|
||||
if ($buildRequest->getAddBuildsToBuildsPart()) {
|
||||
$this->withdraw_add_helper->add($buildRequest->getBuildsPartLot(), $buildRequest->getNumberOfBuilds(), $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue