update permission

This commit is contained in:
ismail 2025-06-30 13:56:23 +03:00
parent 85c151a112
commit 6436649488
5 changed files with 391 additions and 98 deletions

View File

@ -1528,44 +1528,139 @@ class GroupForm(forms.ModelForm):
fields = ["name"]
# class PermissionForm(forms.ModelForm):
# """
# Represents a form for managing permissions using a multiple-choice field.
# This class is a Django ModelForm that is used to handle permission
# assignments. It provides a multiple selection widget pre-populated with
# permissions based on specific app labels. The form offers a way to submit
# and validate permission data for further processing.
# :ivar name: A multiple-choice field that allows users to select permissions
# related to specific app labels (`inventory` and `django_ledger`).
# :type name: ModelMultipleChoiceField
# """
# name = forms.ModelMultipleChoiceField(
# 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 PermissionForm(forms.ModelForm):
"""
Represents a form for managing permissions using a multiple-choice field.
This class is a Django ModelForm that is used to handle permission
assignments. It provides a multiple selection widget pre-populated with
permissions based on specific app labels. The form offers a way to submit
and validate permission data for further processing.
:ivar name: A multiple-choice field that allows users to select permissions
related to specific app labels (`inventory` and `django_ledger`).
:type name: ModelMultipleChoiceField
Form for managing permissions with grouped checkboxes by app and model.
"""
name = forms.ModelMultipleChoiceField(
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(
EXCLUDED_MODELS = [
"inventory.car",
"inventory.carfinance",
"inventory.carlocation",
"inventory.customcard",
"inventory.cartransfer",
"inventory.carcolors",
"inventory.carequipment",
"inventory.interiorcolors",
"inventory.exteriorcolors",
"inventory.carreservation",
"inventory.lead",
"inventory.customgroup",
"inventory.saleorder",
"inventory.payment",
"inventory.staff",
"inventory.schedule",
"inventory.activity",
"inventory.opportunity",
"django_ledger.estimatemodel",
"django_ledger.invoicemodel",
"django_ledger.accountmodel",
"django_ledger.chartofaccountmodel",
"django_ledger.customermodel",
"django_ledger.billmodel"
"inventory.car",
"inventory.carequipment",
"inventory.interiorcolors",
"inventory.exteriorcolors",
"inventory.carcolors",
"inventory.carlocation",
"inventory.customcard",
"inventory.carreservation"
"django_ledger.estimatemodel",
"django_ledger.invoicemodel",
"django_ledger.customermodel",
"inventory.saleorder",
"inventory.payment",
"inventory.staff",
"inventory.schedule",
"inventory.activity",
"inventory.opportunity",
"inventory.customer",
"inventory.organization",
"inventory.lead",
"inventory.salequotation",
"inventory.salequotationcar"
"inventory.carfinance",
"django_ledger.bankaccountmodel",
"django_ledger.accountmodel",
"django_ledger.chartofaccountmodel",
"django_ledger.customcard",
"django_ledger.billmodel",
"django_ledger.itemmodel",
"django_ledger.invoicemodel",
"django_ledger.vendormodel",
"django_ledger.journalentrymodel"
]
permissions = cache.get(
"permissions_queryset",
Permission.objects.filter(
content_type__app_label__in=["inventory", "django_ledger"]
),
60 * 60,
content_type__app_label__in=[m.split('.')[0] for m in EXCLUDED_MODELS],
content_type__model__in=[m.split('.')[1] for m in EXCLUDED_MODELS]
).select_related('content_type')
)
# Group permissions by app_label and model
self.grouped_permissions = {}
for perm in permissions:
app_label = perm.content_type.app_label
model = perm.content_type.model
if app_label not in self.grouped_permissions:
self.grouped_permissions[app_label] = {}
if model not in self.grouped_permissions[app_label]:
self.grouped_permissions[app_label][model] = []
self.grouped_permissions[app_label][model].append(perm)
# Create a multiple choice field (hidden, will use custom rendering)
self.fields['permissions'] = forms.ModelMultipleChoiceField(
queryset=permissions,
widget=forms.MultipleHiddenInput(),
required=False,
initial=self.instance.permissions.all() if self.instance.pk else []
)
class Meta:
model = Permission
fields = ["name"]
fields = []
class UserGroupForm(forms.ModelForm):

View File

@ -644,4 +644,11 @@ def inventory_table(context, queryset):
"inventory_list": queryset,
}
ctx.update(queryset.aggregate(inventory_total_value=Sum("total_value")))
return ctx
return ctx
@register.filter
def count_checked(permissions):
"""Count how many permissions are marked as checked"""
print(permissions)
return sum(1 for perm in permissions if getattr(perm, 'is_checked', False))

