diff --git a/inventory/forms.py b/inventory/forms.py index 98a299fa..63fbfb81 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -1,3 +1,4 @@ +from django.core.cache import cache from django.contrib.auth.models import Permission from django.contrib.auth.models import Group from appointment.models import Appointment, Service, StaffMember @@ -10,7 +11,7 @@ from django.core.validators import RegexValidator from django import forms from django.contrib.auth import get_user_model from phonenumber_field.phonenumber import PhoneNumber -from .models import Status, Stage +from .models import CustomGroup, Status, Stage from .mixins import AddClassMixin from django.forms.models import inlineformset_factory from django_ledger.forms.invoice import ( @@ -900,25 +901,29 @@ class OpportunityStatusForm(forms.Form): class GroupForm(forms.ModelForm): class Meta: - model = Group + model = CustomGroup fields = ["name"] class PermissionForm(forms.ModelForm): name = forms.ModelMultipleChoiceField( - queryset=Permission.objects.filter(content_type__app_label='inventory'), + queryset=cache.get('permissions_queryset', Permission.objects.filter(content_type__app_label__in=["inventory","django_ledger"])), widget=forms.CheckboxSelectMultiple(), required=True ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + cache.set('permissions_queryset', Permission.objects.filter(content_type__app_label__in=["inventory","django_ledger"]), 60*60) + class Meta: model = Permission fields = ["name"] - + class UserGroupForm(forms.ModelForm): name = forms.ModelMultipleChoiceField( - queryset= Group.objects.all(), + queryset= CustomGroup.objects.all(), widget=forms.CheckboxSelectMultiple(), required=True ) class Meta: - model = Group + model = CustomGroup fields = ["name"] \ No newline at end of file diff --git a/inventory/migrations/0038_customgroup.py b/inventory/migrations/0038_customgroup.py new file mode 100644 index 00000000..4e822640 --- /dev/null +++ b/inventory/migrations/0038_customgroup.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.17 on 2025-02-20 08:16 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ('inventory', '0037_alter_schedule_scheduled_type'), + ] + + operations = [ + migrations.CreateModel( + name='CustomGroup', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer')), + ('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='auth.group', verbose_name='')), + ], + ), + ] diff --git a/inventory/migrations/0039_alter_customgroup_dealer.py b/inventory/migrations/0039_alter_customgroup_dealer.py new file mode 100644 index 00000000..c3bfae8f --- /dev/null +++ b/inventory/migrations/0039_alter_customgroup_dealer.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.17 on 2025-02-20 08:17 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0038_customgroup'), + ] + + operations = [ + migrations.AlterField( + model_name='customgroup', + name='dealer', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='groups', to='inventory.dealer'), + ), + ] diff --git a/inventory/models.py b/inventory/models.py index 21dc7129..7bd2d344 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -1,3 +1,4 @@ +from django.contrib.auth.models import Permission from decimal import Decimal import hashlib from django.db import models @@ -957,11 +958,20 @@ class Staff(models.Model, LocalizedNameMixin): @property def user(self): - return self.staff_member.user + return self.staff_member.user @property def groups(self): - return self.staff_member.user.groups + return [x.customgroup for x in self.user.groups.all()] + + def clear_groups(self): + return self.user.groups.clear() + + def add_group(self,group): + try: + self.user.groups.add(group) + except Exception as e: + pass class Meta: verbose_name = _("Staff") verbose_name_plural = _("Staff") @@ -1783,4 +1793,78 @@ class SaleOrder(models.Model): @property def customer(self): - return self.estimate.customer \ No newline at end of file + return self.estimate.customer + +class CustomGroup(models.Model): + name = models.CharField(max_length=100) + dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="groups") + group = models.OneToOneField("auth.Group", verbose_name=_(""), on_delete=models.CASCADE) + + @property + def users(self): + return self.group.user_set.all() + + @property + def permissions(self): + return self.group.permissions.all() + + def clear_permissions(self): + self.group.permissions.clear() + + def add_permission(self, permission): + try: + self.group.permissions.add(permission) + except Permission.DoesNotExist: + pass + + def __str__(self): + return self.name + + def set_default_manager_permissions(self): + self.clear_permissions() + try: + for perm in Permission.objects.filter(content_type__app_label="inventory"): + self.add_permission(perm) + except Exception as e: + pass + + # def set_default_inventory_permissions(self): + # self.clear_permissions() + # allowed_models = ["car","carequipment","interiorcolors","exteriorcolors","carcolors","carlocation","customcard"] + # self.set_permissions(allowed_models=allowed_models,other_perms=['view_carfinance']) + # def set_default_accountant_permissions(self): + # self.clear_permissions() + # allowed_models = ["car","carfinance","carlocation","customcard"] + # allowed_models_ledger = ["accountmodel","chartofaccountmodel","customcard","billmodel"] + # self.set_permissions(allowed_models=allowed_models,other_perms=['view_carfinance']) + # self.set_permissions(app="django_ledger",allowed_models=allowed_models_ledger) + + def set_default_permissions(self): + self.clear_permissions() + if self.name == "Manager": + self.set_permissions(app="inventory",allowed_models=["car","carfinance","carlocation","customcard"]) + self.set_permissions(app="django_ledger",allowed_models=["accountmodel","chartofaccountmodel","customcard","billmodel"]) + elif self.name == "Inventory": + self.set_permissions(allowed_models=["car","carequipment","interiorcolors","exteriorcolors","carcolors","carlocation","customcard"] + ,other_perms=['view_carfinance']) + elif self.name == "Sales": + self.set_permissions(app="inventory",allowed_models=["lead","salequotation","salequotationcar"], + other_perms=['view_car','view_carlocation','view_customcard','view_carcolors','view_cartransfer']. + extend(['view_estimatemodel','view_invoicemodel','view_saleorder'])) + elif self.name == "Accountant": + self.set_permissions(app="inventory",allowed_models=["carfinance"],other_perms=['view_car','view_carlocation','view_customcard','view_carcolors','view_cartransfer']) + self.set_permissions(app="django_ledger",allowed_models=["accountmodel","chartofaccountmodel","customcard","billmodel"]) + elif self.name == "Agent": + # Todo : set permissions for agent + pass + + + def set_permissions(self,app="inventory", allowed_models=[],other_perms=[]): + try: + for perm in Permission.objects.filter(content_type__app_label=app,content_type__model__in=allowed_models): + self.add_permission(perm) + for perm in other_perms: + Permission.objects.get(codename=perm) + self.add_permission(perm) + except Exception as e: + pass \ No newline at end of file diff --git a/inventory/signals.py b/inventory/signals.py index 67e646f0..9397c65d 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -1,3 +1,4 @@ +from django.contrib.auth.models import Group from decimal import Decimal from django.db.models.signals import post_save, post_delete, pre_delete, pre_save from .utils import to_dict @@ -16,6 +17,7 @@ from django_ledger.models import ( ) from . import models from django.utils.timezone import now +from django.db import transaction User = get_user_model() @@ -614,8 +616,19 @@ def create_ledger_entity(sender, instance, created, **kwargs): entity.create_account(coa_model=coa, code="6303", role=roles.EXPENSE_OTHER, name=_("Foreign Currency Translation"), balance_type="debit", active=True) entity.create_account(coa_model=coa, code="6304", role=roles.EXPENSE_OTHER, name=_("Interest Expenses"), balance_type="debit", active=True) - - + +@receiver(post_save, sender=models.Dealer) +def create_dealer_groups(sender, instance, created, **kwargs): + + group_names = ["Manager", "Inventory", "Accountant", "Agent", "Sales"] + + def create_groups(): + for group_name in group_names: + group, created = Group.objects.get_or_create(name=f"{instance.pk}_{group_name}") + group_manager,created = models.CustomGroup.objects.get_or_create(name=group_name, dealer=instance, group=group) + group_manager.set_default_permissions() + + transaction.on_commit(create_groups) # Create Vendor @receiver(post_save, sender=models.Vendor) def create_ledger_vendor(sender, instance, created, **kwargs): diff --git a/inventory/views.py b/inventory/views.py index c29bb78a..c098fe82 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -1871,65 +1871,60 @@ def delete_vendor(request, pk): #group class GroupListView(LoginRequiredMixin, ListView): - model = Group + model = models.CustomGroup 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) + def get_queryset(self): + dealer = get_user_type(self.request) + return dealer.groups.all() class GroupDetailView(LoginRequiredMixin, DetailView): - model = Group + model = models.CustomGroup template_name = "groups/group_detail.html" - context_object_name = "group" - + context_object_name = "group" + class GroupCreateView( LoginRequiredMixin, SuccessMessageMixin, CreateView, ): - model = Group + model = models.CustomGroup 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) - + def form_valid(self, form): + dealer = get_user_type(self.request) + instance = form.save(commit=False) + group = Group.objects.create(name=f"{dealer.pk}_{instance.name}") + instance.dealer = dealer + instance.group = group + instance.save() + return super().form_valid(form) + class GroupUpdateView( LoginRequiredMixin, SuccessMessageMixin, UpdateView, ): - model = Group + model = models.CustomGroup form_class = forms.GroupForm template_name = "groups/group_form.html" success_url = reverse_lazy("group_list") success_message = _("Group updated successfully.") + def form_valid(self, form): + dealer = get_user_type(self.request) + instance = form.save(commit=False) + instance.group.name = f"{dealer.pk}_{instance.name}" + instance.save() + return super().form_valid(form) + # def get_form_kwargs(self): # kwargs = super().get_form_kwargs() # kwargs["instance"] = self.get_object() # Pass the Staff instance to the form @@ -1961,28 +1956,22 @@ class GroupUpdateView( # return super().form_valid(form) def GroupDeleteview(request, pk): - group = get_object_or_404(Group, pk=pk) + group = get_object_or_404(models.CustomGroup, 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) + group = get_object_or_404(models.CustomGroup, pk=pk) if request.method == "POST": - form = forms.PermissionForm(request.POST) - group.permissions.clear() + form = forms.PermissionForm(request.POST) + group.clear_permissions() permissions = request.POST.getlist("name") - - for i in permissions: - try: - group.permissions.add(Permission.objects.get(id=int(i))) - except Permission.DoesNotExist: - continue - + for i in permissions: + group.add_permission(Permission.objects.get(id=int(i))) messages.success(request, _("Permission added successfully.")) - return redirect("group_detail", pk=group.pk) - - form = forms.PermissionForm(initial={"name": group.permissions.all()}) + return redirect("group_detail", pk=group.pk) + form = forms.PermissionForm(initial={"name": group.permissions}) return render(request,"groups/group_permission_form.html",{"group": group, "form": form}) # Users @@ -1991,18 +1980,16 @@ 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 + groups = request.POST.getlist("name") + staff.clear_groups() + for i in groups: + cg = models.CustomGroup.objects.get(id=int(i)) + staff.add_group(cg.group) messages.success(request, _("Group added successfully.")) return redirect("user_detail", pk=staff.pk) - form = forms.UserGroupForm(initial={"name": staff.user.groups.all()}) + form = forms.UserGroupForm(initial={"name": staff.groups}) return render(request,"users/user_group_form.html",{"staff": staff, "form": form}) class UserListView(LoginRequiredMixin, ListView): @@ -2049,9 +2036,9 @@ class UserCreateView( staff = form.save(commit=False) staff.staff_member = staff_member staff.dealer = dealer - group = Group.objects.filter(name__iexact=staff.staff_type).first() + group = Group.objects.filter(customgroup__name__iexact=staff.staff_type).first() if group: - staff.groups.add(group) + staff.add_group(group) staff.save() return super().form_valid(form) diff --git a/scripts/run.py b/scripts/run.py index ab70e21c..b853355f 100644 --- a/scripts/run.py +++ b/scripts/run.py @@ -1,10 +1,11 @@ +from django.contrib.auth.models import Group from django_ledger.models.invoice import InvoiceModel from django_ledger.utils import accruable_net_summary from decimal import Decimal from django_ledger.models import EstimateModel,EntityModel,ItemModel,ItemTransactionModel,AccountModel,CustomerModel from rich import print from datetime import date -from inventory.models import Car, Dealer, VatRate,Lead,CarMake,CarModel,Schedule +from inventory.models import Car, Dealer, VatRate,Lead,CarMake,CarModel,Schedule,CustomGroup from inventory.utils import CarFinanceCalculator from appointment.models import Appointment,AppointmentRequest,Service,StaffMember from django.contrib.auth import get_user_model @@ -121,6 +122,18 @@ def run(): # print(CustomerModel.objects.all()) # customer = CustomerModel.objects.first() + # dealer = Dealer.objects.filter(user__email="esma3el555@gmail.com").first() + # customer = CustomerModel.objects.filter(dealer=dealer).first() + # print(Car.objects.filter(dealer=dealer,status="available")) + + # CustomGroup.objects.all().delete() + # Group.objects.all().delete() + dealer = Dealer.objects.filter(user__email="esma3el555@gmail.com").first() - customer = CustomerModel.objects.filter(dealer=dealer).first() - print(Car.objects.filter(dealer=dealer,status="available")) \ No newline at end of file + group_names = ["Manager", "Inventory", "Accountant", "Agent", "Sales"] + for group_name in group_names: + group,created = Group.objects.get_or_create(name=f"{dealer.pk}_{group_name}") + group_manager,created = CustomGroup.objects.get_or_create(name=group_name, dealer=dealer, group=group) + group_manager.set_default_permissions() + + print(CustomGroup.objects.all()) \ No newline at end of file diff --git a/templates/groups/group_detail.html b/templates/groups/group_detail.html index 1ed60502..d6ee9c0f 100644 --- a/templates/groups/group_detail.html +++ b/templates/groups/group_detail.html @@ -55,7 +55,7 @@
- {% for user in group.user_set.all %} + {% for user in group.users %}{{ _("Name") }}: {{ user.staffmember.staff }}
{{ _("Email") }}: {{ user }}