Compare commits

...

8 commits

12 changed files with 277 additions and 21 deletions

View file

@ -108,7 +108,7 @@ class InventaryMixin(DashboardView, TemplateView):
page = 1 page = 1
if limit < 1: if limit < 1:
limit = 10 limit = 10
except: except Exception:
limit = 10 limit = 10
page = 1 page = 1

View file

@ -18,6 +18,14 @@
<i class="bi bi-tag"></i> <i class="bi bi-tag"></i>
{% trans 'properties' %} {% trans 'properties' %}
</a> </a>
<a href="{% url 'lot:subscription' object.id %}" type="button" class="btn btn-green-admin">
<i class="bi bi-tag"></i>
{% trans 'Subscription' %}
</a>
<a href="{% url 'lot:unsubscription' object.id %}" type="button" class="btn btn-green-admin">
<i class="bi bi-tag"></i>
{% trans 'Unsubscription' %}
</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View file

@ -197,14 +197,14 @@ class DeleteEvidenceTagView(DashboardView, DeleteView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
message = _("<Deleted> Evidence Tag: {}").format(self.object.value) message = _("<Deleted> Evidence Tag: {}").format(self.object.value)
DeviceLog.objects.create( DeviceLog.objects.create(
snapshot_uuid=self.object.uuid, snapshot_uuid=self.object.uuid,
event=message, event=message,
user=self.request.user, user=self.request.user,
institution=self.request.user.institution institution=self.request.user.institution
) )
self.object.delete() self.object.delete()
messages.info(self.request, _("Evicende Tag deleted successfully.")) messages.info(self.request, _("Evicende Tag deleted successfully."))
return self.handle_success() return self.handle_success()
@ -214,6 +214,6 @@ class DeleteEvidenceTagView(DashboardView, DeleteView):
def get_success_url(self): def get_success_url(self):
return self.request.META.get( return self.request.META.get(
'HTTP_REFERER', 'HTTP_REFERER',
reverse_lazy('evidence:details', args=[self.object.uuid]) reverse_lazy('evidence:details', args=[self.object.uuid])
) )

View file

@ -1,4 +1,7 @@
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
from django import forms from django import forms
from user.models import User
from lot.models import Lot from lot.models import Lot
@ -11,7 +14,7 @@ class LotsForm(forms.Form):
def clean(self): def clean(self):
self._lots = self.cleaned_data.get("lots") self._lots = self.cleaned_data.get("lots")
return self._lots return self._lots
def save(self, commit=True): def save(self, commit=True):
if not commit: if not commit:
return return
@ -26,3 +29,63 @@ class LotsForm(forms.Form):
for lot in self._lots: for lot in self._lots:
lot.remove(dev.id) lot.remove(dev.id)
return return
class LotSubscriptionForm(forms.Form):
user = forms.CharField()
type = forms.ChoiceField(
choices=[
("circuit_manager", _("Circuit Manager")),
("shop", _("Shop")),
],
widget=forms.Select(attrs={'class': 'form-control'}),
)
def __init__(self, *args, **kwargs):
self.institution = kwargs.pop("institution")
super().__init__(*args, **kwargs)
def clean(self):
self._user = self.cleaned_data.get("user")
self._type = self.cleaned_data.get("type")
self.user = User.objects.filter(email=self._user).first()
if self.user and self.user.institution != self.institution:
txt = _("This user is from another institution")
raise ValidationError(txt)
return
def save(self, commit=True):
if not commit:
return
if not self.user:
self.user = User.objects.create_user(
self._user,
self.institution,
commit=False
)
# TODO
# self.send_email()
if self._type == "circuit_manager":
self.user.is_circuit_manager = True
if self._type == "shop":
self.user.is_shop = True
self.user.save()
def remove(self):
if not self.user:
return
if self._type == "circuit_manager":
self.user.is_circuit_manager = False
if self._type == "shop":
self.user.is_shop = False
self.user.save()
return

View file

@ -0,0 +1,28 @@
# Generated by Django 5.0.6 on 2025-03-03 10:36
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('lot', '0008_rename_closed_lot_archived'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='LotSubscription',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lot', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='lot.lot')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddConstraint(
model_name='lotsubscription',
constraint=models.UniqueConstraint(fields=('lot', 'user'), name='unique_lot_user'),
),
]

