diff --git a/inventory/forms.py b/inventory/forms.py index 6ddb9c00..4cb1362f 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -1596,8 +1596,8 @@ class PermissionForm(forms.ModelForm): # "inventory.salequotation", # "inventory.salequotationcar" + "django_ledger.purchaseordermodel" "django_ledger.bankaccountmodel", - "django_ledger.chartofaccountmodel", "django_ledger.estimatemodel", "django_ledger.accountmodel", "django_ledger.chartofaccountmodel", @@ -1606,7 +1606,7 @@ class PermissionForm(forms.ModelForm): "django_ledger.invoicemodel", "django_ledger.vendormodel", "django_ledger.journalentrymodel" - # "django_ledger.purchaseordermodel",#TODO add purchase order + "django_ledger.purchaseordermodel",#TODO add purchase order ] permissions = cache.get( diff --git a/inventory/models.py b/inventory/models.py index a8bebb12..e8da8f43 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -2661,8 +2661,9 @@ class CustomGroup(models.Model): "invoicemodel", "vendormodel", "journalentrymodel", + "purchaseordermodel", ], - other_perms=["view_customermodel", "view_estimatemodel"], + other_perms=["view_customermodel", "view_estimatemodel","can_approve_estimatemodel","can_approve_billmodel"], ) elif self.name == "Agent": # Todo : set permissions for agent diff --git a/inventory/templatetags/custom_filters.py b/inventory/templatetags/custom_filters.py index bb91df5a..b33cc5cf 100644 --- a/inventory/templatetags/custom_filters.py +++ b/inventory/templatetags/custom_filters.py @@ -650,7 +650,9 @@ def inventory_table(context, queryset): return ctx + @register.filter +<<<<<<< HEAD def count_checked(permissions): """Count how many permissions are marked as checked""" print(permissions) @@ -670,4 +672,16 @@ def subtract(value, arg): try: return float(value) - float(arg) except (ValueError, TypeError): - return '' # Or raise an error, or return value if conversion fails \ No newline at end of file + return '' # Or raise an error, or return value if conversion fails +======= +def count_checked(permissions, group_permission_ids): + """Count how many permissions are checked from the allowed list""" + if not group_permission_ids: + return 0 + return sum(1 for perm in permissions if perm.id in group_permission_ids) + +# @register.filter +# def count_checked(permissions, group_permission_ids): +# """Count how many permissions are checked from the allowed list""" +# return sum(1 for perm in permissions if perm.id in group_permission_ids) +>>>>>>> 68f7e3fb2c4f4aeb23ae96a9431dade25885d274 diff --git a/inventory/views.py b/inventory/views.py index faa971c6..10df5a68 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -2756,78 +2756,269 @@ def GroupDeleteview(request, dealer_slug,pk): @login_required +# def GroupPermissionView(request, dealer_slug, pk): +# # Verify dealer and group exist +# get_object_or_404(models.Dealer, slug=dealer_slug) +# customgroup = get_object_or_404(models.CustomGroup, pk=pk) + +# if request.method == "POST": +# form = forms.PermissionForm(request.POST, instance=customgroup) +# if form.is_valid(): +# # Clear existing permissions +# customgroup.clear_permissions() + +# # Add new permissions from form +# permissions = form.cleaned_data.get('permissions', []) +# for permission in permissions: +# customgroup.add_permission(permission) + +# messages.success(request, _("Permissions updated successfully")) +# return redirect("group_detail", dealer_slug=dealer_slug, pk=customgroup.pk) +# else: +# # Initial form with current permissions +# form = forms.PermissionForm(instance=customgroup) + +# group_permission_ids = set(customgroup.permissions.values_list('id', flat=True)) + +# # Mark permissions as checked in the form data +# for app_label, model in form.grouped_permissions.items(): +# print(app_label, model) +# for mo, perms in model.items(): +# for perm in perms: +# perm.is_checked = perm.id in group_permission_ids + +# return render(request,"groups/group_permission_form.html", { +# "group": customgroup, +# "form": form, +# "group_permission_apps": set(customgroup.group.permissions.values_list('content_type__app_label', flat=True)), +# "group_permission_models": set(customgroup.group.permissions.values_list('content_type__model', flat=True)) +# }) def GroupPermissionView(request, dealer_slug, pk): - # Verify dealer and group exist + from django.contrib.contenttypes.models import ContentType + from django.db import transaction + from django.db.models import Q + get_object_or_404(models.Dealer, slug=dealer_slug) customgroup = get_object_or_404(models.CustomGroup, pk=pk) + group = customgroup.group + # Define ALL permissions you want to manage + MODEL_LIST = [ + ("inventory", "car"), + ("inventory", "carfinance"), + ("inventory", "carlocation"), + ("inventory", "customcard"), + ("inventory", "cartransfer"), + ("inventory", "carcolors"), + ("inventory", "carequipment"), + ("inventory", "interiorcolors"), + ("inventory", "exteriorcolors"), + ("inventory", "lead"), + ("inventory", "customgroup"), + ("inventory", "saleorder"), + ("inventory", "payment"), + ("inventory", "staff"), + ("inventory", "schedule"), + ("inventory", "activity"), + ("inventory", "opportunity"), + ("inventory", "carreservation"), + ("inventory", "customer"), + ("inventory", "organization"), + + ("django_ledger", "purchaseordermodel"), + ("django_ledger", "bankaccountmodel"), + ("django_ledger", "estimatemodel"), + ("django_ledger", "accountmodel"), + ("django_ledger", "chartofaccountmodel"), + ("django_ledger", "billmodel"), + ("django_ledger", "itemmodel"), + ("django_ledger", "invoicemodel"), + ("django_ledger", "vendormodel"), + ("django_ledger", "journalentrymodel"), + ("django_ledger", "purchaseordermodel"), + ] + CUSTOM_PERMISSIONS = [ + ('django_ledger', 'can_approve_estimatemodel'), + ('django_ledger', 'can_approve_billmodel'), + ] if request.method == "POST": - form = forms.PermissionForm(request.POST, instance=customgroup) - if form.is_valid(): - # Clear existing permissions - customgroup.clear_permissions() + try: + selected_ids = [int(id) for id in request.POST.getlist('permissions', [])] - # Add new permissions from form - permissions = form.cleaned_data.get('permissions', []) - for permission in permissions: - customgroup.add_permission(permission) + # Get content types for model permissions + model_content_types = ContentType.objects.filter( + app_label__in=[m[0] for m in MODEL_LIST], + model__in=[m[1] for m in MODEL_LIST] + ) + + # Get all valid permissions (model CRUD + custom) + valid_perms = Permission.objects.filter( + # Model CRUD permissions + Q(content_type__in=model_content_types) | + # Custom permissions + Q( + content_type__app_label__in=[p[0] for p in CUSTOM_PERMISSIONS], + codename__in=[p[1] for p in CUSTOM_PERMISSIONS] + ), + id__in=selected_ids + ) + + with transaction.atomic(): + group.permissions.clear() + if valid_perms.exists(): + group.permissions.add(*valid_perms) messages.success(request, _("Permissions updated successfully")) return redirect("group_detail", dealer_slug=dealer_slug, pk=customgroup.pk) - else: - # Initial form with current permissions - form = forms.PermissionForm(instance=customgroup) - group_permission_ids = set(customgroup.permissions.values_list('id', flat=True)) + except Exception as e: + messages.error(request, _("Error updating permissions: ") + str(e)) - # Mark permissions as checked in the form data - for app_label, model in form.grouped_permissions.items(): - for mo, perms in model.items(): - for perm in perms: - perm.is_checked = perm.id in group_permission_ids + # GET request handling + # Get permissions for models (CRUD) + model_perms = Permission.objects.filter( + content_type__in=ContentType.objects.filter( + app_label__in=[m[0] for m in MODEL_LIST], + model__in=[m[1] for m in MODEL_LIST] + ) + ) - return render(request,"groups/group_permission_form.html", { + # Get custom permissions + custom_perms = Permission.objects.filter( + content_type__app_label__in=[p[0] for p in CUSTOM_PERMISSIONS], + codename__in=[p[1] for p in CUSTOM_PERMISSIONS] + ) + + # Combine all permissions + all_permissions = model_perms | custom_perms + all_permissions = all_permissions.select_related('content_type').order_by( + 'content_type__app_label', 'content_type__model', 'codename' + ) + + # Group permissions with custom ones in a special section + grouped_permissions = {} + for perm in all_permissions: + app_label = perm.content_type.app_label + + # Check if this is a custom permission + is_custom = any( + p[0] == app_label and p[1] == perm.codename + for p in CUSTOM_PERMISSIONS + ) + + if is_custom: + # Group custom permissions under "Custom" model name + model = "Custom" + else: + model = perm.content_type.model + + if app_label not in grouped_permissions: + grouped_permissions[app_label] = {} + if model not in grouped_permissions[app_label]: + grouped_permissions[app_label][model] = [] + + grouped_permissions[app_label][model].append(perm) + + # Get currently assigned permission IDs + group_permission_ids = set( + group.permissions.filter( + id__in=all_permissions.values_list('id', flat=True) + ).values_list('id', flat=True) + ) + + return render(request, "groups/group_permission_form.html", { "group": customgroup, - "form": form, - "group_permission_apps": set(customgroup.group.permissions.values_list('content_type__app_label', flat=True)), - "group_permission_models": set(customgroup.group.permissions.values_list('content_type__model', flat=True)) + "grouped_permissions": grouped_permissions, + "group_permission_ids": group_permission_ids, + "group_permission_apps": set( + group.permissions.filter( + content_type__app_label__in=grouped_permissions.keys() + ).values_list('content_type__app_label', flat=True) + ), + "group_permission_models": set( + group.permissions.filter( + content_type__app_label__in=grouped_permissions.keys() + ).values_list('content_type__model', flat=True) + ) }) -# def GroupPermissionView(request, dealer_slug,pk): -# """ -# Handles the view for adding or modifying permissions of a specific group. This view -# fetches the group based on the primary key passed as a parameter, and either displays -# a form for editing permissions or processes the submitted permissions. + # if request.method == "POST": + # try: + # selected_ids = [int(id) for id in request.POST.getlist('permissions', [])] -# If the request method is POST, the permissions of the group are cleared and updated -# based on the submitted data. A success message is displayed upon completion, and -# the user is redirected to the group's detail page. + # # Get all permission types for our models + # content_types = ContentType.objects.filter( + # app_label__in=[m[0] for m in MODEL_LIST], + # model__in=[m[1] for m in MODEL_LIST] + # ) -# In case of a GET request, the view renders the form pre-filled with the group's -# current permissions. + # # Get valid permissions that exist for these models + # valid_perms = Permission.objects.filter( + # content_type__in=content_types, + # id__in=selected_ids + # ) + + # # Atomic transaction to ensure data consistency + # with transaction.atomic(): + # # Clear current permissions + # group.permissions.clear() + + # # Add new permissions if any were selected + # if valid_perms.exists(): + # group.permissions.add(*valid_perms) + + # messages.success(request, _("Permissions updated successfully")) + # return redirect("group_detail", dealer_slug=dealer_slug, pk=customgroup.pk) + + # except Exception as e: + # messages.error(request, _("Error updating permissions: ") + str(e)) + + # # GET request handling + # content_types = ContentType.objects.filter( + # app_label__in=[m[0] for m in MODEL_LIST], + # model__in=[m[1] for m in MODEL_LIST] + # ) + + # # Get all permissions for these content types + # all_permissions = Permission.objects.filter( + # content_type__in=content_types + # ).select_related('content_type').order_by('content_type__app_label', 'content_type__model', 'codename') + + # # Group permissions by app and model + # grouped_permissions = {} + # for perm in all_permissions: + # app_label = perm.content_type.app_label + # model = perm.content_type.model + + # if app_label not in grouped_permissions: + # grouped_permissions[app_label] = {} + # if model not in grouped_permissions[app_label]: + # grouped_permissions[app_label][model] = [] + + # grouped_permissions[app_label][model].append(perm) + + # # Get currently assigned permission IDs + # group_permission_ids = set( + # group.permissions.filter( + # content_type__in=content_types + # ).values_list('id', flat=True) + # ) + + # return render(request, "groups/group_permission_form.html", { + # "group": customgroup, + # "grouped_permissions": grouped_permissions, + # "group_permission_ids": group_permission_ids, + # "group_permission_apps": set( + # group.permissions.filter( + # content_type__app_label__in=[m[0] for m in MODEL_LIST] + # ).values_list('content_type__app_label', flat=True) + # ), + # "group_permission_models": set( + # group.permissions.filter( + # content_type__model__in=[m[1] for m in MODEL_LIST] + # ).values_list('content_type__model', flat=True) + # ) + # }) -# :param request: The HTTP request object. -# :type request: HttpRequest -# :param pk: The primary key of the group whose permissions are being modified. -# :type pk: int -# :return: The HTTP response depending on the request type. For GET requests, renders -# the permission form for the specified group. For POST requests, clears and updates -# the group's permissions and redirects to the group's detail page. -# :rtype: HttpResponse -# """ -# get_object_or_404(models.Dealer,slug=dealer_slug) -# group = get_object_or_404(models.CustomGroup, pk=pk) -# if request.method == "POST": -# form = forms.PermissionForm(request.POST) -# group.clear_permissions() -# permissions = request.POST.getlist("name") -# for i in permissions: -# group.add_permission(Permission.objects.get(id=int(i))) -# messages.success(request, _("Permission added successfully")) -# return redirect("group_detail", dealer_slug=dealer_slug,pk=group.pk) -# form = forms.PermissionForm(initial={"name": group.permissions}) -# return render( -# request, "groups/group_permission_form.html", {"group": group, "form": form} -# ) # Users @@ -9382,6 +9573,7 @@ def inventory_items_filter(request, dealer_slug): class PurchaseOrderDetailView(PurchaseOrderModelDetailViewBase): template_name = "purchase_orders/po_detail.html" context_object_name = "po_model" + permission_required = ["inventory.view_carfinance"] def get_queryset(self): dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) diff --git a/templates/groups/group_permission_form.html b/templates/groups/group_permission_form.html index 55863d8b..b36ab961 100644 --- a/templates/groups/group_permission_form.html +++ b/templates/groups/group_permission_form.html @@ -16,7 +16,9 @@ {{ _("Add Permissions") }} {% endif %} - +
+ {{ group_permission_ids|length }} {% trans "permissions assigned" %} +
@@ -24,80 +26,108 @@
{% csrf_token %} + +
+
+
+ + +
+
+
+
+ + {% trans "Checked items are currently assigned permissions" %} +
+
+
+ -
- {% for app_label, models in form.grouped_permissions.items %} -
-
-
-
-
- - {{ app_label|capfirst }} -
- - {{ models|length }} {% trans "models" %} + +
+ {% for app_label, models in grouped_permissions.items %} +
+
+
+
+
+ + {{ app_label|capfirst }} +
+ + {{ models|length }} {% trans "categories" %} + +
+
+
+
+ {% for model, perms in models.items %} +
+
+
-
-
-
- {% for model, perms in models.items %} -
-
- +
+
+
+
+ {% for perm in perms %} + + {% endfor %}
- {% endfor %}
+ {% endfor %}
- {% endfor %}
+
+
+{% endfor %} +
+ + +
- {{ group.permissions.count }} {% trans "selected" %} + {{ group_permission_ids|length }} {% trans "selected" %} {% trans "Permissions will be updated immediately" %} @@ -122,31 +152,59 @@ .list-group-item:hover { background-color: rgba(0, 0, 0, 0.03); } + .accordion-button:not(.collapsed) { + box-shadow: none; + background-color: transparent; + } + .accordion-button:focus { + box-shadow: none; + border-color: rgba(0,0,0,.125); + } {% endblock %} \ No newline at end of file