View File

@ -2757,42 +2757,78 @@ def GroupDeleteview(request, dealer_slug,pk):
@login_required
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.
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 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.
In case of a GET request, the view renders the form pre-filled with the group's
current permissions.
: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}
)
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():
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):
# """
# 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 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.
# In case of a GET request, the view renders the form pre-filled with the group's
# current permissions.
# :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

View File

@ -0,0 +1,45 @@
{% extends "base.html" %}
{% load i18n %}
{% load crispy_forms_filters %}
{% block title %}{% trans "Permission" %}{% endblock title %}
{% block content %}
<div class="row">
<div class="row">
<div class="col-sm-9">
<div class="d-sm-flex justify-content-between">
<h3 class="mb-3">
{% if group.created %}
{{ _("Edit Permission") }}
{% else %}
{{ _("Add Permission") }}
{% endif %}
</h3>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-9">
<form class="row g-3 mb-9" method="post" class="form" novalidate>
{% csrf_token %}
{{ redirect_field }}
{{ form|crispy }}
{% for error in form.errors %}
<div class="text-danger">{{ error }}</div>
{% endfor %}
<div class="d-flex mb-3">
<a href="{% url 'group_detail' request.dealer.slug group.pk %}" class="btn btn-phoenix-primary me-2 "><i class="fa-solid fa-ban"></i> {% trans "Cancel"|capfirst %}</a>
<button class="btn btn-phoenix-primary" type="submit">
<i class="fa-solid fa-floppy-disk"></i>
{{ _("Save") }}
</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,45 +1,155 @@
{% extends "base.html" %}
{% load i18n %}
{% load crispy_forms_filters %}
{% block title %}{% trans "Permission" %}{% endblock title %}
{% load custom_filters %}
{% block title %}{% trans "Permission Management" %}{% endblock title %}
{% block content %}
<div class="container-fluid">
<div class="row mb-4">
<div class="col">
<div class="d-sm-flex justify-content-between align-items-center">
<h3 class="mb-0">
{% if group %}
{{ _("Edit Permissions for") }}: <strong>{{ group.name }}</strong>
{% else %}
{{ _("Add Permissions") }}
{% endif %}
</h3>
<div class="row">
<div class="row">
<div class="col-sm-9">
<div class="d-sm-flex justify-content-between">
<h3 class="mb-3">
{% if group.created %}
{{ _("Edit Permission") }}
{% else %}
{{ _("Add Permission") }}
{% endif %}
</h3>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-9">
<form class="row g-3 mb-9" method="post" class="form" novalidate>
{% csrf_token %}
{{ redirect_field }}
{{ form|crispy }}
{% for error in form.errors %}
<div class="text-danger">{{ error }}</div>
{% endfor %}
<div class="d-flex mb-3">
<a href="{% url 'group_detail' request.dealer.slug group.pk %}" class="btn btn-phoenix-primary me-2 "><i class="fa-solid fa-ban"></i> {% trans "Cancel"|capfirst %}</a>
<button class="btn btn-phoenix-primary" type="submit">
<i class="fa-solid fa-floppy-disk"></i>
{{ _("Save") }}
</button>
</div>
</form>
</div>
</div>
</div>
<form method="post" novalidate>
{% csrf_token %}
<!-- Permissions Grid -->
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4" id="permissionsGrid">
{% for app_label, models in form.grouped_permissions.items %}
<div class="col">
<div class="card h-100 border-{% if app_label in group_permission_apps %}primary{% else %}light{% endif %}">
<div class="card-header bg-{% if app_label in group_permission_apps %}primary text-white{% else %}light{% endif %}">
<div class="d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-{% if app_label in group_permission_apps %}check-circle{% else %}cube{% endif %} me-2"></i>
{{ app_label|capfirst }}
</h5>
<span class="badge bg-{% if app_label in group_permission_apps %}light text-primary{% else %}secondary{% endif %}">
{{ models|length }} {% trans "models" %}
</span>
</div>
</div>
<div class="card-body">
<div class="accordion">
{% for model, perms in models.items %}
<div class="accordion-item border-0">
<h6 class="accordion-header" id="heading-{{ app_label|slugify }}-{{ model|slugify }}">
<button class="accordion-button collapsed bg-white shadow-none py-2"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapse-{{ app_label|slugify }}-{{ model|slugify }}"
aria-expanded="false">
<i class="fas fa-table me-2"></i>{{ model|capfirst }}
<span class="badge bg-{% if model in group_permission_models %}primary{% else %}secondary{% endif %} rounded-pill ms-2">
{{ perms|length }} / {{ perms|count_checked }}
</span>
</button>
</h6>
<div id="collapse-{{ app_label|slugify }}-{{ model|slugify }}"
class="accordion-collapse collapse"
aria-labelledby="heading-{{ app_label|slugify }}-{{ model|slugify }}">
<div class="accordion-body pt-0">
<div class="list-group list-group-flush">
{% for perm in perms %}
<label class="list-group-item d-flex gap-2 {% if perm.is_checked %}bg-light-primary{% endif %}">
<input class="form-check-input flex-shrink-0 mt-0"
type="checkbox"
name="permissions"
value="{{ perm.id }}"
id="perm_{{ perm.id }}"
{% if perm.is_checked %}checked{% endif %}>
<span>
<span class="d-block fw-bold">{{ perm.name|capfirst }}</span>
<small class="d-block text-muted">{{ perm.codename }}</small>
{% if perm.is_checked %}
<span class="badge bg-success mt-1">
<i class="fas fa-check me-1"></i>{% trans "Assigned" %}
</span>
{% endif %}
</span>
</label>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="row mt-4">
<div class="col">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="badge bg-primary rounded-pill me-2">
{{ group.permissions.count }} {% trans "selected" %}
</span>
<span class="text-muted">
{% trans "Permissions will be updated immediately" %}
</span>
</div>
<div>
<a href="{% url 'group_detail' request.dealer.slug group.pk %}"
class="btn btn-outline-secondary me-2">
<i class="fas fa-ban me-2"></i>{% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-2"></i>{% trans "Save Changes" %}
</button>
</div>
</div>
</div>
</div>
</form>
</div>
<style>
.bg-light-primary {
background-color: rgba(13, 110, 253, 0.1);
}
.list-group-item:hover {
background-color: rgba(0, 0, 0, 0.03);
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Search functionality
document.getElementById('permissionSearch').addEventListener('input', function(e) {
const searchTerm = e.target.value.toLowerCase();
document.querySelectorAll('.accordion-body .list-group-item').forEach(item => {
const text = item.textContent.toLowerCase();
item.style.display = text.includes(searchTerm) ? '' : 'none';
});
// Open relevant accordions
document.querySelectorAll('.accordion-collapse').forEach(collapse => {
const visibleItems = collapse.querySelectorAll('.list-group-item[style=""]');
if (visibleItems.length > 0) {
const button = document.querySelector(
`button[data-bs-target="#${collapse.id}"]`
);
if (button && !button.classList.contains('collapsed')) {
new bootstrap.Collapse(collapse, {toggle: true});
}
}
});
});
});
</script>
{% endblock %}