Add deletion fuinctionality to storages
This commit is contained in:
parent
bd2cb79e04
commit
f8e832deed
@ -85,7 +85,10 @@ class Storage(models.Model):
|
||||
if Storage.objects.exclude(id=self.id).filter(name=self.name, parent_storage__isnull=True).exists():
|
||||
if self.parent_storage is None:
|
||||
raise ValidationError('The fields name, parent_storage must make a unique set')
|
||||
super(Storage, self).validate_unique(exclude)
|
||||
try:
|
||||
super(Storage, self).validate_unique(exclude)
|
||||
except:
|
||||
raise
|
||||
|
||||
def get_total_stock_amount(self):
|
||||
stocks = Stock.objects.filter(storage=self)
|
||||
|
@ -100,6 +100,10 @@ class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
||||
base_title = 'Components'
|
||||
navbar_selected = 'Components'
|
||||
|
||||
class AddSubStorageForm(forms.Form):
|
||||
storage_name = forms.CharField(label="storage_name", initial='')
|
||||
responsible = forms.CharField(label='responsible_user')
|
||||
|
||||
class StockView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
||||
template_name = 'parts/stocks.html'
|
||||
base_title = 'Stocks'
|
||||
@ -122,13 +126,45 @@ class StockView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
||||
|
||||
context['low_stocks'] = low_stock_paginator.get_page(low_stock_page)
|
||||
context['storages'] = storage_paginator.get_page(storage_page)
|
||||
|
||||
add_stor_form = AddSubStorageForm()
|
||||
add_stor_form.fields['responsible'].initial = self.request.user.username
|
||||
context['add_storage_form'] = add_stor_form
|
||||
return context
|
||||
|
||||
class AddSubStorageForm(forms.Form):
|
||||
storage_name = forms.CharField(label="storage_name", initial='')
|
||||
responsible = forms.CharField(label='responsible_user')
|
||||
|
||||
def handle_add_storage(self, request, **kwargs):
|
||||
return_invalid_form = False
|
||||
|
||||
f = AddSubStorageForm(data=request.POST)
|
||||
if f.is_valid():
|
||||
new_storage_name = f.cleaned_data['storage_name']
|
||||
try:
|
||||
resp_user = User.objects.get(username=f.cleaned_data['responsible'])
|
||||
except Exception as _:
|
||||
resp_user = None
|
||||
f.add_error('responsible', 'Invalid Responsible User')
|
||||
return_invalid_form = True
|
||||
|
||||
if resp_user is not None:
|
||||
try:
|
||||
Storage.objects.create(name=new_storage_name, responsible=resp_user, parent_storage=None)
|
||||
except ValidationError as verr:
|
||||
return_invalid_form = True
|
||||
f.add_error('storage_name', ' .'.join(verr.messages))
|
||||
else:
|
||||
return_invalid_form = True
|
||||
|
||||
context = self.get_context_data(**kwargs)
|
||||
if return_invalid_form:
|
||||
context['add_storage_form'] = f
|
||||
|
||||
return self.render_to_response(context)
|
||||
|
||||
def post(self, request, **kwargs):
|
||||
if 'submit-add-storage' in request.POST:
|
||||
return self.handle_add_storage(request, **kwargs)
|
||||
|
||||
return super().post(request, **kwargs)
|
||||
|
||||
class StockViewDetail(LoginRequiredMixin, BaseTemplateMixin, DetailView):
|
||||
template_name = 'parts/stocks-detail.html'
|
||||
model = Storage
|
||||
@ -157,6 +193,7 @@ class StockViewDetail(LoginRequiredMixin, BaseTemplateMixin, DetailView):
|
||||
add_storage_form = AddSubStorageForm()
|
||||
add_storage_form.fields['responsible'].initial = self.request.user.username
|
||||
context['add_storage_form'] = add_storage_form
|
||||
context['delete_storage_error'] = None
|
||||
return context
|
||||
|
||||
def handle_add_storage_post(self, request, **kwargs):
|
||||
@ -175,10 +212,26 @@ class StockViewDetail(LoginRequiredMixin, BaseTemplateMixin, DetailView):
|
||||
context['add_storage_form'] = f
|
||||
return self.render_to_response(context)
|
||||
|
||||
def handle_del_storage_post(self, request, **kwargs):
|
||||
parent = self.object.parent_storage
|
||||
try:
|
||||
self.object.delete()
|
||||
except:
|
||||
context = self.get_context_data(**kwargs)
|
||||
context['delete_storage_errors'] = ['Error deleting Storage '+str(self.object)]
|
||||
return self.render_to_response(context)
|
||||
|
||||
if parent is None:
|
||||
return redirect('parts-stocks')
|
||||
else:
|
||||
return redirect(reverse('parts-stocks-detail', kwargs={'uuid':parent.id}))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
|
||||
if 'submit-add-storage' in request.POST:
|
||||
return self.handle_add_storage_post(request)
|
||||
return self.handle_add_storage_post(request, **kwargs)
|
||||
elif 'submit-delete-storage' in request.POST:
|
||||
return self.handle_del_storage_post(request, **kwargs)
|
||||
|
||||
return super().post(request, *args, **kwargs)
|
1
shimatta_kenkyusho/static/css/autocomplete.css
Normal file
1
shimatta_kenkyusho/static/css/autocomplete.css
Normal file
@ -0,0 +1 @@
|
||||
:root{--close-button:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M18.984 6.422 13.406 12l5.578 5.578-1.406 1.406L12 13.406l-5.578 5.578-1.406-1.406L10.594 12 5.016 6.422l1.406-1.406L12 10.594l5.578-5.578z'/%3E%3C/svg%3E");--loupe-icon:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23929292' d='M16.041 15.856a.995.995 0 0 0-.186.186A6.97 6.97 0 0 1 11 18c-1.933 0-3.682-.782-4.95-2.05S4 12.933 4 11s.782-3.682 2.05-4.95S9.067 4 11 4s3.682.782 4.95 2.05S18 9.067 18 11a6.971 6.971 0 0 1-1.959 4.856zm5.666 4.437-3.675-3.675A8.967 8.967 0 0 0 20 11c0-2.485-1.008-4.736-2.636-6.364S13.485 2 11 2 6.264 3.008 4.636 4.636 2 8.515 2 11s1.008 4.736 2.636 6.364S8.515 20 11 20a8.967 8.967 0 0 0 5.618-1.968l3.675 3.675a.999.999 0 1 0 1.414-1.414z'/%3E%3C/svg%3E")}.auto-search{display:block;position:relative;width:100%}.auto-search input{border:1px solid #d7d7d7;box-shadow:none;box-sizing:border-box;font-size:16px;padding:12px 45px 12px 10px;width:100%}.auto-search input:focus{border:1px solid #858585;outline:none}.auto-search input::-ms-clear{display:none}.auto-search ul{box-sizing:border-box;list-style:none;overflow:auto;padding:0}.auto-search ul li{cursor:pointer;margin:0;overflow:hidden;padding:10px;position:relative}.auto-search ul li:not(:last-child){border-top:none}.auto-search ul li[disabled]{background:#ececec;opacity:.5;pointer-events:none}.auto-search .auto-expanded{border:1px solid #858585;outline:none}.auto-search.loupe:before{filter:invert(60%)}.auto-is-loading:after{animation:auto-spinner .6s linear infinite;border-color:#d9d9d9 grey grey #d9d9d9;border-radius:50%;border-style:solid;border-width:2px;bottom:0;box-sizing:border-box;content:"";height:20px;margin:auto;position:absolute;right:10px;top:0;width:20px}.auto-is-loading .auto-clear{display:none}@keyframes auto-spinner{to{transform:rotate(1turn)}}li.loupe:before{bottom:auto;top:15px}.loupe input{padding:12px 45px 12px 40px}.loupe:before{background-image:var(--loupe-icon);bottom:0;content:"";height:17px;left:10px;margin:auto;position:absolute;top:0;width:17px}.auto-selected:before{opacity:1}.auto-clear{align-items:center;background-color:transparent;border:none;bottom:0;cursor:pointer;display:flex;height:auto;justify-content:center;margin:auto;position:absolute;right:0;top:0;width:40px}.auto-clear:before{content:var(--close-button);height:24px;line-height:100%;width:24px}.auto-clear span{display:none}.auto-wrapper{background-color:#fff;border:1px solid #858585;border-top:none;display:none;overflow:hidden}.auto-wrapper ul>.loupe{padding-left:40px}.auto-wrapper.auto-is-active{display:block;margin-top:-1px;position:absolute;width:100%;z-index:99999}.auto-selected{background-color:#e6e6e6}.auto-selected+li:before{border-top:none}.auto-error{border:1px solid #ff3838}.auto-error::placeholder{color:#f66;opacity:1}.hidden{display:none}
|
1
shimatta_kenkyusho/static/js/autocomplete.min.js
vendored
Normal file
1
shimatta_kenkyusho/static/js/autocomplete.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
shimatta_kenkyusho/static/js/autocomplete.umd.min.js
vendored
Normal file
1
shimatta_kenkyusho/static/js/autocomplete.umd.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -6,6 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'css/icons/bootstrap-icons.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'css/autocomplete.css' %}" rel="stylesheet">
|
||||
<title>{{ base.title }}</title>
|
||||
{% block customhead %}
|
||||
{% endblock customhead %}
|
||||
@ -53,7 +54,7 @@
|
||||
{% endblock content %}
|
||||
|
||||
<script type="text/javascript" src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
|
||||
|
||||
<script type="text/javascript" src="{% static 'js/autocomplete.min.js' %}"></script>
|
||||
{% block custom_scripts %}
|
||||
{% endblock custom_scripts %}
|
||||
|
||||
|
@ -0,0 +1,25 @@
|
||||
<div class="modal fade" id="delete-storage-modal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="staticBackdropLabel">Delete Storage</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="modal-body">
|
||||
<h2>Do you want to delete this storage and possibly all of its substorages?</h2>
|
||||
{% if err_msgs %}
|
||||
{% for error in err_msgs %}
|
||||
<p class="text-danger text-center">{{ error }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<input type="submit" class="btn btn-danger" value="Delete Storage" name="submit-delete-storage">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,43 @@
|
||||
<div class="modal fade" id="add-sub-modal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="staticBackdropLabel">Add Storage</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="modal-body">
|
||||
<label for="add-storage-name" class="form-label">Storage Name</label>
|
||||
<div class="input-group has-validation">
|
||||
<input value="{{form.storage_name.value}}" class="form-control{% if form.storage_name.errors or form.non_field_errors %} is-invalid{% endif %}" id="add-storage-name" name="{{form.storage_name.name}}" type="text" aria-describedby="validationStorageName" required>
|
||||
<div id="validationStorageName" class="invalid-feedback">
|
||||
{% for msg in form.storage_name.errors %}
|
||||
{{msg}}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<label for="{{form.responsible.id_for_label}}">Responsible</label>
|
||||
<div class="input-group has-validation">
|
||||
<span class="input-group-text" id="add_storage_username_prepend">@</span><input type="text" value="{{form.responsible.value}}" class="form-control{% if form.responsible.errors or form.non_field_errors %} is-invalid{% endif %}" id="{{form.responsible.id_for_label}}" name="{{form.responsible.name}}" aria-describedby="add_storage_username_prepend validationServerUsernameFeedback" required>
|
||||
<div id="validationServerUsernameFeedback" class="invalid-feedback">
|
||||
{% for msg in form.responsible.errors %}
|
||||
{{msg}}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<input type="submit" class="btn btn-primary" value="Add Storage" name="submit-add-storage">
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<p class="text-danger text-center">{{ error }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -21,6 +21,7 @@
|
||||
<h1>Sub-Storages <a class="btn btn-secondary" href="{% url 'parts-stocks-detail' uuid=object.parent_storage.id %}">Parent Storage</a> {% else %}
|
||||
<h1>Sub-Storages <a class="btn btn-secondary" href="{% url 'parts-stocks'%}">Stock Overview</a>
|
||||
{% endif %}
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#delete-storage-modal">D</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#add-sub-modal"><i class="bi bi-plus-circle"></i></button>
|
||||
</h1>
|
||||
<div class="list-group">
|
||||
@ -41,49 +42,13 @@
|
||||
</div>
|
||||
<!-- Modal for adding a substorage-->
|
||||
{% with add_storage_form as form %}
|
||||
<div class="modal fade" id="add-sub-modal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="staticBackdropLabel">Add Storage</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="modal-body">
|
||||
<label for="add-storage-name" class="form-label">Storage Name</label>
|
||||
<div class="input-group has-validation">
|
||||
<input value="{{form.storage_name.value}}" class="form-control{% if form.storage_name.errors or form.non_field_errors %} is-invalid{% endif %}" id="add-storage-name" name="{{form.storage_name.name}}" type="text" aria-describedby="validationStorageName" required>
|
||||
<div id="validationStorageName" class="invalid-feedback">
|
||||
{% for msg in form.storage_name.errors %}
|
||||
{{msg}}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<label for="{{form.responsible.id_for_label}}">Responsible</label>
|
||||
<div class="input-group has-validation">
|
||||
<span class="input-group-text" id="add_storage_username_prepend">@</span><input type="text" value="{{form.responsible.value}}" class="form-control{% if form.responsible.errors or form.non_field_errors %} is-invalid{% endif %}" id="{{form.responsible.id_for_label}}" name="{{form.responsible.name}}" aria-describedby="add_storage_username_prepend validationServerUsernameFeedback" required>
|
||||
<div id="validationServerUsernameFeedback" class="invalid-feedback">
|
||||
{% for msg in form.responsible.errors %}
|
||||
{{msg}}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<input type="submit" class="btn btn-primary" value="Add Storage" name="submit-add-storage">
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<p class="text-danger text-center">{{ error }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% include 'parts/modals/new-substorage-modal.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
<!-- Modal for deleting this storage -->
|
||||
{% with delete_storage_errors as err_msgs %}
|
||||
{% include 'parts/modals/delete-storage-modal.html' %}
|
||||
{% endwith %}
|
||||
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
@ -91,9 +56,16 @@
|
||||
{% if add_storage_form.errors %}
|
||||
<script type="text/javascript">
|
||||
var addSubStorageModal = document.querySelector('#add-sub-modal');
|
||||
var modal = bootstrap.Modal.getOrCreateInstance(addSubStorageModal);
|
||||
modal.show();
|
||||
var c_modal = bootstrap.Modal.getOrCreateInstance(addSubStorageModal);
|
||||
c_modal.show();
|
||||
</script>
|
||||
{% endif %}
|
||||
{% if delete_storage_errors %}
|
||||
<script type="text/javascript">
|
||||
var deleteStorageModal = document.querySelector('#delete-storage-modal');
|
||||
var d_modal = bootstrap.Modal.getOrCreateInstance(deleteStorageModal);
|
||||
d_modal.show();
|
||||
</script
|
||||
{% endif %}
|
||||
{% endblock custom_scripts %}
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
{% include 'paginator.html' with paginator=low_stocks get_param='low_stock_page' aria_label='Low Stock Page Navigation' %}
|
||||
</div>
|
||||
<div class="col-md">
|
||||
<h1>Storages</h1>
|
||||
<h1>Storages <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#add-sub-modal"><i class="bi bi-plus-circle"></i></button></h1>
|
||||
<div class="list-group">
|
||||
{% for storage in storages %}
|
||||
<a href="{% url 'parts-stocks-detail' uuid=storage.id %}" class="text-decoration-none">
|
||||
@ -51,5 +51,22 @@
|
||||
{% include 'paginator.html' with paginator=storages get_param='storage_page' aria_label='Storage Page Navigation'%}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add storage modal form -->
|
||||
{% with add_storage_form as form %}
|
||||
{% include 'parts/modals/new-substorage-modal.html' %}
|
||||
{% endwith %}
|
||||
|
||||
</div>
|
||||
{% endblock content %}
|
||||
{% endblock content %}
|
||||
|
||||
{% block custom_scripts %}
|
||||
{% if add_storage_form.errors %}
|
||||
<script type="text/javascript">
|
||||
var addSubStorageModal = document.querySelector('#add-sub-modal');
|
||||
var modal = bootstrap.Modal.getOrCreateInstance(addSubStorageModal);
|
||||
modal.show();
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock custom_scripts %}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user