diff --git a/assets/controllers/pages/part_withdraw_modal_controller.js b/assets/controllers/pages/part_withdraw_modal_controller.js index 0e5c0fc5a..d14e1cf2f 100644 --- a/assets/controllers/pages/part_withdraw_modal_controller.js +++ b/assets/controllers/pages/part_withdraw_modal_controller.js @@ -6,6 +6,13 @@ export default class extends Controller connect() { this.element.addEventListener('show.bs.modal', event => this._handleModalOpen(event)); this.element.addEventListener('shown.bs.modal', event => this._handleModalShown(event)); + const newLotRadio = this.element.querySelector('input[name="target_id"][value="new"]'); + if (newLotRadio) { + //Any radio in the group can toggle the "new" radio off, so listen on all of them + this.element.querySelectorAll('input[name="target_id"]').forEach(radio => { + radio.addEventListener('change', () => this._toggleNewLotLocation(newLotRadio)); + }); + } } _handleModalOpen(event) { @@ -39,6 +46,7 @@ export default class extends Controller //Hide the move to lot select, if the action is not move (and unhide it, if it is) const moveToLotSelect = this.element.querySelector('#withdraw-modal-move-to'); + const newLotRadio = this.element.querySelector('input[name="target_id"][value="new"]'); if (action === 'move') { moveToLotSelect.classList.remove('d-none'); } else { @@ -51,10 +59,15 @@ export default class extends Controller moveToLotOptions.forEach(option => { if (option.getAttribute('value') === lotID) { option.parentElement.classList.add('d-none'); - option.selected = false; + option.checked = false; } }); + if (newLotRadio) { + newLotRadio.checked = false; + this._toggleNewLotLocation(newLotRadio); + } + //For adding parts there is no limit on the amount to add if (action == 'add') { amountInput.removeAttribute('max'); @@ -66,4 +79,17 @@ export default class extends Controller _handleModalShown(event) { this.element.querySelector('input[name="amount"]').focus(); } -} \ No newline at end of file + + _toggleNewLotLocation(newLotRadio) { + const newLotLocation = this.element.querySelector('#withdraw-modal-new-lot-location'); + if (!newLotLocation) { + return; + } + + newLotLocation.classList.toggle('d-none', !newLotRadio.checked); + const locationInput = newLotLocation.querySelector('select, input'); + if (locationInput) { + locationInput.required = newLotRadio.checked; + } + } +} diff --git a/src/Controller/PartController.php b/src/Controller/PartController.php index c4c0e5260..914a4c823 100644 --- a/src/Controller/PartController.php +++ b/src/Controller/PartController.php @@ -131,13 +131,24 @@ public function show( // Build the add-lot form for the INFO page modal (only when not in time-travel mode) $addLotForm = null; - if ($timeTravel_timestamp === null && $this->isGranted('edit', $part)) { + $moveNewLotForm = null; + if ($timeTravel_timestamp === null) { $newLot = new PartLot(); $newLot->setPart($part); - $addLotForm = $this->createForm(PartLotType::class, $newLot, [ - 'measurement_unit' => $part->getPartUnit(), - 'action' => $this->generateUrl('part_lot_add', ['id' => $part->getID()]), - ]); + if ($this->isGranted('edit', $part)) { + $addLotForm = $this->createForm(PartLotType::class, $newLot, [ + 'measurement_unit' => $part->getPartUnit(), + 'action' => $this->generateUrl('part_lot_add', ['id' => $part->getID()]), + ]); + } + + if ($this->isGranted('create', $newLot) && $this->isGranted('move', $newLot)) { + $moveNewLotForm = $this->createForm(PartLotType::class, $newLot, [ + 'measurement_unit' => $part->getPartUnit(), + //CSRF is already covered by the outer withdraw/move form's token + 'csrf_protection' => false, + ]); + } } return $this->render( @@ -153,6 +164,7 @@ public function show( 'withdraw_add_helper' => $withdrawAddHelper, 'highlightLotId' => $request->query->getInt('highlightLot', 0), 'add_lot_form' => $addLotForm, + 'move_new_lot_form' => $moveNewLotForm, ] ); } @@ -643,7 +655,26 @@ public function withdrawAddHandler(Part $part, Request $request, EntityManagerIn break; case "move": $this->denyAccessUnlessGranted('move', $partLot); - $this->denyAccessUnlessGranted('move', $targetLot); + if ($targetId === 'new') { + $targetLot = new PartLot(); + $targetLot->setPart($part); + $this->denyAccessUnlessGranted('create', $targetLot); + + $newLotForm = $this->createForm(PartLotType::class, $targetLot, [ + 'measurement_unit' => $part->getPartUnit(), + //CSRF is already covered by the outer withdraw/move form's token + 'csrf_protection' => false, + ]); + $newLotForm->handleRequest($request); + if (!$newLotForm->isSubmitted() || !$newLotForm->isValid() || !$targetLot->getStorageLocation()) { + $this->addFlash('error', 'part.created_flash.invalid'); + goto err; + } + + $em->persist($targetLot); + } else { + $this->denyAccessUnlessGranted('move', $targetLot); + } $withdrawAddHelper->move($partLot, $targetLot, $amount, $comment, $timestamp, $delete_lot_if_empty); break; default: diff --git a/templates/parts/info/_part_lots.html.twig b/templates/parts/info/_part_lots.html.twig index 7e53aec14..3e681da51 100644 --- a/templates/parts/info/_part_lots.html.twig +++ b/templates/parts/info/_part_lots.html.twig @@ -93,7 +93,7 @@ diff --git a/templates/parts/info/_withdraw_modal.html.twig b/templates/parts/info/_withdraw_modal.html.twig index 45f6799ec..75d891792 100644 --- a/templates/parts/info/_withdraw_modal.html.twig +++ b/templates/parts/info/_withdraw_modal.html.twig @@ -1,4 +1,7 @@