mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-21 09:35:49 +02:00
Added the possibility to edit/create/delete part lots.
This commit is contained in:
parent
6a0adae8f3
commit
813e7dc85b
11 changed files with 226 additions and 43 deletions
|
@ -82,7 +82,7 @@ class PartController extends AbstractController
|
||||||
return $this->render('Parts/edit/edit_part_info.html.twig',
|
return $this->render('Parts/edit/edit_part_info.html.twig',
|
||||||
[
|
[
|
||||||
'part' => $part,
|
'part' => $part,
|
||||||
'form_main' => $form->createView(),
|
'form' => $form->createView(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,8 @@ class Part extends AttachmentContainingDBElement
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ?PartLot[]
|
* @var ?PartLot[]
|
||||||
* @ORM\OneToMany(targetEntity="PartLot", mappedBy="part")
|
* @ORM\OneToMany(targetEntity="PartLot", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||||
|
* @Assert\Valid()
|
||||||
*/
|
*/
|
||||||
protected $partLots;
|
protected $partLots;
|
||||||
|
|
||||||
|
@ -593,6 +594,18 @@ class Part extends AttachmentContainingDBElement
|
||||||
return $this->partLots;
|
return $this->partLots;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addPartLot(PartLot $lot): Part
|
||||||
|
{
|
||||||
|
$lot->setPart($this);
|
||||||
|
$this->partLots->add($lot);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removePartLot(PartLot $lot): Part
|
||||||
|
{
|
||||||
|
$this->partLots->removeElement($lot);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the assigned manufacturer product number (MPN) for this part.
|
* Returns the assigned manufacturer product number (MPN) for this part.
|
||||||
|
|
|
@ -56,13 +56,13 @@ class PartLot extends DBElement
|
||||||
* @var string A short description about this lot, shown in table
|
* @var string A short description about this lot, shown in table
|
||||||
* @ORM\Column(type="text")
|
* @ORM\Column(type="text")
|
||||||
*/
|
*/
|
||||||
protected $description;
|
protected $description = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string A comment stored with this lot.
|
* @var string A comment stored with this lot.
|
||||||
* @ORM\Column(type="text")
|
* @ORM\Column(type="text")
|
||||||
*/
|
*/
|
||||||
protected $comment;
|
protected $comment = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ?\DateTime Set a time until when the lot must be used.
|
* @var ?\DateTime Set a time until when the lot must be used.
|
||||||
|
@ -90,21 +90,21 @@ class PartLot extends DBElement
|
||||||
* @var bool If this is set to true, the instock amount is marked as not known
|
* @var bool If this is set to true, the instock amount is marked as not known
|
||||||
* @ORM\Column(type="boolean")
|
* @ORM\Column(type="boolean")
|
||||||
*/
|
*/
|
||||||
protected $instock_unknown;
|
protected $instock_unknown = false;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var float For continuos sizes (length, volume, etc.) the instock is saved here.
|
* @var float For continuos sizes (length, volume, etc.) the instock is saved here.
|
||||||
* @ORM\Column(type="float")
|
* @ORM\Column(type="float")
|
||||||
* @Assert\Positive()
|
* @Assert\PositiveOrZero()
|
||||||
*/
|
*/
|
||||||
protected $amount;
|
protected $amount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var boolean Determines if this lot was manually marked for refilling.
|
* @var boolean Determines if this lot was manually marked for refilling.
|
||||||
* @ORM\Column(type="boolean")
|
* @ORM\Column(type="boolean")
|
||||||
*/
|
*/
|
||||||
protected $needs_refill;
|
protected $needs_refill = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ID as an string, defined by the element class.
|
* Returns the ID as an string, defined by the element class.
|
||||||
|
@ -198,7 +198,7 @@ class PartLot extends DBElement
|
||||||
* Gets the storage locatiion, where this part lot is stored.
|
* Gets the storage locatiion, where this part lot is stored.
|
||||||
* @return Storelocation The store location where this part is stored
|
* @return Storelocation The store location where this part is stored
|
||||||
*/
|
*/
|
||||||
public function getStorageLocation(): Storelocation
|
public function getStorageLocation(): ?Storelocation
|
||||||
{
|
{
|
||||||
return $this->storage_location;
|
return $this->storage_location;
|
||||||
}
|
}
|
||||||
|
@ -260,7 +260,7 @@ class PartLot extends DBElement
|
||||||
*/
|
*/
|
||||||
public function getAmount(): float
|
public function getAmount(): float
|
||||||
{
|
{
|
||||||
if (!$this->part->useFloatAmount()) {
|
if ($this->part instanceof Part && !$this->part->useFloatAmount()) {
|
||||||
return round($this->amount);
|
return round($this->amount);
|
||||||
}
|
}
|
||||||
return (float) $this->amount;
|
return (float) $this->amount;
|
||||||
|
@ -269,6 +269,7 @@ class PartLot extends DBElement
|
||||||
public function setAmount(float $new_amount): PartLot
|
public function setAmount(float $new_amount): PartLot
|
||||||
{
|
{
|
||||||
$this->amount = $new_amount;
|
$this->amount = $new_amount;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ use FOS\CKEditorBundle\Form\Type\CKEditorType;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\UrlType;
|
use Symfony\Component\Form\Extension\Core\Type\UrlType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
@ -119,14 +120,18 @@ class PartBaseType extends AbstractType
|
||||||
->add('favorite', CheckboxType::class, ['label_attr'=> ['class' => 'checkbox-custom'],
|
->add('favorite', CheckboxType::class, ['label_attr'=> ['class' => 'checkbox-custom'],
|
||||||
'required' => false, 'label' => 'part.edit.is_favorite']);
|
'required' => false, 'label' => 'part.edit.is_favorite']);
|
||||||
|
|
||||||
|
//Part Lots section
|
||||||
|
$builder->add('partLots', CollectionType::class, [
|
||||||
|
'entry_type' => PartLotType::class,
|
||||||
|
'allow_add' => true, 'allow_delete' => true,
|
||||||
|
'label' => false,
|
||||||
|
'by_reference' => false
|
||||||
|
]);
|
||||||
|
|
||||||
$builder
|
$builder
|
||||||
//Buttons
|
//Buttons
|
||||||
->add('save1', SubmitType::class, ['label' => 'part.edit.save'])
|
->add('save', SubmitType::class, ['label' => 'part.edit.save'])
|
||||||
->add('reset1', ResetType::class, ['label' => 'part.edit.reset'])
|
->add('reset', ResetType::class, ['label' => 'part.edit.reset']);
|
||||||
->add('save2', SubmitType::class, ['label' => 'part.edit.save'])
|
|
||||||
->add('reset2', ResetType::class, ['label' => 'part.edit.reset'])
|
|
||||||
->add('save3', SubmitType::class, ['label' => 'part.edit.save'])
|
|
||||||
->add('reset3', ResetType::class, ['label' => 'part.edit.reset']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
|
90
src/Form/Part/PartLotType.php
Normal file
90
src/Form/Part/PartLotType.php
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* part-db version 0.1
|
||||||
|
* Copyright (C) 2005 Christoph Lechner
|
||||||
|
* http://www.cl-projects.de/
|
||||||
|
*
|
||||||
|
* part-db version 0.2+
|
||||||
|
* Copyright (C) 2009 K. Jacobs and others (see authors.php)
|
||||||
|
* http://code.google.com/p/part-db/
|
||||||
|
*
|
||||||
|
* Part-DB Version 0.4+
|
||||||
|
* Copyright (C) 2016 - 2019 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 General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Form\Part;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Entity\Parts\Part;
|
||||||
|
use App\Entity\Parts\PartLot;
|
||||||
|
use App\Entity\Parts\Storelocation;
|
||||||
|
use App\Form\Type\StructuralEntityType;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use function GuzzleHttp\Promise\queue;
|
||||||
|
|
||||||
|
class PartLotType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder->add('description', TextType::class, ['label' => 'part_lot.edit.description',
|
||||||
|
'required' => false, 'empty_data' => "", 'attr' => ['class' => 'form-control-sm']]);
|
||||||
|
|
||||||
|
$builder->add('storage_location', StructuralEntityType::class, ['class' => Storelocation::class,
|
||||||
|
'label' => 'part_lot.edit.location',
|
||||||
|
'disable_not_selectable' => true, 'attr' => ['class' => 'form-control-sm']]);
|
||||||
|
|
||||||
|
$builder->add('amount',NumberType::class, [ 'html5' => true,
|
||||||
|
'label' => 'part_lot.edit.amount',
|
||||||
|
'attr' => ['class' => 'form-control-sm', 'min' => 0, 'step' => 'any']
|
||||||
|
]);
|
||||||
|
$builder->add('instock_unknown', CheckboxType::class, ['required' => false,
|
||||||
|
'label' => 'part_lot.edit.instock_unknown',
|
||||||
|
'attr' => ['class' => 'form-control-sm'],
|
||||||
|
'label_attr'=> ['class' => 'checkbox-custom']]);
|
||||||
|
$builder->add('needs_refill', CheckboxType::class, ['label_attr'=> ['class' => 'checkbox-custom'],
|
||||||
|
'label' => 'part_lot.edit.needs_refill',
|
||||||
|
'attr' => ['class' => 'form-control-sm'],
|
||||||
|
'required' => false]);
|
||||||
|
$builder->add('expirationDate', DateTimeType::class, [
|
||||||
|
'label' => 'part_lot.edit.expiration_date',
|
||||||
|
'attr' => [],
|
||||||
|
'required' => false]);
|
||||||
|
|
||||||
|
$builder->add('comment', TextType::class, ['label' => 'part_lot.edit.comment',
|
||||||
|
'label' => 'part_lot.edit.comment',
|
||||||
|
'attr' => ['class' => 'form-control-sm'],
|
||||||
|
'required' => false, 'empty_data' => ""]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => PartLot::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
60
templates/Parts/edit/_lots.html.twig
Normal file
60
templates/Parts/edit/_lots.html.twig
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
{% set delete_btn %}
|
||||||
|
<button type="button" class="btn btn-danger lot_btn_delete" onclick="delete_lot_entry(this);">
|
||||||
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
|
{% trans %}part_lot.delete{% endtrans %}
|
||||||
|
</button>
|
||||||
|
{% endset %}
|
||||||
|
|
||||||
|
|
||||||
|
<table class="table table-striped" id="lots_table" data-prototype="{{ form_widget(form.partLots.vars.prototype)|e('html_attr') }}">
|
||||||
|
<tbody>
|
||||||
|
{% for lot in form.partLots %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ form_widget(lot) }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ delete_btn }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-success" onclick="create_lot_entry(this)">
|
||||||
|
<i class="fas fa-plus-square fa-fw"></i>
|
||||||
|
{% trans %}part_lot.create{% endtrans %}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function delete_lot_entry(btn) {
|
||||||
|
window.bootbox.confirm('{% trans %}part_lot.edit.delete.confirm{% endtrans %}', function (result) {
|
||||||
|
if(result) {
|
||||||
|
$(btn).parents("tr").remove();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_lot_entry(btn) {
|
||||||
|
//Determine the table, so we can determine, how many entries there are already.
|
||||||
|
$holder = $("#lots_table");
|
||||||
|
|
||||||
|
var index = $holder.find(":input").length;
|
||||||
|
var newForm = $holder.data("prototype");
|
||||||
|
|
||||||
|
//Increase the index
|
||||||
|
newForm = newForm.replace(/__name__/g, index);
|
||||||
|
newForm = '<td>' + newForm + '</td>';
|
||||||
|
$newFormRow = $('<tr></tr>').html(newForm);
|
||||||
|
|
||||||
|
//Add delete button
|
||||||
|
$btn = '<td>' + '{{ delete_btn|e('js') }}' + '</td>';
|
||||||
|
$newFormRow.append($btn);
|
||||||
|
|
||||||
|
$holder.append($newFormRow);
|
||||||
|
|
||||||
|
//Reinit the selectpickers
|
||||||
|
$(".selectpicker").selectpicker();
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,13 +1,11 @@
|
||||||
{{ form_row(form_main.name) }}
|
{{ form_row(form.name) }}
|
||||||
{{ form_row(form_main.description) }}
|
{{ form_row(form.description) }}
|
||||||
{{ form_row(form_main.category) }}
|
{{ form_row(form.category) }}
|
||||||
{{ form_row(form_main.tags) }}
|
{{ form_row(form.tags) }}
|
||||||
{{ form_row(form_main.minAmount) }}
|
{{ form_row(form.minAmount) }}
|
||||||
{{ form_row(form_main.partUnit)}}
|
{{ form_row(form.partUnit)}}
|
||||||
|
|
||||||
{{ form_row(form_main.footprint) }}
|
{{ form_row(form.footprint) }}
|
||||||
|
|
||||||
{{ form_row(form_main.comment) }}
|
{{ form_row(form.comment) }}
|
||||||
|
|
||||||
{{ form_row(form_main.save1) }}
|
|
||||||
{{ form_row(form_main.reset1) }}
|
|
|
@ -1,6 +1,3 @@
|
||||||
{{ form_row(form_main.manufacturer) }}
|
{{ form_row(form.manufacturer) }}
|
||||||
{{ form_row(form_main.manufacturer_product_number) }}
|
{{ form_row(form.manufacturer_product_number) }}
|
||||||
{{ form_row(form_main.manufacturer_product_url)}}
|
{{ form_row(form.manufacturer_product_url)}}
|
||||||
|
|
||||||
{{ form_row(form_main.save2) }}
|
|
||||||
{{ form_row(form_main.reset2) }}
|
|
|
@ -1,5 +1,2 @@
|
||||||
{{ form_row(form_main.needsReview) }}
|
{{ form_row(form.needsReview) }}
|
||||||
{{ form_row(form_main.favorite) }}
|
{{ form_row(form.favorite) }}
|
||||||
|
|
||||||
{{ form_row(form_main.save3) }}
|
|
||||||
{{ form_row(form_main.reset3) }}
|
|
|
@ -17,18 +17,34 @@
|
||||||
|
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" data-toggle="tab" role="tab" href="#common">{% trans %}part.edit.tab.common{% endtrans %}</a>
|
<a class="nav-link active" data-toggle="tab" role="tab" href="#common">
|
||||||
|
<i class="fas fa-id-card fa-fw"></i>
|
||||||
|
{% trans %}part.edit.tab.common{% endtrans %}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" data-toggle="tab" role="tab" href="#manufacturer">{% trans %}part.edit.tab.manufacturer{% endtrans %}</a>
|
<a class="nav-link" data-toggle="tab" role="tab" href="#manufacturer">
|
||||||
|
<i class="fas fa-industry fa-fw"></i>
|
||||||
|
{% trans %}part.edit.tab.manufacturer{% endtrans %}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" data-toggle="tab" role="tab" href="#options">{% trans %}part.edit.tab.options{% endtrans %}</a>
|
<a class="nav-link" data-toggle="tab" role="tab" href="#options">
|
||||||
|
<i class="fas fa-shapes fa-fw"></i>
|
||||||
|
{% trans %}part.edit.tab.options{% endtrans %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" data-toggle="tab" role="tab" href="#part_lots">
|
||||||
|
<i class="fas fa-boxes fa-fw"></i>
|
||||||
|
{% trans %}part.edit.tab.part_lots{% endtrans %}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
{{ form_start(form) }}
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
{{ form_start(form_main) }}
|
|
||||||
<div class="tab-pane fade show active p-2" id="common" role="tabpanel">
|
<div class="tab-pane fade show active p-2" id="common" role="tabpanel">
|
||||||
{% include "Parts/edit/_main.html.twig" %}
|
{% include "Parts/edit/_main.html.twig" %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -38,8 +54,14 @@
|
||||||
<div class="tab-pane fade p-2" id="options" role="tabpanel">
|
<div class="tab-pane fade p-2" id="options" role="tabpanel">
|
||||||
{% include "Parts/edit/_options.html.twig" %}
|
{% include "Parts/edit/_options.html.twig" %}
|
||||||
</div>
|
</div>
|
||||||
{{ form_end(form_main) }}
|
<div class="tab-pane fade p-2" id="part_lots" role="tabpanel">
|
||||||
|
{% include "Parts/edit/_lots.html.twig" %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
{{ form_row(form.save) }}
|
||||||
|
{{ form_row(form.reset) }}
|
||||||
|
{{ form_end(form) }}
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -1,4 +1,4 @@
|
||||||
{% extends "Parts/edit_part_info.html.twig" %}
|
{% extends "Parts/edit/edit_part_info.html.twig" %}
|
||||||
|
|
||||||
{% block card_border %}border-success{% endblock %}
|
{% block card_border %}border-success{% endblock %}
|
||||||
{% block card_type %}bg-success text-white{% endblock %}
|
{% block card_type %}bg-success text-white{% endblock %}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue