Compare commits
No commits in common. "9bf0516d89bb7288ffcb45191941be3ca4dc8241" and "4b4a85d1aa11e9b94d9cf7e4e2ae1a8018e97e24" have entirely different histories.
9bf0516d89
...
4b4a85d1aa
|
@ -1,17 +0,0 @@
|
||||||
from django import forms
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
|
|
||||||
class AddStockForm(forms.Form):
|
|
||||||
part_number = forms.CharField(label=_("Component"))
|
|
||||||
properties = forms.JSONField(initial={}, label=_("Properties"), required=False)
|
|
||||||
quantity = forms.IntegerField(min_value=1, label=_("Quantity"))
|
|
||||||
original_quantity = forms.IntegerField(min_value=1, label=_("Original quantity"))
|
|
||||||
total_value = forms.DecimalField(
|
|
||||||
min_value=0, max_digits=8, decimal_places=2, label=_("Total value")
|
|
||||||
)
|
|
||||||
location = forms.CharField(label=_("Location"), widget=forms.Textarea)
|
|
||||||
owner = forms.CharField(label=_("Owner"))
|
|
||||||
|
|
||||||
|
|
||||||
AddStockFormSet = forms.formset_factory(AddStockForm, extra=0)
|
|
|
@ -1,21 +1,3 @@
|
||||||
from django.dispatch import Signal, receiver
|
from django.dispatch import Signal
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.translation import gettext as _
|
|
||||||
|
|
||||||
populate_nav = Signal()
|
populate_nav = Signal()
|
||||||
|
|
||||||
|
|
||||||
@receiver(populate_nav, dispatch_uid="populate_nav_core")
|
|
||||||
def populate_nav_core(sender, **kwargs):
|
|
||||||
request = sender
|
|
||||||
|
|
||||||
if not request.user.is_authenticated:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
"is_active": request.resolver_match.url_name == "add_stock",
|
|
||||||
"link": reverse("add_stock"),
|
|
||||||
"text": _("Add stock"),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
let totalForms = Object.defineProperty({}, 'value', {
|
|
||||||
get() {
|
|
||||||
return +document.getElementById('id_form-TOTAL_FORMS').value;
|
|
||||||
},
|
|
||||||
|
|
||||||
set(value) {
|
|
||||||
document.getElementById('id_form-TOTAL_FORMS').value = value;
|
|
||||||
},
|
|
||||||
|
|
||||||
enumerable: false,
|
|
||||||
configurable: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateNames = (elem, i) => {
|
|
||||||
const replaceIndex = s => s.replaceAll('__prefix__', `${i}`);
|
|
||||||
|
|
||||||
if (elem.hasAttribute('for')) {
|
|
||||||
elem.htmlFor = replaceIndex(elem.htmlFor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elem.hasAttribute('id')) {
|
|
||||||
elem.id = replaceIndex(elem.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elem.hasAttribute('name')) {
|
|
||||||
elem.name = replaceIndex(elem.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const child of elem.children) {
|
|
||||||
updateNames(child, i);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const autofillQuantity = i => {
|
|
||||||
const originalQuantity = document.getElementById(`id_form-${i}-original_quantity`);
|
|
||||||
const quantity = document.getElementById(`id_form-${i}-quantity`);
|
|
||||||
|
|
||||||
const autofillOnInput = (self, other) => () => {
|
|
||||||
delete self.dataset.autofilled;
|
|
||||||
|
|
||||||
if (!other.value || other.dataset.autofilled) {
|
|
||||||
other.value = self.value;
|
|
||||||
other.dataset.autofilled = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
originalQuantity.addEventListener('input', autofillOnInput(originalQuantity, quantity));
|
|
||||||
quantity.addEventListener('input', autofillOnInput(quantity, originalQuantity));
|
|
||||||
};
|
|
||||||
|
|
||||||
const addFormButton = document.getElementById('addForm');
|
|
||||||
|
|
||||||
const addForm = () => {
|
|
||||||
const form = document.getElementById('formTemplate').cloneNode(true);
|
|
||||||
const i = totalForms.value++;
|
|
||||||
updateNames(form, i);
|
|
||||||
|
|
||||||
const heading = document.createElement('h3');
|
|
||||||
heading.innerText = `#${i+1}`;
|
|
||||||
|
|
||||||
addFormButton.before(heading, ...form.children);
|
|
||||||
|
|
||||||
autofillQuantity(i);
|
|
||||||
document.getElementById(`id_form-${i}-owner`).value = JSON.parse(document.getElementById('currentUser').textContent);
|
|
||||||
};
|
|
||||||
|
|
||||||
addFormButton.addEventListener('click', addForm);
|
|
||||||
|
|
||||||
for (let i = 0; i < totalForms.value; ++i) {
|
|
||||||
autofillQuantity(i);
|
|
||||||
}
|
|
|
@ -13,14 +13,14 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% block navbar %}{% include "partials/navbar.html" %}{% endblock %}
|
{% block navbar %}
|
||||||
<div class="container-fluid">
|
{% include "partials/navbar.html" %}
|
||||||
|
{% endblock %}
|
||||||
{% block messages %}
|
{% block messages %}
|
||||||
{% bootstrap_messages %}
|
{% bootstrap_messages %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<script src="{% static 'bootstrap.bundle.min.js' %}"></script>
|
<script src="{% static 'bootstrap.bundle.min.js' %}"></script>
|
||||||
<script>
|
<script>
|
||||||
|
@ -28,6 +28,5 @@
|
||||||
e.classList.remove('show');
|
e.classList.remove('show');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% block extra_js %}{% endblock %}
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% load django_bootstrap5 %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
{% block page_title %}{% translate "Add stock" %}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>{% translate "Add stock" %}</h1>
|
|
||||||
<form class="mb-3" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ formset.management_form }}
|
|
||||||
{% bootstrap_formset_errors formset layout="floating" %}
|
|
||||||
{% for form in formset %}
|
|
||||||
<h3>#{{ forloop.counter }}</h3>
|
|
||||||
{% bootstrap_form form layout="floating" %}
|
|
||||||
{% endfor %}
|
|
||||||
{% bootstrap_button "+" button_class="btn-outline-secondary" button_type="button" id="addForm" %}
|
|
||||||
{% translate "Add" as add_button_text %}
|
|
||||||
{% bootstrap_button button_type="submit" content=add_button_text %}
|
|
||||||
</form>
|
|
||||||
<div class="d-none" id="formTemplate">
|
|
||||||
{% bootstrap_form formset.empty_form layout="floating" %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block extra_js %}
|
|
||||||
{{ properties|json_script:"properties" }}
|
|
||||||
{{ request.user.username|json_script:"currentUser" }}
|
|
||||||
<script src="{% static 'core/add_stock.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
|
@ -3,8 +3,3 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block page_title %}{% translate "Unauthorized" %}{% endblock %}
|
{% block page_title %}{% translate "Unauthorized" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>{% translate "Unauthorized" %}</h1>
|
|
||||||
<p>{% translate "Please log in to use this application." %}</p>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", views.index, name="index"),
|
path("", views.index, name="index"),
|
||||||
path("add-stock", views.add_stock, name="add_stock"),
|
|
||||||
path("unauthorized", views.unauthorized, name="unauthorized"),
|
path("unauthorized", views.unauthorized, name="unauthorized"),
|
||||||
path("admin/login/", views.admin_login),
|
path("admin/login/", views.admin_login),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,14 +1,7 @@
|
||||||
from django.contrib import messages
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.db import transaction
|
|
||||||
from django.forms.models import model_to_dict
|
|
||||||
from django.http import QueryDict
|
from django.http import QueryDict
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ngettext
|
|
||||||
|
|
||||||
from .forms import AddStockFormSet
|
|
||||||
from .models import Component, Property, Stock, User, Value
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -20,101 +13,6 @@ def unauthorized(request):
|
||||||
return render(request, "core/unauthorized.html", {})
|
return render(request, "core/unauthorized.html", {})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def add_stock(request):
|
|
||||||
property_models = {p.name: p for p in Property.objects.all()}
|
|
||||||
|
|
||||||
if request.method == "POST":
|
|
||||||
formset = AddStockFormSet(request.POST, initial=[{"owner": request.user}])
|
|
||||||
if not formset.is_valid():
|
|
||||||
return render(request, "core/add_stock.html", {"formset": formset})
|
|
||||||
|
|
||||||
try:
|
|
||||||
with transaction.atomic():
|
|
||||||
for form in formset:
|
|
||||||
part_number = form.cleaned_data["part_number"]
|
|
||||||
component, created = Component.objects.get_or_create(
|
|
||||||
part_number__iexact=part_number,
|
|
||||||
defaults={"part_number": part_number},
|
|
||||||
)
|
|
||||||
|
|
||||||
if created:
|
|
||||||
properties = form.cleaned_data["properties"]
|
|
||||||
for name in properties:
|
|
||||||
if name not in property_models:
|
|
||||||
property_kwargs = {
|
|
||||||
k: v
|
|
||||||
for k, v in properties[name].items()
|
|
||||||
if k
|
|
||||||
in (
|
|
||||||
"filterable",
|
|
||||||
"type",
|
|
||||||
"searchable",
|
|
||||||
"unit",
|
|
||||||
"scale",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
property_models[name] = Property.objects.create(
|
|
||||||
name=name, **property_kwargs
|
|
||||||
)
|
|
||||||
property = property_models[name]
|
|
||||||
|
|
||||||
value_kwargs = {
|
|
||||||
k: v
|
|
||||||
for k, v in properties[name].items()
|
|
||||||
if k in ("integer", "text")
|
|
||||||
}
|
|
||||||
value_kwargs.setdefault("integer", 0)
|
|
||||||
value_kwargs.setdefault("text", "")
|
|
||||||
Value.objects.create(
|
|
||||||
property=property, component=component, **value_kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
owner_name = form.cleaned_data["owner"]
|
|
||||||
owner = User.objects.filter(username=owner_name).first()
|
|
||||||
|
|
||||||
Stock.objects.create(
|
|
||||||
component=component,
|
|
||||||
quantity=form.cleaned_data["quantity"],
|
|
||||||
original_quantity=form.cleaned_data["original_quantity"],
|
|
||||||
total_value=form.cleaned_data["total_value"],
|
|
||||||
location=form.cleaned_data["location"],
|
|
||||||
owner=owner,
|
|
||||||
owner_name=owner_name,
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
messages.add_message(request, messages.ERROR, str(e))
|
|
||||||
|
|
||||||
return render(
|
|
||||||
request,
|
|
||||||
"core/add_stock.html",
|
|
||||||
{
|
|
||||||
"formset": formset,
|
|
||||||
"properties": list(map(model_to_dict, property_models.values())),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
messages.add_message(
|
|
||||||
request,
|
|
||||||
messages.SUCCESS,
|
|
||||||
ngettext(
|
|
||||||
formset.total_form_count,
|
|
||||||
"Added %(count)d stock entry",
|
|
||||||
"Added %(count)d stock entries",
|
|
||||||
)
|
|
||||||
% {"count": formset.total_form_count},
|
|
||||||
)
|
|
||||||
|
|
||||||
return render(
|
|
||||||
request,
|
|
||||||
"core/add_stock.html",
|
|
||||||
{
|
|
||||||
"formset": AddStockFormSet(initial=[{"owner": request.user}]),
|
|
||||||
"properties": list(map(model_to_dict, property_models.values())),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def admin_login(request):
|
def admin_login(request):
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
return redirect("unauthorized")
|
return redirect("unauthorized")
|
||||||
|
|
Loading…
Reference in New Issue