Merge branch 'main' of http://10.10.1.136:3000/ismail/haikal into frontend

This commit is contained in:
Faheedkhan 2025-07-01 16:40:25 +03:00
commit d506f11545
8 changed files with 76 additions and 65 deletions

View File

@ -21,8 +21,8 @@ urlpatterns += i18n_patterns(
path("switch_language/", views.switch_language, name="switch_language"), path("switch_language/", views.switch_language, name="switch_language"),
path("accounts/", include("allauth.urls")), path("accounts/", include("allauth.urls")),
# path('prometheus/', include('django_prometheus.urls')), # path('prometheus/', include('django_prometheus.urls')),
path("", include("inventory.urls")),
path("ledger/", include("django_ledger.urls", namespace="django_ledger")), path("ledger/", include("django_ledger.urls", namespace="django_ledger")),
path("", include("inventory.urls")),
path("haikalbot/", include("haikalbot.urls")), path("haikalbot/", include("haikalbot.urls")),
path("appointment/", include("appointment.urls")), path("appointment/", include("appointment.urls")),
path("plans/", include("plans.urls")), path("plans/", include("plans.urls")),

View File

@ -126,7 +126,7 @@ class DealerSlugMiddleware:
request.path_info.startswith('/en/login/') or \ request.path_info.startswith('/en/login/') or \
request.path_info.startswith('/en/logout/') or \ request.path_info.startswith('/en/logout/') or \
request.path_info.startswith('/en/ledger/') or \ request.path_info.startswith('/en/ledger/') or \
request.path_info.startswith('/en/ledger/') or \ request.path_info.startswith('/ar/ledger/') or \
request.path_info.startswith('/en/notifications/') or \ request.path_info.startswith('/en/notifications/') or \
request.path_info.startswith('/ar/notifications/'): request.path_info.startswith('/ar/notifications/'):
return None return None

View File

@ -1,7 +1,8 @@
from django.http import Http404 from django.http import Http404
from django.shortcuts import redirect from django.shortcuts import get_object_or_404, redirect
from django.utils.translation import get_language from django.utils.translation import get_language
from django_ledger.models import EntityModel
from inventory import models
from inventory.utils import get_user_type from inventory.utils import get_user_type
@ -74,3 +75,12 @@ class DealerSlugMixin:
elif kwargs["dealer_slug"] != request.dealer.slug: elif kwargs["dealer_slug"] != request.dealer.slug:
raise Http404("Dealer slug mismatch") raise Http404("Dealer slug mismatch")
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
class AuthorizedEntityMixin:
def get_authorized_entity_queryset(self):
dealer = get_object_or_404(models.Dealer,slug=self.kwargs["dealer_slug"])
return EntityModel.objects.for_user(
user_model=dealer.entity.admin,
authorized_superuser=self.get_superuser_authorization(),
)

View File

@ -34,6 +34,7 @@ from django_ledger.models import (
EntityManagementModel, EntityManagementModel,
PurchaseOrderModel, PurchaseOrderModel,
ItemTransactionModel, ItemTransactionModel,
BillModel
) )
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -1197,24 +1198,16 @@ class Staff(models.Model, LocalizedNameMixin):
try: try:
self.user.groups.add(group) self.user.groups.add(group)
if "accountant" in group.name.lower() or "manager" in group.name.lower(): if "accountant" in group.name.lower() or "manager" in group.name.lower():
self.add_as_superuser() self.add_superuser_permission()
except Exception as e: except Exception as e:
print(e) print(e)
def add_as_superuser(self): def add_superuser_permission(self):
EntityManagementModel.objects.get_or_create( pass
user=self.user, entity=self.dealer.entity # self.dealer.entity.managers.add(self.user)
)
def remove_superuser_permission(self): def remove_superuser_permission(self):
EntityManagementModel.objects.filter( pass
user=self.user, entity=self.dealer.entity # self.dealer.entity.managers.remove(self.user)
).delete()
# self.user.groups.clear()
# group = Group.objects.filter(
# customgroup__name__iexact=self.staff_type
# ).first()
# if group:
# self.add_group(group)
class Meta: class Meta:
verbose_name = _("Staff") verbose_name = _("Staff")
@ -2552,6 +2545,10 @@ class CustomGroup(models.Model):
pass pass
def set_default_permissions(self): def set_default_permissions(self):
est = ContentType.objects.get_for_model(EstimateModel)
bill = ContentType.objects.get_for_model(BillModel)
Permission.objects.get_or_create(name="Can approve estimate",codename="can_approve_estimate",content_type=est)
Permission.objects.get_or_create(name="Can approve bill",codename="can_approve_bill",content_type=bill)
self.clear_permissions() self.clear_permissions()
if self.name == "Manager": if self.name == "Manager":
self.set_permissions( self.set_permissions(
@ -2591,6 +2588,8 @@ class CustomGroup(models.Model):
"chartofaccountmodel", "chartofaccountmodel",
"customermodel", "customermodel",
"billmodel", "billmodel",
"can_approve_estimate"
"can_approve_bill",
], ],
) )
elif self.name == "Inventory": elif self.name == "Inventory":

