mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-07-15 04:44:36 +02:00
Added way to batch edit the location of parts with a single stock
This commit is contained in:
parent
68124a340b
commit
48c70c3bb4
5 changed files with 120 additions and 36 deletions
|
@ -29,6 +29,7 @@ use App\DataTables\PartsDataTable;
|
|||
use App\Entity\Parts\Category;
|
||||
use App\Entity\Parts\Footprint;
|
||||
use App\Entity\Parts\Manufacturer;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Entity\Parts\StorageLocation;
|
||||
use App\Entity\Parts\Supplier;
|
||||
use App\Exceptions\InvalidRegexException;
|
||||
|
@ -43,8 +44,11 @@ use Symfony\Component\Form\FormInterface;
|
|||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Translation\TranslatableMessage;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function Symfony\Component\Translation\t;
|
||||
|
||||
class PartListsController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly NodesListBuilder $nodesListBuilder, private readonly DataTableFactory $dataTableFactory, private readonly TranslatorInterface $translator)
|
||||
|
@ -71,13 +75,32 @@ class PartListsController extends AbstractController
|
|||
if (null === $action || null === $ids) {
|
||||
$this->addFlash('error', 'part.table.actions.no_params_given');
|
||||
} else {
|
||||
$errors = [];
|
||||
|
||||
$parts = $actionHandler->idStringToArray($ids);
|
||||
$redirectResponse = $actionHandler->handleAction($action, $parts, $target ? (int) $target : null, $redirect);
|
||||
$redirectResponse = $actionHandler->handleAction($action, $parts, $target ? (int) $target : null, $redirect, $errors);
|
||||
|
||||
//Save changes
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->addFlash('success', 'part.table.actions.success');
|
||||
if (count($errors) === 0) {
|
||||
$this->addFlash('success', 'part.table.actions.success');
|
||||
} else {
|
||||
$this->addFlash('error', t('part.table.actions.error', ['%count%' => count($errors)]));
|
||||
//Create a flash message for each error
|
||||
foreach ($errors as $error) {
|
||||
/** @var Part $part */
|
||||
$part = $error['part'];
|
||||
|
||||
$this->addFlash('error',
|
||||
t('part.table.actions.error_detail', [
|
||||
'%part_name%' => $part->getName(),
|
||||
'%part_id%' => $part->getID(),
|
||||
'%message%' => $error['message']
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If the action handler returned a response, we use it, otherwise we redirect back to the previous page.
|
||||
|
|
|
@ -29,6 +29,7 @@ use App\Entity\Parts\Category;
|
|||
use App\Entity\Parts\Footprint;
|
||||
use App\Entity\Parts\Manufacturer;
|
||||
use App\Entity\Parts\MeasurementUnit;
|
||||
use App\Entity\Parts\StorageLocation;
|
||||
use App\Entity\ProjectSystem\Project;
|
||||
use App\Form\Type\Helper\StructuralEntityChoiceHelper;
|
||||
use App\Services\Trees\NodesListBuilder;
|
||||
|
@ -78,6 +79,12 @@ class SelectAPIController extends AbstractController
|
|||
return $this->getResponseForClass(Project::class, false);
|
||||
}
|
||||
|
||||
#[Route(path: '/storage_location', name: 'select_storage_location')]
|
||||
public function locations(): Response
|
||||
{
|
||||
return $this->getResponseForClass(StorageLocation::class, true);
|
||||
}
|
||||
|
||||
#[Route(path: '/export_level', name: 'select_export_level')]
|
||||
public function exportLevel(): Response
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
|||
*/
|
||||
namespace App\Services\Parts;
|
||||
|
||||
use App\Entity\Parts\StorageLocation;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use App\Entity\Parts\Category;
|
||||
use App\Entity\Parts\Footprint;
|
||||
|
@ -35,6 +36,9 @@ use InvalidArgumentException;
|
|||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Contracts\Translation\TranslatableInterface;
|
||||
|
||||
use function Symfony\Component\Translation\t;
|
||||
|
||||
final class PartsTableActionHandler
|
||||
{
|
||||
|
@ -61,8 +65,9 @@ final class PartsTableActionHandler
|
|||
/**
|
||||
* @param Part[] $selected_parts
|
||||
* @return RedirectResponse|null Returns a redirect response if the user should be redirected to another page, otherwise null
|
||||
* @phpstan-param-out array<array{'part': Part, 'message': string|TranslatableInterface}> $errors
|
||||
*/
|
||||
public function handleAction(string $action, array $selected_parts, ?int $target_id, ?string $redirect_url = null): ?RedirectResponse
|
||||
public function handleAction(string $action, array $selected_parts, ?int $target_id, ?string $redirect_url = null, array &$errors = []): ?RedirectResponse
|
||||
{
|
||||
if ($action === 'add_to_project') {
|
||||
return new RedirectResponse(
|
||||
|
@ -161,6 +166,29 @@ implode(',', array_map(static fn (PartLot $lot) => $lot->getID(), $part->getPart
|
|||
$this->denyAccessUnlessGranted('@measurement_units.read');
|
||||
$part->setPartUnit(null === $target_id ? null : $this->entityManager->find(MeasurementUnit::class, $target_id));
|
||||
break;
|
||||
case 'change_location':
|
||||
$this->denyAccessUnlessGranted('@storelocations.read');
|
||||
//Retrieve the first part lot and set the location for it
|
||||
$part_lots = $part->getPartLots();
|
||||
if ($part_lots->count() > 0) {
|
||||
if ($part_lots->count() > 1) {
|
||||
$errors[] = [
|
||||
'part' => $part,
|
||||
'message' => t('parts.table.action_handler.error.part_lots_multiple'),
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
$part_lot = $part_lots->first();
|
||||
$part_lot->setStorageLocation(null === $target_id ? null : $this->entityManager->find(StorageLocation::class, $target_id));
|
||||
} else { //Create a new part lot if there are none
|
||||
$part_lot = new PartLot();
|
||||
$part_lot->setPart($part);
|
||||
$part_lot->setInstockUnknown(true); //We do not know how many parts are in stock, so we set it to true
|
||||
$part_lot->setStorageLocation(null === $target_id ? null : $this->entityManager->find(StorageLocation::class, $target_id));
|
||||
$this->entityManager->persist($part_lot);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException('The given action is unknown! ('.$action.')');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue