diff --git a/inventory/forms.py b/inventory/forms.py index 8f2fd0fe..98a299fa 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -1,3 +1,5 @@ +from django.contrib.auth.models import Permission +from django.contrib.auth.models import Group from appointment.models import Appointment, Service, StaffMember from django.urls import reverse from django_countries.widgets import CountrySelectWidget @@ -896,3 +898,27 @@ class OpportunityStatusForm(forms.Form): required=True, ) +class GroupForm(forms.ModelForm): + class Meta: + model = Group + fields = ["name"] + +class PermissionForm(forms.ModelForm): + name = forms.ModelMultipleChoiceField( + queryset=Permission.objects.filter(content_type__app_label='inventory'), + widget=forms.CheckboxSelectMultiple(), + required=True + ) + class Meta: + model = Permission + fields = ["name"] + +class UserGroupForm(forms.ModelForm): + name = forms.ModelMultipleChoiceField( + queryset= Group.objects.all(), + widget=forms.CheckboxSelectMultiple(), + required=True + ) + class Meta: + model = Group + fields = ["name"] \ No newline at end of file diff --git a/inventory/middleware.py b/inventory/middleware.py index 665d9d00..be51159c 100644 --- a/inventory/middleware.py +++ b/inventory/middleware.py @@ -54,7 +54,6 @@ class InjectDealerMiddleware: try: dealer = get_user_type(request) request.user.dealer = dealer - except Exception as e: pass response = self.get_response(request) diff --git a/inventory/models.py b/inventory/models.py index 9e4d5eec..e1caca3d 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -938,6 +938,13 @@ class Staff(models.Model, LocalizedNameMixin): objects = StaffUserManager() + @property + def user(self): + return self.staff_member.user + + @property + def groups(self): + return self.staff_member.user.groups class Meta: verbose_name = _("Staff") verbose_name_plural = _("Staff") diff --git a/inventory/urls.py b/inventory/urls.py index 25b5f6fa..d4e737b0 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -336,6 +336,14 @@ urlpatterns = [ path("user//", views.UserDetailView.as_view(), name="user_detail"), path("user/", views.UserListView.as_view(), name="user_list"), path("user//confirm/", views.UserDeleteview, name="user_delete"), + path("user//groups/", views.UserGroupView, name="user_groups"), + # Group URLs + path("group/create/", views.GroupCreateView.as_view(), name="group_create"), + path("group//update/", views.GroupUpdateView.as_view(), name="group_update"), + path("group//", views.GroupDetailView.as_view(), name="group_detail"), + path("group/", views.GroupListView.as_view(), name="group_list"), + path("group//confirm/", views.GroupDeleteview, name="group_delete"), + path("group//permission/", views.GroupPermissionView, name="group_permission"), # Organization URLs path( "organizations/", views.OrganizationListView.as_view(), name="organization_list" diff --git a/inventory/views.py b/inventory/views.py index c3e4eee5..47b5c989 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -1,4 +1,5 @@ from django.db.models import Func +from django.contrib.auth.models import Permission from appointment.models import Appointment,AppointmentRequest,Service,StaffMember from datetime import timedelta from calendar import month_name @@ -1845,8 +1846,142 @@ def delete_vendor(request, pk): # slug_field = "order_id" # slug_url_kwarg = "order_id" +#group +class GroupListView(LoginRequiredMixin, ListView): + model = Group + context_object_name = "groups" + paginate_by = 10 + template_name = "groups/group_list.html" + + # def get_queryset(self): + # query = self.request.GET.get("q") + # dealer = get_user_type(self.request) + # staff = models.Staff.objects.filter(dealer=dealer).all() + # return apply_search_filters(staff, query) + +class GroupDetailView(LoginRequiredMixin, DetailView): + model = Group + template_name = "groups/group_detail.html" + context_object_name = "group" + + +class GroupCreateView( + LoginRequiredMixin, + SuccessMessageMixin, + CreateView, +): + model = Group + form_class = forms.GroupForm + template_name = "groups/group_form.html" + success_url = reverse_lazy("group_list") + success_message = _("Group created successfully.") + + # def form_valid(self, form): + # dealer = get_user_type(self.request) + + # email = form.cleaned_data["email"] + # password = "Tenhal@123" + # user = User.objects.create_user(username=form.cleaned_data["name"], email=email, password=password) + # user.is_staff = True + # user.save() + # staff_member = StaffMember.objects.create(user=user) + # services = form.cleaned_data["service_offered"] + # if services: + # for service in services: + # staff_member.services_offered.add(service) + # staff = form.save(commit=False) + # staff.staff_member = staff_member + # staff.dealer = dealer + # staff.save() + # return super().form_valid(form) + + +class GroupUpdateView( + LoginRequiredMixin, + SuccessMessageMixin, + UpdateView, +): + model = Group + form_class = forms.GroupForm + template_name = "groups/group_form.html" + success_url = reverse_lazy("group_list") + success_message = _("Group updated successfully.") + + # def get_form_kwargs(self): + # kwargs = super().get_form_kwargs() + # kwargs["instance"] = self.get_object() # Pass the Staff instance to the form + # return kwargs + + # def get_form(self, form_class = None): + # form = super().get_form(form_class) + # form.fields['email'].disabled = True + # return form + # def get_initial(self): + # initial = super().get_initial() + # initial['service_offered'] = self.object.staff_member.services_offered.all() + # initial['email'] = self.object.staff_member.user.email + # return initial + # def form_valid(self, form): + # services = form.cleaned_data["service_offered"] + # if not services: + # self.object.staff_member.services_offered.clear() + # else: + # for service in services: + # self.object.staff_member.services_offered.add(service) + + # staff = form.save(commit=False) + # staff.name = form.cleaned_data["name"] + # staff.arabic_name = form.cleaned_data["arabic_name"] + # staff.phone_number = form.cleaned_data["phone_number"] + # staff.staff_type = form.cleaned_data["staff_type"] + # staff.save() + # return super().form_valid(form) + +def GroupDeleteview(request, pk): + group = get_object_or_404(Group, pk=pk) + group.delete() + messages.success(request, _("Group deleted successfully.")) + return redirect("group_list") + +def GroupPermissionView(request, pk): + group = get_object_or_404(Group, pk=pk) + if request.method == "POST": + form = forms.PermissionForm(request.POST) + group.permissions.clear() + permissions = request.POST.getlist("name") + + for i in permissions: + try: + group.permissions.add(Permission.objects.get(id=int(i))) + except Permission.DoesNotExist: + continue + + messages.success(request, _("Permission added successfully.")) + return redirect("group_detail", pk=group.pk) + + form = forms.PermissionForm(initial={"name": group.permissions.all()}) + return render(request,"groups/group_permission_form.html",{"group": group, "form": form}) # Users + +def UserGroupView(request, pk): + staff = get_object_or_404(models.Staff, pk=pk) + if request.method == "POST": + form = forms.UserGroupForm(request.POST) + groups = request.POST.getlist("name") + staff.groups.clear() + for i in groups: + try: + staff.groups.add(Group.objects.get(id=int(i))) + except Group.DoesNotExist: + continue + + messages.success(request, _("Group added successfully.")) + return redirect("user_detail", pk=staff.pk) + + form = forms.UserGroupForm(initial={"name": staff.user.groups.all()}) + return render(request,"users/user_group_form.html",{"staff": staff, "form": form}) + class UserListView(LoginRequiredMixin, ListView): model = models.Staff context_object_name = "users" @@ -1885,14 +2020,15 @@ class UserCreateView( user = User.objects.create_user(username=form.cleaned_data["name"], email=email, password=password) user.is_staff = True user.save() - staff_member = StaffMember.objects.create(user=user) - services = form.cleaned_data["service_offered"] - if services: - for service in services: - staff_member.services_offered.add(service) + staff_member = StaffMember.objects.create(user=user) + for service in form.cleaned_data["service_offered"]: + staff_member.services_offered.add(service) staff = form.save(commit=False) staff.staff_member = staff_member staff.dealer = dealer + group = Group.objects.filter(name__iexact=staff.staff_type).first() + if group: + staff.groups.add(group) staff.save() return super().form_valid(form) @@ -1919,8 +2055,8 @@ class UserUpdateView( return form def get_initial(self): initial = super().get_initial() - initial['service_offered'] = self.object.staff_member.services_offered.all() initial['email'] = self.object.staff_member.user.email + initial['service_offered'] = self.object.staff_member.services_offered.all() return initial def form_valid(self, form): services = form.cleaned_data["service_offered"] @@ -1934,12 +2070,13 @@ class UserUpdateView( staff.name = form.cleaned_data["name"] staff.arabic_name = form.cleaned_data["arabic_name"] staff.phone_number = form.cleaned_data["phone_number"] - staff.staff_type = form.cleaned_data["staff_type"] + staff.staff_type = form.cleaned_data["staff_type"] staff.save() return super().form_valid(form) def UserDeleteview(request, pk): user = get_object_or_404(models.Staff, pk=pk) + user.staff_member.delete() user.delete() messages.success(request, _("User deleted successfully.")) return redirect("user_list") diff --git a/templates/groups/group_detail.html b/templates/groups/group_detail.html new file mode 100644 index 00000000..1ed60502 --- /dev/null +++ b/templates/groups/group_detail.html @@ -0,0 +1,119 @@ +{% extends "base.html" %} +{% load static %} +{% load i18n %} + +{% block title %}{{ _("View Group") }}{% endblock title %} + +{% block content %} + + + +
+
+
+

{{ _("Group Details") }}

+
+
+
+
+

{{ _("Name") }}: {{ group.name }}

+
+
+ + + + + + + + + {% for user in group.user_set.all %} + + + + + {% empty %} + + + + {% endfor %} + +

{% trans 'Users'|capfirst %}

{{ _("Name") }}: {{ user.staffmember.staff }}

{{ _("Email") }}: {{ user }}

{% trans "No Permissions" %}
+ +
+
+
+
+
+

Permissions

+ Manage Permissions + + + + + + + + + + {% for permission in group.permissions.all %} + + + + + {% empty %} + + + + {% endfor %} + +
{% trans 'name'|capfirst %}{% trans 'Action'|capfirst %}
{{ permission.codename }}{{ permission.name }}
{% trans "No Permissions" %}
+
+ +
+
+{% endblock %} diff --git a/templates/groups/group_form.html b/templates/groups/group_form.html new file mode 100644 index 00000000..dda25677 --- /dev/null +++ b/templates/groups/group_form.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% load i18n %} +{% load crispy_forms_filters %} +{% block title %}{% trans "Group" %}{% endblock title %} + +{% block content %} + + +
+
+
+
+ +

+ {% if staff.created %} + {{ _("Edit Group") }} + {% else %} + {{ _("Add Group") }} + {% endif %} +

+
+
+
+
+
+ +
+ {% csrf_token %} + {{ redirect_field }} + {{ form|crispy }} + {% for error in form.errors %} +
{{ error }}
+ {% endfor %} +
+ {% trans "cancel"|capfirst %} + +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/groups/group_list.html b/templates/groups/group_list.html new file mode 100644 index 00000000..fc1057e1 --- /dev/null +++ b/templates/groups/group_list.html @@ -0,0 +1,57 @@ +{% extends "base.html" %} +{% load i18n %} +{% load render_table from django_tables2 %} + +{% block title %}{% trans "Group" %}{% endblock title %} + +{% block content %} +
+ +
+
+ +
+
+ + + + + + + + + + + + {% for group in groups %} + + + + + + + {% endfor %} + +
{% trans 'name'|capfirst %}{% trans 'total Users'|capfirst %}{% trans 'total permission'|capfirst %}{% trans 'actions'|capfirst %}
{{ group.name }} {{ group.user_set.count }} {{ group.permissions.count }} + + + {% trans 'view'|capfirst %} + +
+
+
+ {% if is_paginated %} + {% include 'partials/pagination.html' %} + {% endif %} +
+ +
+
+{% endblock %} + diff --git a/templates/groups/group_permission_form.html b/templates/groups/group_permission_form.html new file mode 100644 index 00000000..b9327b6d --- /dev/null +++ b/templates/groups/group_permission_form.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% load i18n %} +{% load crispy_forms_filters %} +{% block title %}{% trans "Permission" %}{% endblock title %} + +{% block content %} + + +
+
+
+
+ +

+ {% if group.created %} + {{ _("Edit Permission") }} + {% else %} + {{ _("Add Permission") }} + {% endif %} +

+
+
+
+
+
+ +
+ {% csrf_token %} + {{ redirect_field }} + {{ form|crispy }} + {% for error in form.errors %} +
{{ error }}
+ {% endfor %} +
+ {% trans "cancel"|capfirst %} + +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/header.html b/templates/header.html index 26894a64..495059e5 100644 --- a/templates/header.html +++ b/templates/header.html @@ -411,7 +411,7 @@
- +
{% if user.dealer.logo %} @@ -441,7 +441,7 @@ {% endif %} {% if user.dealer %}
+
diff --git a/templates/users/user_group_form.html b/templates/users/user_group_form.html new file mode 100644 index 00000000..8748b43b --- /dev/null +++ b/templates/users/user_group_form.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} +{% load i18n %} +{% load crispy_forms_filters %} +{% block title %}{% trans "Group" %}{% endblock title %} + +{% block content %} + + +
+
+
+
+

{{ _("Manage Groups") }}

+
+
+
+
+
+ +
+ {% csrf_token %} + {{ redirect_field }} + {{ form|crispy }} + {% for error in form.errors %} +
{{ error }}
+ {% endfor %} +
+ {% trans "cancel"|capfirst %} + +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/users/user_list.html b/templates/users/user_list.html index 730fc84c..748ddcc3 100644 --- a/templates/users/user_list.html +++ b/templates/users/user_list.html @@ -11,8 +11,8 @@ @@ -25,6 +25,7 @@ {% trans 'arabic name'|capfirst %} {% trans 'phone number'|capfirst %} {% trans 'role'|capfirst %} + {% trans 'groups'|capfirst %} {% trans 'actions'|capfirst %} @@ -34,10 +35,18 @@ {{ user.name }} {{ user.arabic_name }} {{ user.phone_number }} - {% trans user.staff_type %} + + {% trans user.staff_type|title %} + + + {% for group in user.groups.all %} + {{ group.name }} + {% endfor %} + + {% trans 'view'|capfirst %}