Implement Add Stock modal
This commit is contained in:
parent
b4e561279b
commit
fbb60b455f
@ -47,7 +47,7 @@ class PartsComponentViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = ComponentSerializer
|
||||
permission_classes = [permissions.DjangoModelPermissions]
|
||||
filter_backends = [filters.SearchFilter]
|
||||
search_fields = ['name', 'package__name', 'manufacturer__name']
|
||||
search_fields = ['id', 'name', 'package__name', 'manufacturer__name']
|
||||
|
||||
class PartsManufacturerViewSet(viewsets.ModelViewSet):
|
||||
queryset = parts_models.Manufacturer.objects.all()
|
||||
|
@ -72,3 +72,36 @@ class EditStockAmountForm(forms.Form):
|
||||
amount = -amount
|
||||
|
||||
return stock.atomic_increment(amount)
|
||||
|
||||
class AddStockForm(forms.Form):
|
||||
watermark_active = forms.BooleanField(required=False)
|
||||
watermark = forms.IntegerField(min_value=0, required=True, initial=0)
|
||||
amount = forms.IntegerField(min_value=0, required=True, initial=1)
|
||||
component_uuid = forms.UUIDField(required=True)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
id = cleaned_data.get('component_uuid')
|
||||
if not id:
|
||||
raise ValidationError('No valid component selected!')
|
||||
component = None
|
||||
try:
|
||||
component = parts_models.Component.objects.get(id=id)
|
||||
except:
|
||||
raise ValidationError("Invalid component selected!")
|
||||
cleaned_data['component'] = component
|
||||
|
||||
return cleaned_data
|
||||
|
||||
def save(self, storage):
|
||||
component = self.cleaned_data.get('component')
|
||||
amount = self.cleaned_data.get('amount')
|
||||
watermark = -1
|
||||
|
||||
if self.cleaned_data.get('watermark-active'):
|
||||
watermark = self.cleaned_data.get('watermark')
|
||||
|
||||
new_stock = parts_models.Stock.objects.create(storage=storage, component=component, watermark=watermark, amount=amount)
|
||||
new_stock.save()
|
||||
|
@ -13,7 +13,7 @@ from .models import Storage, Stock
|
||||
from .qr_parser import QrCodeValidator
|
||||
from django.core.paginator import Paginator
|
||||
from django.core.exceptions import ValidationError
|
||||
from .forms import MyTestForm, AddSubStorageForm, DeleteStockForm, EditWatermarkForm, EditStockAmountForm
|
||||
from .forms import MyTestForm, AddSubStorageForm, DeleteStockForm, EditWatermarkForm, EditStockAmountForm, AddStockForm
|
||||
from django.db.models import Q
|
||||
from django.db.models.functions import Lower
|
||||
import uuid
|
||||
@ -223,6 +223,7 @@ class StockViewDetail(LoginRequiredMixin, BaseTemplateMixin, DetailView):
|
||||
add_storage_form.fields['responsible'].initial = self.request.user.username
|
||||
context['add_storage_form'] = add_storage_form
|
||||
context['delete_storage_error'] = None
|
||||
context['add_stock_form'] = AddStockForm()
|
||||
|
||||
return context
|
||||
|
||||
@ -293,6 +294,24 @@ class StockViewDetail(LoginRequiredMixin, BaseTemplateMixin, DetailView):
|
||||
context = self.get_context_data(**kwargs)
|
||||
return self.render_to_response(context)
|
||||
|
||||
def handle_add_stock_post(self, request, **kwargs):
|
||||
f = AddStockForm(data=request.POST)
|
||||
error_occured = False
|
||||
|
||||
if f.is_valid():
|
||||
try:
|
||||
f.save(self.object)
|
||||
except Exception as ex:
|
||||
f.add_error('', str(ex))
|
||||
error_occured = True
|
||||
else:
|
||||
error_occured = True
|
||||
|
||||
context = self.get_context_data(**kwargs)
|
||||
if error_occured:
|
||||
context['add_stock_form'] = f
|
||||
return self.render_to_response(context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
|
||||
@ -308,4 +327,7 @@ class StockViewDetail(LoginRequiredMixin, BaseTemplateMixin, DetailView):
|
||||
return self.handle_amount_change_post(request, False, **kwargs)
|
||||
elif 'submit-amount-increase' in request.POST:
|
||||
return self.handle_amount_change_post(request, True, **kwargs)
|
||||
elif 'submit-add-stock' in request.POST:
|
||||
return self.handle_add_stock_post(request, **kwargs)
|
||||
|
||||
return super().post(request, *args, **kwargs)
|
@ -1,3 +1,31 @@
|
||||
function fill_add_stock_modal_component(data) {
|
||||
// component has been selected. Process it:
|
||||
document.getElementById('add-stock-search').value = '';
|
||||
console.log(data);
|
||||
var template = document.querySelector('#add-stock-modal-component-template');
|
||||
var container = document.querySelector('#add-stock-modal-component-container');
|
||||
container.innerHTML = '';
|
||||
var comp_elem = template.content.cloneNode(true);
|
||||
|
||||
if (data.ro_image) {
|
||||
comp_elem.querySelector('#add-stock-cmp-img').setAttribute('src', data.ro_image);
|
||||
}
|
||||
var description_container = comp_elem.querySelector('#add-stock-cmp-desc-container');
|
||||
var heading = document.createElement('h6');
|
||||
heading.appendChild(document.createTextNode(data.name));
|
||||
description_container.appendChild(heading);
|
||||
if (data.package_data && data.package_data.name) {
|
||||
description_container.appendChild(document.createTextNode('in '+data.package_data.name));
|
||||
}
|
||||
if (data.ro_manufacturer_name) {
|
||||
description_container.appendChild(document.createElement('br'));
|
||||
description_container.appendChild(document.createTextNode('by '+data.ro_manufacturer_name));
|
||||
}
|
||||
document.querySelector('#add-stock-modal-comp-uuid').value = data.id;
|
||||
console.log("Selected element: "+data.id);
|
||||
container.appendChild(comp_elem);
|
||||
}
|
||||
|
||||
new AutocompleteCustomUi('add-stock-search', 'add-stock-search-ac-dropdown',
|
||||
function(search, autocomplete_obj) {
|
||||
api_search_component(search, function(results) {
|
||||
@ -36,9 +64,9 @@ function(search, autocomplete_obj) {
|
||||
node.appendChild(img_container);
|
||||
node.appendChild(text_container);
|
||||
|
||||
test.push({'ui': node, 'data': c.url})
|
||||
test.push({'ui': node, 'data': c})
|
||||
}
|
||||
autocomplete_obj.show_results(test,
|
||||
function(data) {console.log(data);});
|
||||
function(data){fill_add_stock_modal_component(data);});
|
||||
}, function(){});
|
||||
});
|
@ -11,6 +11,8 @@ class AutocompleteCustomUi {
|
||||
this.query_callback(document.getElementById(this.text_id).value, this);
|
||||
}, autocomplete_query_delay_ms).bind(this));
|
||||
|
||||
this.dropdown_data = {};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -24,15 +26,17 @@ class AutocompleteCustomUi {
|
||||
var ul = document.getElementById(this.dropdown_id);
|
||||
ul.innerHTML = '';
|
||||
this.select_data = {};
|
||||
this.dropdown_data = {};
|
||||
|
||||
for (var i = 0; i < nodes_to_add.length; i++) {
|
||||
var ui = nodes_to_add[i]['ui'];
|
||||
var data = nodes_to_add[i]['data']
|
||||
var dropdown_node = document.createElement('li');
|
||||
dropdown_node.dataset.ac_clicked = data;
|
||||
dropdown_node.dataset.ac_clicked = i;
|
||||
this.dropdown_data[i] = data;
|
||||
dropdown_node.addEventListener('click',
|
||||
(e) => {
|
||||
data_clicked_callback(e.currentTarget.dataset.ac_clicked);
|
||||
data_clicked_callback(this.dropdown_data[e.currentTarget.dataset.ac_clicked]);
|
||||
var text_box = document.getElementById(this.text_id);
|
||||
var dropdown = bootstrap.Dropdown.getOrCreateInstance(text_box);
|
||||
dropdown.hide();
|
||||
|
@ -32,3 +32,7 @@ function api_search_user(search, onSuccess, onFail) {
|
||||
function api_search_component(search, onSuccess, onFail) {
|
||||
return api_ajax_request_without_send('GET', api_urls_v1['component-list']+`?search=${encodeURIComponent(search)}`, function(method, url, json) {onSuccess(json);}, onFail);
|
||||
}
|
||||
|
||||
function api_get_component_from_id(id, onSuccess, onFail) {
|
||||
return api_ajax_request_without_send('GET', api_urls_v1['component-list']+`?search=${encodeURIComponent(id)}`, function(method, url, json) {onSuccess(json.results[0]);}, onFail);
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
Input context:
|
||||
- form: A stock-create-form
|
||||
{% endcomment %}
|
||||
{% load static %}
|
||||
<div class="modal fade" id="add-stock-modal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
@ -14,12 +15,59 @@ Input context:
|
||||
<input class="form-control" autocomplete="off" data-bs-toggle="dropdown" type="search" id="add-stock-search" placeholder="Search Component" aria-label="Search for Component">
|
||||
<ul class="dropdown-menu" aria-labelledby="add-stock-search" id="add-stock-search-ac-dropdown">
|
||||
</div>
|
||||
<hr>
|
||||
</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 Stock" name="submit-add-stock">
|
||||
</div>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="modal-body">
|
||||
<div id="add-stock-modal-component-container" class="mb-3">
|
||||
<h4>No component selected.<h4>
|
||||
<!-- Will be filled dynamically by selecting a component from the dropdown list -->
|
||||
</div>
|
||||
<input type="hidden" name="{{form.component_uuid.name}}" id="add-stock-modal-comp-uuid" value="{{form.component_uuid.value}}" required>
|
||||
<div class="mb-3">
|
||||
<label for="add-stock-form-amount" class="form-label">Amount</label>
|
||||
<input id="add-stock-form-amount" type="number" class="form-control{% if form.amount.errors %} is-invalid{% endif %}" min="0" name="{{form.amount.name}}" value="{{form.amount.value}}" required aria-describedby="add-stock-form-amount-err">
|
||||
<div id="add-stock-form-amount-err" class="invalid-feedback">
|
||||
{% for msg in form.amount.errors %}
|
||||
{{msg}}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="add-stock-form-watermark" class="form-label">Watermark</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">
|
||||
<input class="form-check-input mt-0" type="checkbox" {% if form.watermark_active.value %}checked{% endif %} name="{{form.watermark_active.name}}">
|
||||
</div>
|
||||
<input id="add-stock-form-watermark" type="number" class="form-control{% if form.watermark.errors %} is-invalid{% endif %}" name="{{form.watermark.name}}" value="{{form.watermark.value}}" min="0" required aria-describedby="add-stock-form-watermark-err">
|
||||
<div id="add-stock-form-watermark-err" class="invalid-feedback">
|
||||
{% for msg in form.watermark.errors %}
|
||||
{{msg}}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</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 Stock" name="submit-add-stock">
|
||||
{% 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>
|
||||
<template id="add-stock-modal-component-template">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<img id="add-stock-cmp-img" src="{% static 'css/icons/card-image.svg' %}" style="width:64px;max-height:64px;">
|
||||
</div>
|
||||
<div class="flex-grow-1 ms-1" id="add-stock-cmp-desc-container">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -121,6 +121,7 @@
|
||||
{% endblock content %}
|
||||
|
||||
{% block custom_scripts %}
|
||||
<script type="text/javascript" src="{% static 'js/add-stock-modal.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
{% if add_storage_form.errors %}
|
||||
|
||||
@ -133,6 +134,26 @@
|
||||
var d_modal = bootstrap.Modal.getOrCreateInstance(deleteStorageModal);
|
||||
d_modal.show();
|
||||
{% endif %}
|
||||
{% if add_stock_form.errors %}
|
||||
var uuid = "{{add_stock_form.component_uuid.value}}";
|
||||
if (uuid && uuid != '' && uuid != 'None') {
|
||||
api_get_component_from_id(uuid, function(component){
|
||||
var add_stock_modal_div = document.querySelector('#add-stock-modal');
|
||||
var add_stock_modal = bootstrap.Modal.getOrCreateInstance(add_stock_modal_div);
|
||||
fill_add_stock_modal_component(component);
|
||||
add_stock_modal.show();
|
||||
console.log(component)
|
||||
}, function(){
|
||||
var add_stock_modal_div = document.querySelector('#add-stock-modal');
|
||||
var add_stock_modal = bootstrap.Modal.getOrCreateInstance(add_stock_modal_div);
|
||||
add_stock_modal.show();
|
||||
});
|
||||
} else {
|
||||
var add_stock_modal_div = document.querySelector('#add-stock-modal');
|
||||
var add_stock_modal = bootstrap.Modal.getOrCreateInstance(add_stock_modal_div);
|
||||
add_stock_modal.show();
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
new AutocompleteText('{{add_storage_form.responsible.id_for_label}}', '{{add_storage_form.responsible.id_for_label}}-ac-dropdown',
|
||||
function(search, autocomplete_obj) {
|
||||
@ -150,7 +171,6 @@ function(search, autocomplete_obj) {
|
||||
<script type="text/javascript">
|
||||
const fallback_img_path = "{% static 'css/icons/card-image.svg' %}";
|
||||
</script>
|
||||
<script type="text/javascript" src="{% static 'js/add-stock-modal.js' %}"></script>
|
||||
|
||||
{% endblock custom_scripts %}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user