View File

@ -994,12 +994,12 @@ urlpatterns = [
# CASH FLOW STATEMENTS... # CASH FLOW STATEMENTS...
# Entities... # Entities...
path( path(
"entity/<slug:entity_slug>/cash-flow-statement/", "<slug:dealer_slug>/entity/<slug:entity_slug>/cash-flow-statement/",
views.BaseCashFlowStatementRedirectViewBase.as_view(), views.BaseCashFlowStatementRedirectViewBase.as_view(),
name="entity-cf", name="entity-cf",
), ),
path( path(
"entity/<slug:entity_slug>/cash-flow-statement/year/<int:year>/", "<slug:dealer_slug>/entity/<slug:entity_slug>/cash-flow-statement/year/<int:year>/",
views.FiscalYearCashFlowStatementViewBase.as_view(), views.FiscalYearCashFlowStatementViewBase.as_view(),
name="entity-cf-year", name="entity-cf-year",
), ),

View File

@ -19,7 +19,7 @@ from pyzbar.pyzbar import decode
from urllib.parse import urlparse, urlunparse from urllib.parse import urlparse, urlunparse
##################################################################### #####################################################################
from inventory.mixins import DealerSlugMixin from inventory.mixins import AuthorizedEntityMixin, DealerSlugMixin
from inventory.models import Status as LeadStatus from inventory.models import Status as LeadStatus
from django.db import IntegrityError from django.db import IntegrityError
from django.views.generic import FormView from django.views.generic import FormView
@ -2685,7 +2685,7 @@ class GroupCreateView(
group_manager, _ = models.CustomGroup.objects.get_or_create( group_manager, _ = models.CustomGroup.objects.get_or_create(
name=instance.name, dealer=dealer, group=group name=instance.name, dealer=dealer, group=group
) )
group_manager.set_default_permissions() # group_manager.set_default_permissions()
dealer.user.groups.add(group) dealer.user.groups.add(group)
return super().form_valid(form) return super().form_valid(form)
@ -7438,7 +7438,7 @@ class BaseBalanceSheetRedirectView(RedirectView):
class FiscalYearBalanceSheetViewBase( class FiscalYearBalanceSheetViewBase(
FiscalYearBalanceSheetView, DjangoLedgerSecurityMixIn FiscalYearBalanceSheetView
): ):
""" """
Defines a base view for the fiscal year balance sheet. Defines a base view for the fiscal year balance sheet.
@ -7465,7 +7465,7 @@ class FiscalYearBalanceSheetViewBase(
class QuarterlyBalanceSheetView( class QuarterlyBalanceSheetView(
FiscalYearBalanceSheetViewBase, QuarterlyReportMixIn, DjangoLedgerSecurityMixIn FiscalYearBalanceSheetViewBase, QuarterlyReportMixIn
): ):
""" """
Represents a quarterly balance sheet view. Represents a quarterly balance sheet view.
@ -7488,7 +7488,7 @@ class QuarterlyBalanceSheetView(
class MonthlyBalanceSheetView( class MonthlyBalanceSheetView(
FiscalYearBalanceSheetViewBase, MonthlyReportMixIn, DjangoLedgerSecurityMixIn FiscalYearBalanceSheetViewBase, MonthlyReportMixIn,
): ):
""" """
Represents the view for the monthly balance sheet. Represents the view for the monthly balance sheet.
@ -7511,7 +7511,7 @@ class MonthlyBalanceSheetView(
class DateBalanceSheetView( class DateBalanceSheetView(
FiscalYearBalanceSheetViewBase, DateReportMixIn, DjangoLedgerSecurityMixIn FiscalYearBalanceSheetViewBase, DateReportMixIn,
): ):
""" """
Represents a balance sheet view for a specific date. Represents a balance sheet view for a specific date.
@ -7541,7 +7541,7 @@ class DateBalanceSheetView(
class BaseIncomeStatementRedirectViewBase( class BaseIncomeStatementRedirectViewBase(
BaseIncomeStatementRedirectView, DjangoLedgerSecurityMixIn BaseIncomeStatementRedirectView
): ):
""" """
The BaseIncomeStatementRedirectViewBase class provides functionality for handling The BaseIncomeStatementRedirectViewBase class provides functionality for handling
@ -7570,7 +7570,7 @@ class BaseIncomeStatementRedirectViewBase(
class FiscalYearIncomeStatementViewBase( class FiscalYearIncomeStatementViewBase(
FiscalYearIncomeStatementView, DjangoLedgerSecurityMixIn FiscalYearIncomeStatementView
): ):
""" """
Represents a base view for fiscal year income statement. Represents a base view for fiscal year income statement.
@ -7595,7 +7595,7 @@ class FiscalYearIncomeStatementViewBase(
class QuarterlyIncomeStatementView( class QuarterlyIncomeStatementView(
FiscalYearIncomeStatementViewBase, QuarterlyReportMixIn, DjangoLedgerSecurityMixIn FiscalYearIncomeStatementViewBase, QuarterlyReportMixIn
): ):
""" """
Represents a detailed view for a quarterly income statement. Represents a detailed view for a quarterly income statement.
@ -7624,7 +7624,7 @@ class QuarterlyIncomeStatementView(
class MonthlyIncomeStatementView( class MonthlyIncomeStatementView(
FiscalYearIncomeStatementViewBase, MonthlyReportMixIn, DjangoLedgerSecurityMixIn FiscalYearIncomeStatementViewBase, MonthlyReportMixIn,
): ):
""" """
Represents the view for a monthly income statement in the financial Represents the view for a monthly income statement in the financial
@ -7650,7 +7650,7 @@ class MonthlyIncomeStatementView(
class DateModelIncomeStatementView( class DateModelIncomeStatementView(
FiscalYearIncomeStatementViewBase, DateReportMixIn, DjangoLedgerSecurityMixIn FiscalYearIncomeStatementViewBase, DateReportMixIn,
): ):
""" """
Represents a detailed view of an income statement for a fiscal year with additional Represents a detailed view of an income statement for a fiscal year with additional
@ -7675,7 +7675,7 @@ class DateModelIncomeStatementView(
class BaseCashFlowStatementRedirectViewBase( class BaseCashFlowStatementRedirectViewBase(
BaseCashFlowStatementRedirectView, DjangoLedgerSecurityMixIn AuthorizedEntityMixin,BaseCashFlowStatementRedirectView
): ):
""" """
Base class for handling cash flow statement redirection views. Base class for handling cash flow statement redirection views.
@ -7690,10 +7690,10 @@ class BaseCashFlowStatementRedirectViewBase(
""" """
def get_redirect_url(self, *args, **kwargs): def get_redirect_url(self, *args, **kwargs):
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
year = get_localdate().year year = get_localdate().year
dealer = get_user_type(self.request)
return reverse( return reverse(
"entity-cf-year", kwargs={"entity_slug": dealer.entity.slug, "year": year} "entity-cf-year", kwargs={"dealer_slug": dealer.slug,"entity_slug": dealer.entity.slug, "year": year}
) )
def get_login_url(self): def get_login_url(self):
@ -7701,7 +7701,7 @@ class BaseCashFlowStatementRedirectViewBase(
class FiscalYearCashFlowStatementViewBase( class FiscalYearCashFlowStatementViewBase(
FiscalYearCashFlowStatementView, DjangoLedgerSecurityMixIn AuthorizedEntityMixin,FiscalYearCashFlowStatementView
): ):
""" """
Represents a base view for fiscal year cash flow statements. Represents a base view for fiscal year cash flow statements.
@ -7726,8 +7726,11 @@ class FiscalYearCashFlowStatementViewBase(
return reverse("account_login") return reverse("account_login")
class QuarterlyCashFlowStatementView( class QuarterlyCashFlowStatementView(
FiscalYearCashFlowStatementViewBase, QuarterlyReportMixIn, DjangoLedgerSecurityMixIn FiscalYearCashFlowStatementViewBase, QuarterlyReportMixIn,
): ):
""" """
Represents a view model for quarterly cash flow statements. Represents a view model for quarterly cash flow statements.
@ -7755,7 +7758,7 @@ class QuarterlyCashFlowStatementView(
class MonthlyCashFlowStatementView( class MonthlyCashFlowStatementView(
FiscalYearCashFlowStatementViewBase, MonthlyReportMixIn, DjangoLedgerSecurityMixIn FiscalYearCashFlowStatementViewBase, MonthlyReportMixIn,
): ):
""" """
Represents a view for monthly cash flow statements. Represents a view for monthly cash flow statements.
@ -7774,7 +7777,7 @@ class MonthlyCashFlowStatementView(
class DateCashFlowStatementView( class DateCashFlowStatementView(
FiscalYearCashFlowStatementViewBase, DateReportMixIn, DjangoLedgerSecurityMixIn FiscalYearCashFlowStatementViewBase, DateReportMixIn,
): ):
""" """
Representation of a detailed view for a cash flow statement associated with a specific fiscal year, Representation of a detailed view for a cash flow statement associated with a specific fiscal year,
@ -7793,7 +7796,7 @@ class DateCashFlowStatementView(
class EntityModelDetailHandlerViewBase( class EntityModelDetailHandlerViewBase(
EntityModelDetailHandlerView, DjangoLedgerSecurityMixIn EntityModelDetailHandlerView,
): ):
""" """
Handles detailed views for Entity Models along with redirection logic Handles detailed views for Entity Models along with redirection logic
@ -7835,7 +7838,7 @@ class EntityModelDetailHandlerViewBase(
class EntityModelDetailBaseViewBase( class EntityModelDetailBaseViewBase(
EntityModelDetailBaseView, DjangoLedgerSecurityMixIn EntityModelDetailBaseView,
): ):
""" """
Represents a base view that extends functionality for displaying detailed Represents a base view that extends functionality for displaying detailed
@ -7892,7 +7895,7 @@ class EntityModelDetailBaseViewBase(
class FiscalYearEntityModelDashboardView( class FiscalYearEntityModelDashboardView(
EntityModelDetailBaseViewBase, DjangoLedgerSecurityMixIn EntityModelDetailBaseViewBase,
): ):
""" """
Represents a dashboard view for fiscal year entity models. Represents a dashboard view for fiscal year entity models.
@ -7913,7 +7916,7 @@ class FiscalYearEntityModelDashboardView(
class QuarterlyEntityDashboardView( class QuarterlyEntityDashboardView(
FiscalYearEntityModelDashboardView, QuarterlyReportMixIn, DjangoLedgerSecurityMixIn FiscalYearEntityModelDashboardView, QuarterlyReportMixIn,
): ):
""" """
Represents a dashboard view for quarterly entities. Represents a dashboard view for quarterly entities.
@ -7937,7 +7940,7 @@ class QuarterlyEntityDashboardView(
class MonthlyEntityDashboardView( class MonthlyEntityDashboardView(
FiscalYearEntityModelDashboardView, MonthlyReportMixIn, DjangoLedgerSecurityMixIn FiscalYearEntityModelDashboardView, MonthlyReportMixIn,
): ):
""" """
Represents a dashboard view for a specific entity's monthly report. Represents a dashboard view for a specific entity's monthly report.
@ -7966,7 +7969,7 @@ class MonthlyEntityDashboardView(
class DateEntityDashboardView( class DateEntityDashboardView(
FiscalYearEntityModelDashboardView, DateReportMixIn, DjangoLedgerSecurityMixIn FiscalYearEntityModelDashboardView, DateReportMixIn,
): ):
""" """
Represents a dashboard view for date-based entity data visualization. Represents a dashboard view for date-based entity data visualization.
@ -7986,7 +7989,7 @@ class DateEntityDashboardView(
""" """
class PayableNetAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View): class PayableNetAPIView(EntityUnitMixIn, View):
""" """
Handles the retrieval of net payable data for authenticated users. Handles the retrieval of net payable data for authenticated users.
@ -8871,22 +8874,23 @@ def sse_stream(request):
last_id = request.GET.get("last_id", 0) last_id = request.GET.get("last_id", 0)
while True: while True:
# Check for new notifications # Check for new notifications
notifications = models.Notification.objects.filter( if request.user.is_authenticated:
user=request.user, id__gt=last_id, is_read=False notifications = models.Notification.objects.filter(
).order_by("created") user=request.user, id__gt=last_id, is_read=False
for notification in notifications: ).order_by("created")
notification_data = { for notification in notifications:
"id": notification.id, notification_data = {
"message": notification.message, "id": notification.id,
"created": notification.created.isoformat(), "message": notification.message,
} "created": notification.created.isoformat(),
}
yield ( yield (
f"id: {notification.id}\n" f"id: {notification.id}\n"
f"event: notification\n" f"event: notification\n"
f"data: {json.dumps(notification_data)}\n\n" f"data: {json.dumps(notification_data)}\n\n"
) )
last_id = notification.id last_id = notification.id
sleep(2) sleep(2)

View File

@ -219,7 +219,7 @@
</button> </button>
{% endif %} {% endif %}
<!-- Mark as Approved --> <!-- Mark as Approved -->
{% if bill.can_approve %} {% if bill.can_approve and perms.django_ledger.can_approve_bill %}
<button class="btn btn-phoenix-success" <button class="btn btn-phoenix-success"
onclick="showPOModal('Mark as Approved', '{% url 'bill-action-mark-as-approved' dealer_slug=request.dealer.slug entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Approved')"> onclick="showPOModal('Mark as Approved', '{% url 'bill-action-mark-as-approved' dealer_slug=request.dealer.slug entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Approved')">
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Approved' %} <i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Approved' %}

View File

@ -297,10 +297,8 @@
{% comment %} <i class="fa-solid fa-chart-line"></i><span class="nav-link-text">{% trans 'Dashboard'|capfirst %}</span> {% endcomment %} {% comment %} <i class="fa-solid fa-chart-line"></i><span class="nav-link-text">{% trans 'Dashboard'|capfirst %}</span> {% endcomment %}
</div> </div>
</a> </a>
{% if request.user.is_authenticated and request.is_dealer %} {% if request.user.is_authenticated %}
<a class="nav-link" href="{% url 'entity-cf' request.user.dealer.entity.slug %}"> <a class="nav-link" href="{% url 'entity-cf' request.dealer.slug request.dealer.entity.slug %}">
{% elif request.user.is_authenticated and request.is_staff %}
<a class="nav-link" href="{% url 'entity-cf' request.user.staffmember.staff.dealer.entity.slug %}">
{% else %} {% else %}
<a class="nav-link" href="#"> <a class="nav-link" href="#">
{% endif %} {% endif %}