View file

@ -59,4 +59,15 @@ class LotProperty(Property):
constraints = [ constraints = [
models.UniqueConstraint( models.UniqueConstraint(
fields=["key", "lot"], name="property_unique_type_key_lot") fields=["key", "lot"], name="property_unique_type_key_lot")
]
class LotSubscription(models.Model):
lot = models.ForeignKey(Lot, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=False, blank=False)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["lot", "user"], name="unique_lot_user")
] ]

View file

@ -0,0 +1,32 @@
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col">
<h3>{{ subtitle }}</h3>
</div>
</div>
{% load django_bootstrap5 %}
<form role="form" method="post">
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger alert-icon alert-icon-border alert-dismissible" role="alert">
<div class="icon"><span class="mdi mdi-close-circle-o"></span></div>
<div class="message">
{% for field, error in form.errors.items %}
{{ error }}<br />
{% endfor %}
<button class="btn-close" type="button" data-dismiss="alert" aria-label="Close"></button>
</div>
</div>
{% endif %}
{% bootstrap_form form %}
<div class="form-actions-no-box">
<a class="btn btn-grey" href="{% url 'dashboard:lot' lot.id %}">{% translate "Cancel" %}</a>
<input class="btn btn-green-admin" type="submit" name="submit" value="{{ action }}" />
</div>
</form>
{% endblock %}

View file

@ -14,4 +14,6 @@ urlpatterns = [
path("<int:pk>/property/add", views.AddLotPropertyView.as_view(), name="add_property"), path("<int:pk>/property/add", views.AddLotPropertyView.as_view(), name="add_property"),
path("<int:pk>/property/update", views.UpdateLotPropertyView.as_view(), name="update_property"), path("<int:pk>/property/update", views.UpdateLotPropertyView.as_view(), name="update_property"),
path("<int:pk>/property/delete", views.DeleteLotPropertyView.as_view(), name="delete_property"), path("<int:pk>/property/delete", views.DeleteLotPropertyView.as_view(), name="delete_property"),
path("<int:pk>/subscription/", views.SubscriptLotView.as_view(), name="subscription"),
path("<int:pk>/unsubscription/", views.UnsubscriptLotView.as_view(), name="unsubscription"),
] ]

View file

@ -11,8 +11,8 @@ from django.views.generic.edit import (
FormView, FormView,
) )
from dashboard.mixins import DashboardView from dashboard.mixins import DashboardView
from lot.models import Lot, LotTag, LotProperty from lot.models import Lot, LotTag, LotProperty, LotSubscription
from lot.forms import LotsForm from lot.forms import LotsForm, LotSubscriptionForm
class NewLotView(DashboardView, CreateView): class NewLotView(DashboardView, CreateView):
template_name = "new_lot.html" template_name = "new_lot.html"
@ -83,7 +83,7 @@ class EditLotView(DashboardView, UpdateView):
owner=self.request.user.institution, owner=self.request.user.institution,
pk=pk, pk=pk,
) )
# self.success_url = reverse_lazy('dashbiard:lot', args=[pk]) # self.success_url = reverse_lazy('dashboard:lot', args=[pk])
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
return kwargs return kwargs
@ -274,3 +274,54 @@ class DeleteLotPropertyView(DashboardView, DeleteView):
# Redirect back to the original URL # Redirect back to the original URL
return redirect(self.success_url) return redirect(self.success_url)
class SubscriptLotMixing(DashboardView, FormView):
template_name = "subscription.html"
title = _("Subscription")
breadcrumb = "Lot / Subscription"
form_class = LotSubscriptionForm
def get_context_data(self, **kwargs):
self.pk = self.kwargs.get('pk')
context = super().get_context_data(**kwargs)
self.lot = get_object_or_404(
Lot, owner=self.request.user.institution,
id=self.pk
)
context.update({
'lot': self.lot,
"action": _("Subscribe")
})
return context
def get_form_kwargs(self):
self.pk = self.kwargs.get('pk')
self.success_url = reverse_lazy('dashboard:lot', args=[self.pk])
kwargs = super().get_form_kwargs()
kwargs["institution"] = self.request.user.institution
return kwargs
class SubscriptLotView(SubscriptLotMixing):
def form_valid(self, form):
form.save()
response = super().form_valid(form)
return response
class UnsubscriptLotView(SubscriptLotMixing):
title = _("Unsubscription")
breadcrumb = "Lot / Unsubscription"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["action"] = _("Unsubscribe")
return context
def form_valid(self, form):
form.remove()
response = super().form_valid(form)
return response

View file

@ -28,14 +28,12 @@ class Command(BaseCommand):
self.create_user(institution, email, password, is_admin, predefined_token) self.create_user(institution, email, password, is_admin, predefined_token)
def create_user(self, institution, email, password, is_admin, predefined_token): def create_user(self, institution, email, password, is_admin, predefined_token):
self.u = User.objects.create( self.u = User.objects.create_superuser(
institution=institution, institution=institution,
email=email, email=email,
password=password, password=password,
is_admin=is_admin,
) )
self.u.set_password(password)
self.u.save()
if predefined_token: if predefined_token:
token = predefined_token token = predefined_token
else: else:

View file

@ -0,0 +1,29 @@
# Generated by Django 5.0.6 on 2025-03-03 10:36
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('user', '0002_institution_algorithm'),
]
operations = [
migrations.AddField(
model_name='user',
name='is_circuit_manager',
field=models.BooleanField(default=False, verbose_name='is circuit manager'),
),
migrations.AddField(
model_name='user',
name='is_shop',
field=models.BooleanField(default=False, verbose_name='is shop'),
),
migrations.AlterField(
model_name='user',
name='institution',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='users', to='user.institution'),
),
]

View file

@ -33,7 +33,7 @@ class Institution(models.Model):
class UserManager(BaseUserManager): class UserManager(BaseUserManager):
def create_user(self, email, institution, password=None): def create_user(self, email, institution, password=None, commit=True):
""" """
Creates and saves a User with the given email, date of Creates and saves a User with the given email, date of
birth and password. birth and password.
@ -47,23 +47,55 @@ class UserManager(BaseUserManager):
) )
user.set_password(password) user.set_password(password)
user.save(using=self._db) if commit:
user.save(using=self._db)
return user return user
def create_superuser(self, email, institution, password=None): def create_superuser(self, email, institution, password=None):
""" """
Creates and saves a superuser with the given email, date of Creates and saves a superuser with the given email and password.
birth and password.
""" """
user = self.create_user( user = self.create_user(
email, email,
institution=institution, institution,
password=password, password=password,
commit=False
) )
user.is_admin = True user.is_admin = True
user.save(using=self._db) user.save(using=self._db)
return user return user
def create_circuit_manager(self, email, institution, password=None):
"""
Creates and saves a circuit manager with the given email and password.
"""
user = self.create_user(
email,
institution,
password=password,
commit=False
)
user.is_circuit_manager = True
user.save(using=self._db)
return user
def create_shop(self, email, institution, password=None):
"""
Creates and saves a shop with the given email and password.
"""
user = self.create_user(
email,
institution,
password=password,
commit=False
)
user.is_shop = True
user.save(using=self._db)
return user
class User(AbstractBaseUser): class User(AbstractBaseUser):
email = models.EmailField( email = models.EmailField(
@ -71,13 +103,15 @@ class User(AbstractBaseUser):
max_length=255, max_length=255,
unique=True, unique=True,
) )
is_active = models.BooleanField(_("is active"), default=True)
is_admin = models.BooleanField(_("is admin"), default=False)
first_name = models.CharField(_("First name"), max_length=255, blank=True, null=True) first_name = models.CharField(_("First name"), max_length=255, blank=True, null=True)
last_name = models.CharField(_("Last name"), max_length=255, blank=True, null=True) last_name = models.CharField(_("Last name"), max_length=255, blank=True, null=True)
accept_gdpr = models.BooleanField(default=False) accept_gdpr = models.BooleanField(default=False)
institution = models.ForeignKey(Institution, on_delete=models.CASCADE) is_active = models.BooleanField(_("is active"), default=True)
is_admin = models.BooleanField(_("is admin"), default=False)
is_circuit_manager = models.BooleanField(_("is circuit manager"), default=False)
is_shop = models.BooleanField(_("is shop"), default=False)
institution = models.ForeignKey(Institution, on_delete=models.CASCADE, related_name="users")
objects = UserManager() objects = UserManager()