fix the notificatin and notification counter
This commit is contained in:
parent
473d7e1990
commit
73ae641ddb
@ -1,21 +1,7 @@
|
|||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.contrib.auth import get_user_model
|
from inventory.tasks import long_running_task
|
||||||
from inventory.tasks import create_coa_accounts
|
from django_q.tasks import async_task
|
||||||
from inventory.models import Dealer
|
|
||||||
|
|
||||||
User = get_user_model()
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
def handle(self, *args, **kwargs):
|
def handle(self, *args, **kwargs):
|
||||||
# user = User.objects.last()
|
async_task(long_running_task, 20)
|
||||||
# print(user.email)
|
|
||||||
# # 2. Force email confirmation
|
|
||||||
# # email = user.emailaddress_set.first()
|
|
||||||
# confirmation = EmailConfirmation.create(user.email)
|
|
||||||
# confirmation.send()
|
|
||||||
|
|
||||||
# result = re.match(r'^05\d{8}$', '0625252522')
|
|
||||||
# print(result)
|
|
||||||
dealer = Dealer.objects.last()
|
|
||||||
create_coa_accounts(dealer.pk)
|
|
||||||
@ -126,7 +126,9 @@ 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('/ar/ledger/'):
|
request.path_info.startswith('/en/ledger/') or \
|
||||||
|
request.path_info.startswith('/en/notifications/') or \
|
||||||
|
request.path_info.startswith('/ar/notifications/'):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
@ -141,6 +143,7 @@ class DealerSlugMiddleware:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if dealer_slug.lower() != request.dealer.slug.lower():
|
if dealer_slug.lower() != request.dealer.slug.lower():
|
||||||
|
print(dealer_slug)
|
||||||
logger.warning(f"Dealer slug mismatch: {dealer_slug} != {request.dealer.slug}")
|
logger.warning(f"Dealer slug mismatch: {dealer_slug} != {request.dealer.slug}")
|
||||||
raise Http404("Dealer slug mismatch")
|
raise Http404("Dealer slug mismatch")
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ from datetime import datetime
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
@ -600,6 +601,8 @@ class Car(Base):
|
|||||||
)
|
)
|
||||||
# history = HistoricalRecords()
|
# history = HistoricalRecords()
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse("car_detail", kwargs={"dealer_slug": self.dealer.slug,"slug": self.slug})
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.slug = slugify(self.vin)
|
self.slug = slugify(self.vin)
|
||||||
self.hash = self.get_hash
|
self.hash = self.get_hash
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from django.urls import reverse
|
||||||
from inventory.tasks import create_coa_accounts, create_make_accounts
|
from inventory.tasks import create_coa_accounts, create_make_accounts
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.db.models.signals import post_save, post_delete
|
from django.db.models.signals import post_save, post_delete
|
||||||
@ -18,7 +20,7 @@ from django_ledger.models import (
|
|||||||
from . import models
|
from . import models
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django_q.tasks import async_task
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
@ -143,7 +145,7 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
entity.create_uom(name=u[1], unit_abbr=u[0])
|
entity.create_uom(name=u[1], unit_abbr=u[0])
|
||||||
|
|
||||||
# Create COA accounts, background task
|
# Create COA accounts, background task
|
||||||
create_coa_accounts(instance.pk)
|
async_task(create_coa_accounts,instance.pk)
|
||||||
|
|
||||||
# create_settings(instance.pk)
|
# create_settings(instance.pk)
|
||||||
# create_accounts_for_make(instance.pk)
|
# create_accounts_for_make(instance.pk)
|
||||||
@ -164,12 +166,12 @@ def create_dealer_groups(sender, instance, created, **kwargs):
|
|||||||
:param kwargs: Additional keyword arguments passed by the signal.
|
:param kwargs: Additional keyword arguments passed by the signal.
|
||||||
:type kwargs: dict
|
:type kwargs: dict
|
||||||
"""
|
"""
|
||||||
group_names = ["Inventory", "Accountant", "Sales"]
|
group_names = ["Inventory", "Accountant", "Sales","Manager"]
|
||||||
|
|
||||||
def create_groups():
|
def create_groups():
|
||||||
for group_name in group_names:
|
for group_name in group_names:
|
||||||
group, created = Group.objects.get_or_create(
|
group, created = Group.objects.get_or_create(
|
||||||
name=f"{instance.pk}_{group_name}"
|
name=f"{instance.slug}_{group_name}"
|
||||||
)
|
)
|
||||||
group_manager, created = models.CustomGroup.objects.get_or_create(
|
group_manager, created = models.CustomGroup.objects.get_or_create(
|
||||||
name=group_name, dealer=instance, group=group
|
name=group_name, dealer=instance, group=group
|
||||||
@ -896,7 +898,26 @@ def update_finance_cost(sender, instance, created, **kwargs):
|
|||||||
|
|
||||||
@receiver(post_save, sender=PurchaseOrderModel)
|
@receiver(post_save, sender=PurchaseOrderModel)
|
||||||
def create_po_item_upload(sender,instance,created,**kwargs):
|
def create_po_item_upload(sender,instance,created,**kwargs):
|
||||||
if instance.po_status == "fulfilled":
|
if instance.po_status == "fulfilled":
|
||||||
for item in instance.get_itemtxs_data()[0]:
|
for item in instance.get_itemtxs_data()[0]:
|
||||||
dealer = models.Dealer.objects.get(entity=instance.entity)
|
dealer = models.Dealer.objects.get(entity=instance.entity)
|
||||||
models.PoItemsUploaded.objects.create(dealer=dealer,po=instance, item=item, status="fulfilled")
|
models.PoItemsUploaded.objects.create(dealer=dealer,po=instance, item=item, status="fulfilled")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################
|
||||||
|
######################Notification########################
|
||||||
|
##########################################################
|
||||||
|
|
||||||
|
@receiver(post_save, sender=models.Car)
|
||||||
|
def car_created_notification(sender, instance, created, **kwargs):
|
||||||
|
if created:
|
||||||
|
accountants = models.CustomGroup.objects.filter(dealer=instance.dealer,name="Accountant").first().group.user_set.exclude(email=instance.dealer.user.email)
|
||||||
|
for accountant in accountants:
|
||||||
|
models.Notification.objects.create(
|
||||||
|
user=accountant,
|
||||||
|
message=f"""
|
||||||
|
New Car {instance.vin} has been added to dealer {instance.dealer.name}.
|
||||||
|
<a href="{instance.get_absolute_url()}" target="_blank">View</a>
|
||||||
|
""",
|
||||||
|
)
|
||||||
@ -1,13 +1,11 @@
|
|||||||
from datetime import datetime
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django_ledger.io import roles
|
from django_ledger.io import roles
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from background_task import background
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from inventory.models import DealerSettings, Dealer
|
from inventory.models import DealerSettings, Dealer
|
||||||
|
from django_q.tasks import async_task
|
||||||
|
|
||||||
|
|
||||||
# @background
|
|
||||||
def create_settings(pk):
|
def create_settings(pk):
|
||||||
instance = Dealer.objects.get(pk=pk)
|
instance = Dealer.objects.get(pk=pk)
|
||||||
|
|
||||||
@ -34,7 +32,6 @@ def create_settings(pk):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@background
|
|
||||||
def create_coa_accounts(pk):
|
def create_coa_accounts(pk):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
instance = Dealer.objects.select_for_update().get(pk=pk)
|
instance = Dealer.objects.select_for_update().get(pk=pk)
|
||||||
@ -772,8 +769,6 @@ def create_coa_accounts(pk):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
@background
|
|
||||||
def create_coa_accounts1(pk):
|
def create_coa_accounts1(pk):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
instance = Dealer.objects.select_for_update().get(pk=pk)
|
instance = Dealer.objects.select_for_update().get(pk=pk)
|
||||||
@ -1456,25 +1451,16 @@ def create_make_accounts(entity, coa, makes, name, role, balance_type):
|
|||||||
)
|
)
|
||||||
return acc
|
return acc
|
||||||
|
|
||||||
|
|
||||||
@background
|
|
||||||
def send_email(from_, to_, subject, message):
|
def send_email(from_, to_, subject, message):
|
||||||
subject = subject
|
subject = subject
|
||||||
message = message
|
message = message
|
||||||
from_email = from_
|
from_email = from_
|
||||||
recipient_list = [to_]
|
recipient_list = [to_]
|
||||||
send_mail(subject, message, from_email, recipient_list)
|
async_task(send_mail,subject, message, from_email, recipient_list)
|
||||||
|
|
||||||
|
|
||||||
@background
|
# @background
|
||||||
def long_running_task(task_id, *args, **kwargs):
|
def long_running_task(duration):
|
||||||
"""Example background task"""
|
"""Example background task"""
|
||||||
print(f"Starting task {task_id} with args: {args}, kwargs: {kwargs}")
|
print("Task completed")
|
||||||
|
return True
|
||||||
# Simulate work
|
|
||||||
for i in range(5):
|
|
||||||
print(f"Task {task_id} progress: {i + 1}/5")
|
|
||||||
|
|
||||||
result = f"Task {task_id} completed at {datetime.now()}"
|
|
||||||
print(result)
|
|
||||||
return result
|
|
||||||
|
|||||||
@ -644,4 +644,4 @@ def inventory_table(context, queryset):
|
|||||||
"inventory_list": queryset,
|
"inventory_list": queryset,
|
||||||
}
|
}
|
||||||
ctx.update(queryset.aggregate(inventory_total_value=Sum("total_value")))
|
ctx.update(queryset.aggregate(inventory_total_value=Sum("total_value")))
|
||||||
return ctx
|
return ctx
|
||||||
@ -14,7 +14,6 @@ urlpatterns = [
|
|||||||
path("<slug:dealer_slug>/", views.HomeView.as_view(), name="home"),
|
path("<slug:dealer_slug>/", views.HomeView.as_view(), name="home"),
|
||||||
|
|
||||||
# Tasks
|
# Tasks
|
||||||
path("<slug:dealer_slug>/tasks/", views.task_list, name="task_list"),
|
|
||||||
path("legal/", views.terms_and_privacy, name="terms_and_privacy"),
|
path("legal/", views.terms_and_privacy, name="terms_and_privacy"),
|
||||||
# path('tasks/<int:task_id>/detail/', views.task_detail, name='task_detail'),
|
# path('tasks/<int:task_id>/detail/', views.task_detail, name='task_detail'),
|
||||||
# Dashboards
|
# Dashboards
|
||||||
@ -203,38 +202,29 @@ urlpatterns = [
|
|||||||
),
|
),
|
||||||
# path('crm/opportunities/<int:pk>/logs/', views.OpportunityLogsView.as_view(), name='opportunity_logs'),
|
# path('crm/opportunities/<int:pk>/logs/', views.OpportunityLogsView.as_view(), name='opportunity_logs'),
|
||||||
# #######################
|
# #######################
|
||||||
path("stream/", views.sse_stream, name="sse_stream"),
|
|
||||||
path("fetch/", views.fetch_notifications, name="fetch_notifications"),
|
|
||||||
# Mark single notification as read
|
|
||||||
path(
|
|
||||||
"<int:notification_id>/mark-read/",
|
|
||||||
views.mark_notification_as_read,
|
|
||||||
name="mark_notification_as_read",
|
|
||||||
),
|
|
||||||
# Mark all notifications as read
|
|
||||||
path(
|
|
||||||
"mark-all-read/",
|
|
||||||
views.mark_all_notifications_as_read,
|
|
||||||
name="mark_all_notifications_as_read",
|
|
||||||
),
|
|
||||||
# Notification history
|
|
||||||
path("history/", views.notifications_history, name="notifications_history"),
|
|
||||||
# #######################
|
# #######################
|
||||||
|
# Notifications
|
||||||
|
path("notifications/stream/", views.sse_stream, name="sse_stream"),
|
||||||
|
path("notifications/fetch/", views.fetch_notifications, name="fetch_notifications"),
|
||||||
|
|
||||||
path(
|
path(
|
||||||
"crm/notifications/",
|
"/notifications/",
|
||||||
views.NotificationListView.as_view(),
|
views.NotificationListView.as_view(),
|
||||||
name="notifications_history",
|
name="notifications_history",
|
||||||
),
|
),
|
||||||
|
|
||||||
path(
|
path(
|
||||||
"crm/fetch_notifications/",
|
"notifications/<int:notification_id>/mark_as_read/",
|
||||||
views.fetch_notifications,
|
|
||||||
name="fetch_notifications",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"crm/notifications/<int:notification_id>/mark_as_read/",
|
|
||||||
views.mark_notification_as_read,
|
views.mark_notification_as_read,
|
||||||
name="mark_notification_as_read",
|
name="mark_notification_as_read",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"notifications/mark_all_notifications_as_read/",
|
||||||
|
views.mark_all_notifications_as_read,
|
||||||
|
name="mark_all_notifications_as_read",
|
||||||
|
),
|
||||||
|
# #######################
|
||||||
|
# #######################
|
||||||
path("crm/calender/", views.EmployeeCalendarView.as_view(), name="calendar_list"),
|
path("crm/calender/", views.EmployeeCalendarView.as_view(), name="calendar_list"),
|
||||||
#######################################################
|
#######################################################
|
||||||
# Vendor URLs
|
# Vendor URLs
|
||||||
|
|||||||
@ -26,7 +26,7 @@ from django_ledger.models import (
|
|||||||
from django.utils.translation import get_language
|
from django.utils.translation import get_language
|
||||||
from appointment.models import StaffMember
|
from appointment.models import StaffMember
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django_q.tasks import async_task
|
||||||
import secrets
|
import secrets
|
||||||
|
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ def send_email(from_, to_, subject, message):
|
|||||||
message = message
|
message = message
|
||||||
from_email = from_
|
from_email = from_
|
||||||
recipient_list = [to_]
|
recipient_list = [to_]
|
||||||
send_mail(subject, message, from_email, recipient_list)
|
async_task(send_mail,subject, message, from_email, recipient_list)
|
||||||
|
|
||||||
|
|
||||||
def get_user_type(request):
|
def get_user_type(request):
|
||||||
|
|||||||
@ -23,7 +23,6 @@ from urllib.parse import urlparse, urlunparse
|
|||||||
from inventory.mixins import DealerSlugMixin
|
from inventory.mixins import 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 background_task.models import Task
|
|
||||||
from django.views.generic import FormView
|
from django.views.generic import FormView
|
||||||
from django.views.decorators.http import require_http_methods
|
from django.views.decorators.http import require_http_methods
|
||||||
from django.db.models.deletion import RestrictedError
|
from django.db.models.deletion import RestrictedError
|
||||||
@ -2670,9 +2669,17 @@ class GroupCreateView(
|
|||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
instance = form.save(commit=False)
|
instance = form.save(commit=False)
|
||||||
group = Group.objects.create(name=f"{dealer.slug}_{instance.name}")
|
group_name = f"{dealer.slug}_{instance.name}"
|
||||||
instance.dealer = dealer
|
group,created = Group.objects.get_or_create(name=group_name)
|
||||||
instance.group = group
|
if created:
|
||||||
|
group_manager, created = models.CustomGroup.objects.get_or_create(
|
||||||
|
name=group_name, dealer=dealer, group=group
|
||||||
|
)
|
||||||
|
group_manager.set_default_permissions()
|
||||||
|
dealer.user.groups.add(group)
|
||||||
|
else:
|
||||||
|
instance.dealer = dealer
|
||||||
|
instance.group = group
|
||||||
instance.save()
|
instance.save()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
@ -2951,10 +2958,10 @@ class UserCreateView(
|
|||||||
staff.staff_member = staff_member
|
staff.staff_member = staff_member
|
||||||
staff.dealer = dealer
|
staff.dealer = dealer
|
||||||
staff.add_as_manager()
|
staff.add_as_manager()
|
||||||
group = Group.objects.filter(customgroup__name__iexact=staff.staff_type).first()
|
group = models.CustomGroup.objects.filter(dealer=dealer,name__iexact=staff.staff_type).first()
|
||||||
staff.save()
|
staff.save()
|
||||||
if group:
|
if group:
|
||||||
staff.add_group(group)
|
staff.add_group(group.group)
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy("user_list", args=[self.request.dealer.slug])
|
return reverse_lazy("user_list", args=[self.request.dealer.slug])
|
||||||
@ -8777,20 +8784,8 @@ def payment_callback(request,dealer_slug):
|
|||||||
return render(request, "payment_failed.html", {"message": message})
|
return render(request, "payment_failed.html", {"message": message})
|
||||||
|
|
||||||
|
|
||||||
# Background Tasks
|
|
||||||
def task_list(request):
|
|
||||||
# Get all tasks ordered by creation time
|
|
||||||
tasks = Task.objects.all()
|
|
||||||
|
|
||||||
# Add pagination
|
|
||||||
paginator = Paginator(tasks, 10) # Show 10 tasks per page
|
|
||||||
page_number = request.GET.get("page")
|
|
||||||
page_obj = paginator.get_page(page_number)
|
|
||||||
|
|
||||||
return render(request, "tasks/task_list.html", {"page_obj": page_obj})
|
|
||||||
|
|
||||||
|
|
||||||
def sse_stream(request):
|
def sse_stream(request):
|
||||||
|
print("hi")
|
||||||
def event_stream():
|
def event_stream():
|
||||||
last_id = request.GET.get("last_id", 0)
|
last_id = request.GET.get("last_id", 0)
|
||||||
while True:
|
while True:
|
||||||
@ -8833,7 +8828,7 @@ def mark_notification_as_read(request, notification_id):
|
|||||||
notification = get_object_or_404(
|
notification = get_object_or_404(
|
||||||
models.Notification, id=notification_id, user=request.user
|
models.Notification, id=notification_id, user=request.user
|
||||||
)
|
)
|
||||||
notification.read = True
|
notification.is_read = True
|
||||||
notification.save()
|
notification.save()
|
||||||
return JsonResponse({"status": "success"})
|
return JsonResponse({"status": "success"})
|
||||||
|
|
||||||
@ -8841,9 +8836,9 @@ def mark_notification_as_read(request, notification_id):
|
|||||||
@login_required
|
@login_required
|
||||||
def mark_all_notifications_as_read(request):
|
def mark_all_notifications_as_read(request):
|
||||||
models.Notification.objects.filter(user=request.user, is_read=False).update(
|
models.Notification.objects.filter(user=request.user, is_read=False).update(
|
||||||
read=True
|
is_read=True
|
||||||
)
|
)
|
||||||
return JsonResponse({"status": "success"})
|
return redirect(request.META.get("HTTP_REFERER"))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -9669,7 +9664,7 @@ def upload_cars(request, dealer_slug, pk=None):
|
|||||||
|
|
||||||
if not csv_file.name.endswith(".csv"):
|
if not csv_file.name.endswith(".csv"):
|
||||||
messages.error(request, "Please upload a CSV file")
|
messages.error(request, "Please upload a CSV file")
|
||||||
return redirect("upload_cars", dealer_slug=dealer_slug)
|
return response
|
||||||
try:
|
try:
|
||||||
# Read the file content
|
# Read the file content
|
||||||
file_content = csv_file.read().decode("utf-8")
|
file_content = csv_file.read().decode("utf-8")
|
||||||
@ -9715,10 +9710,16 @@ def upload_cars(request, dealer_slug, pk=None):
|
|||||||
po_item.save()
|
po_item.save()
|
||||||
|
|
||||||
messages.success(request, f"Successfully imported {cars_created} cars")
|
messages.success(request, f"Successfully imported {cars_created} cars")
|
||||||
return response
|
return redirect(
|
||||||
|
"view_items_inventory",
|
||||||
|
dealer_slug=dealer_slug,
|
||||||
|
slug_entity=dealer.entity.slug,
|
||||||
|
po_pk=item.po_model.pk,)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
messages.error(request, f"Error processing CSV: {str(e)}")
|
messages.error(request, f"Error processing CSV: {str(e)}")
|
||||||
|
return response
|
||||||
|
|
||||||
form = forms.CSVUploadForm()
|
form = forms.CSVUploadForm()
|
||||||
form.fields["vendor"].queryset = dealer.vendors.all()
|
form.fields["vendor"].queryset = dealer.vendors.all()
|
||||||
|
|
||||||
|
|||||||
@ -4,18 +4,21 @@ arrow==1.3.0
|
|||||||
asgiref==3.8.1
|
asgiref==3.8.1
|
||||||
attrs==25.3.0
|
attrs==25.3.0
|
||||||
Babel==2.15.0
|
Babel==2.15.0
|
||||||
|
beautifulsoup4==4.13.4
|
||||||
blessed==1.21.0
|
blessed==1.21.0
|
||||||
cattrs==24.1.3
|
cattrs==24.1.3
|
||||||
certifi==2025.1.31
|
certifi==2025.1.31
|
||||||
cffi==1.17.1
|
cffi==1.17.1
|
||||||
charset-normalizer==3.4.1
|
charset-normalizer==3.4.1
|
||||||
|
click==8.2.1
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
crispy-bootstrap5==2024.10
|
crispy-bootstrap5==2024.10
|
||||||
cryptography==44.0.2
|
cryptography==44.0.2
|
||||||
|
cssbeautifier==1.15.4
|
||||||
defusedxml==0.7.1
|
defusedxml==0.7.1
|
||||||
diff-match-patch==20241021
|
diff-match-patch==20241021
|
||||||
distro==1.9.0
|
distro==1.9.0
|
||||||
Django==5.1.7
|
Django==5.2.3
|
||||||
django-allauth==65.6.0
|
django-allauth==65.6.0
|
||||||
django-appointment==3.8.0
|
django-appointment==3.8.0
|
||||||
django-background-tasks==1.2.8
|
django-background-tasks==1.2.8
|
||||||
@ -24,17 +27,20 @@ django-ckeditor==6.7.2
|
|||||||
django-cors-headers==4.7.0
|
django-cors-headers==4.7.0
|
||||||
django-countries==7.6.1
|
django-countries==7.6.1
|
||||||
django-crispy-forms==2.3
|
django-crispy-forms==2.3
|
||||||
|
django-easy-audit==1.3.7
|
||||||
django-extensions==3.2.3
|
django-extensions==3.2.3
|
||||||
django-filter==25.1
|
django-filter==25.1
|
||||||
django-import-export==4.3.7
|
django-import-export==4.3.7
|
||||||
django-js-asset==3.1.2
|
django-js-asset==3.1.2
|
||||||
django-ledger==0.7.6.1
|
django-ledger==0.7.7
|
||||||
|
django-manager-utils==3.1.5
|
||||||
django-next-url-mixin==0.4.0
|
django-next-url-mixin==0.4.0
|
||||||
django-ordered-model==3.7.4
|
django-ordered-model==3.7.4
|
||||||
django-phonenumber-field==8.0.0
|
django-phonenumber-field==8.0.0
|
||||||
django-picklefield==3.3
|
django-picklefield==3.3
|
||||||
django-plans==2.0.0
|
django-plans==2.0.0
|
||||||
django-q==1.3.9
|
django-q2==1.8.0
|
||||||
|
django-query-builder==3.2.0
|
||||||
django-schema-graph==3.1.0
|
django-schema-graph==3.1.0
|
||||||
django-sequences==3.0
|
django-sequences==3.0
|
||||||
django-tables2==2.7.5
|
django-tables2==2.7.5
|
||||||
@ -42,29 +48,47 @@ django-treebeard==4.7.1
|
|||||||
django-widget-tweaks==1.5.0
|
django-widget-tweaks==1.5.0
|
||||||
djangorestframework==3.15.2
|
djangorestframework==3.15.2
|
||||||
djhtml==3.0.7
|
djhtml==3.0.7
|
||||||
|
djlint==1.36.4
|
||||||
docopt==0.6.2
|
docopt==0.6.2
|
||||||
Faker==37.1.0
|
EditorConfig==0.17.0
|
||||||
|
Faker==37.3.0
|
||||||
|
fleming==0.7.0
|
||||||
fonttools==4.57.0
|
fonttools==4.57.0
|
||||||
fpdf==1.7.2
|
fpdf==1.7.2
|
||||||
fpdf2==2.8.3
|
fpdf2==2.8.3
|
||||||
|
greenlet==3.2.2
|
||||||
h11==0.14.0
|
h11==0.14.0
|
||||||
httpcore==1.0.7
|
httpcore==1.0.7
|
||||||
httpx==0.28.1
|
httpx==0.28.1
|
||||||
icalendar==6.1.2
|
icalendar==6.1.2
|
||||||
idna==3.10
|
idna==3.10
|
||||||
jiter==0.9.0
|
jiter==0.9.0
|
||||||
|
jsbeautifier==1.15.4
|
||||||
|
json5==0.12.0
|
||||||
|
jsonpatch==1.33
|
||||||
|
jsonpointer==3.0.0
|
||||||
jwt==1.3.1
|
jwt==1.3.1
|
||||||
|
langchain==0.3.25
|
||||||
|
langchain-core==0.3.61
|
||||||
|
langchain-ollama==0.3.3
|
||||||
|
langchain-text-splitters==0.3.8
|
||||||
|
langsmith==0.3.42
|
||||||
luhnchecker==0.0.12
|
luhnchecker==0.0.12
|
||||||
Markdown==3.7
|
Markdown==3.8
|
||||||
markdown-it-py==3.0.0
|
markdown-it-py==3.0.0
|
||||||
mdurl==0.1.2
|
mdurl==0.1.2
|
||||||
num2words==0.5.14
|
num2words==0.5.14
|
||||||
numpy==2.2.4
|
numpy==2.2.4
|
||||||
ofxtools==0.9.5
|
ofxtools==0.9.5
|
||||||
|
ollama==0.4.8
|
||||||
openai==1.68.2
|
openai==1.68.2
|
||||||
opencv-python==4.11.0.86
|
opencv-python==4.11.0.86
|
||||||
|
orjson==3.10.18
|
||||||
|
packaging==24.2
|
||||||
|
pandas==2.2.3
|
||||||
|
pathspec==0.12.1
|
||||||
phonenumbers==8.13.42
|
phonenumbers==8.13.42
|
||||||
pillow==10.4.0
|
pillow==11.2.1
|
||||||
pycparser==2.22
|
pycparser==2.22
|
||||||
pydantic==2.10.6
|
pydantic==2.10.6
|
||||||
pydantic_core==2.27.2
|
pydantic_core==2.27.2
|
||||||
@ -74,24 +98,29 @@ python-slugify==8.0.4
|
|||||||
python-stdnum==1.20
|
python-stdnum==1.20
|
||||||
pytz==2025.2
|
pytz==2025.2
|
||||||
pyvin==0.0.2
|
pyvin==0.0.2
|
||||||
|
PyYAML==6.0.2
|
||||||
pyzbar==0.1.9
|
pyzbar==0.1.9
|
||||||
redis==3.5.3
|
redis==3.5.3
|
||||||
|
regex==2024.11.6
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
|
requests-toolbelt==1.0.0
|
||||||
rich==14.0.0
|
rich==14.0.0
|
||||||
|
ruff==0.11.10
|
||||||
setuptools==80.3.0
|
setuptools==80.3.0
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
sniffio==1.3.1
|
sniffio==1.3.1
|
||||||
|
soupsieve==2.7
|
||||||
|
SQLAlchemy==2.0.41
|
||||||
sqlparse==0.5.3
|
sqlparse==0.5.3
|
||||||
suds==1.2.0
|
suds==1.2.0
|
||||||
swapper==1.3.0
|
swapper==1.3.0
|
||||||
tablib==3.8.0
|
tablib==3.8.0
|
||||||
|
tenacity==9.1.2
|
||||||
text-unidecode==1.3
|
text-unidecode==1.3
|
||||||
tqdm==4.67.1
|
tqdm==4.67.1
|
||||||
types-python-dateutil==2.9.0.20241206
|
types-python-dateutil==2.9.0.20250516
|
||||||
typing_extensions==4.13.0
|
typing_extensions==4.13.0
|
||||||
tzdata==2025.2
|
tzdata==2025.2
|
||||||
urllib3==2.3.0
|
urllib3==2.3.0
|
||||||
wcwidth==0.2.13
|
wcwidth==0.2.13
|
||||||
langchain
|
zstandard==0.23.0
|
||||||
langchain_ollama
|
|
||||||
django-easy-audit==1.3.7
|
|
||||||
|
|||||||
@ -243,7 +243,7 @@
|
|||||||
<span class="fas fa-ellipsis-h fs-10"></span>
|
<span class="fas fa-ellipsis-h fs-10"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||||
<a href="{% url 'activate_account' 'staff' obj.slug %}"><button class="dropdown-item text-primary">{% trans "Activate" %}</button></a>
|
<a href="{% url 'activate_account' request.dealer.slug 'staff' obj.slug %}"><button class="dropdown-item text-primary">{% trans "Activate" %}</button></a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<a href="{% url 'permenant_delete_account' request.dealer.slug 'staff' obj.slug %}"><button class="dropdown-item text-danger">{% trans "Permenantly Delete" %}</button></a>
|
<a href="{% url 'permenant_delete_account' request.dealer.slug 'staff' obj.slug %}"><button class="dropdown-item text-danger">{% trans "Permenantly Delete" %}</button></a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h2 class="mb-5">{{ _("Notifications") }}</h2>
|
<h2 class="mb-5">{{ _("Notifications") }}</h2>
|
||||||
|
<div class="d-flex justify-content-end mb-3">
|
||||||
|
<a href="{% url 'mark_all_notifications_as_read' %}" class="btn btn-primary"><i class="far fa-envelope fs-8 me-2"></i>{{ _("Mark all as read") }}</a>
|
||||||
|
</div>
|
||||||
{% if notifications %}
|
{% if notifications %}
|
||||||
<div class="mx-n4 mx-lg-n6 mb-5 border-bottom">
|
<div class="mx-n4 mx-lg-n6 mb-5 border-bottom">
|
||||||
{% for notification in notifications %}
|
{% for notification in notifications %}
|
||||||
@ -20,10 +22,7 @@
|
|||||||
<p class="text-body-secondary fs-9 mb-0"><span class="me-1 far fa-clock"></span>{{ notification.created }}</p>
|
<p class="text-body-secondary fs-9 mb-0"><span class="me-1 far fa-clock"></span>{{ notification.created }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn fs-10 btn-sm dropdown-toggle dropdown-caret-none transition-none notification-dropdown-toggle" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10 text-body"></span></button>
|
|
||||||
<div class="dropdown-menu dropdown-menu-end py-2"><a class="dropdown-item" href="{% url 'mark_notification_as_read' notification.id %}">{{ _("Mark as Read")}}</a></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
<div class="card-header p-2">
|
<div class="card-header p-2">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<h5 class="text-body-emphasis mb-0">{{ _("Notifications") }}</h5>
|
<h5 class="text-body-emphasis mb-0">{{ _("Notifications") }}</h5>
|
||||||
<button class="btn btn-link p-0 fs-9 fw-normal" type="button" id="mark-all-read">{{ _("Mark all as read")}}</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body p-0">
|
<div class="card-body p-0">
|
||||||
@ -72,7 +72,7 @@
|
|||||||
|
|
||||||
data.notifications.forEach(notification => {
|
data.notifications.forEach(notification => {
|
||||||
seenNotificationIds.add(notification.id);
|
seenNotificationIds.add(notification.id);
|
||||||
if (notification.unread) {
|
if (!notification.is_read) {
|
||||||
unreadCount++;
|
unreadCount++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -136,7 +136,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createNotificationElement(data) {
|
function createNotificationElement(data) {
|
||||||
const isRead = data.read ? 'read' : 'unread';
|
const isRead = data.is_read ? 'read' : 'unread';
|
||||||
return `
|
return `
|
||||||
<div class="px-2 px-sm-3 py-3 notification-card position-relative ${isRead} border-bottom"
|
<div class="px-2 px-sm-3 py-3 notification-card position-relative ${isRead} border-bottom"
|
||||||
data-notification-id="${data.id}">
|
data-notification-id="${data.id}">
|
||||||
@ -223,7 +223,7 @@
|
|||||||
if (e.target.classList.contains('mark-as-read')) {
|
if (e.target.classList.contains('mark-as-read')) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const notificationId = e.target.getAttribute('data-notification-id');
|
const notificationId = e.target.getAttribute('data-notification-id');
|
||||||
fetch(`/notifications/${notificationId}/mark-read/`, {
|
fetch(`/notifications/${notificationId}/mark_as_read/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-CSRFToken': '{{ csrf_token }}',
|
'X-CSRFToken': '{{ csrf_token }}',
|
||||||
|
|||||||
@ -82,7 +82,7 @@
|
|||||||
<div class="card border-light h-100">
|
<div class="card border-light h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="h6 text-muted mb-2">{% trans 'Purchase Order Amount' %}</h5>
|
<h5 class="h6 text-muted mb-2">{% trans 'Purchase Order Amount' %}</h5>
|
||||||
<p class="h5">{% currency_symbol %}{{ po_model.po_amount|currency_format }}</p>
|
<p class="h5"><span class="currency">{{CURRENCY}}</span>{{ po_model.po_amount|currency_format }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -101,7 +101,7 @@
|
|||||||
<div class="card border-light h-100">
|
<div class="card border-light h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="h6 text-muted mb-2">{% trans 'Purchase Order Amount' %}</h5>
|
<h5 class="h6 text-muted mb-2">{% trans 'Purchase Order Amount' %}</h5>
|
||||||
<p class="h5">{% currency_symbol %}{{ po_model.po_amount|currency_format }}</p>
|
<p class="h5"><span class="currency">{{CURRENCY}}</span>{{ po_model.po_amount|currency_format }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -120,7 +120,7 @@
|
|||||||
<div class="card border-light h-100">
|
<div class="card border-light h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="h6 text-muted mb-2">{% trans 'PO Amount' %}</h5>
|
<h5 class="h6 text-muted mb-2">{% trans 'PO Amount' %}</h5>
|
||||||
<p class="h5">{{ po_model.po_amount|currency_format }}{% currency_symbol %}</p>
|
<p class="h5">{{ po_model.po_amount|currency_format }}<span class="currency">{{CURRENCY}}</span></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -128,7 +128,7 @@
|
|||||||
<div class="card border-light h-100">
|
<div class="card border-light h-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="h6 text-muted mb-2">{% trans 'Received Amount' %}</h5>
|
<h5 class="h6 text-muted mb-2">{% trans 'Received Amount' %}</h5>
|
||||||
<p class="h5 text-success">{{ po_model.po_amount_received|currency_format }}{% currency_symbol %}</p>
|
<p class="h5 text-success">{{ po_model.po_amount_received|currency_format }}<span class="currency">{{CURRENCY}}</span></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -148,7 +148,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="h6 text-muted mb-2">{% trans 'PO Amount' %}</h5>
|
<h5 class="h6 text-muted mb-2">{% trans 'PO Amount' %}</h5>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<p class="h5 mb-0 me-2">{% currency_symbol %}{{ po_model.po_amount|currency_format }}</p>
|
<p class="h5 mb-0 me-2"><span class="currency">{{CURRENCY}}</span>{{ po_model.po_amount|currency_format }}</p>
|
||||||
<span class="badge bg-success">
|
<span class="badge bg-success">
|
||||||
<i class="fas fa-check-circle me-1"></i>{% trans 'Fulfilled' %}
|
<i class="fas fa-check-circle me-1"></i>{% trans 'Fulfilled' %}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
<table class="table table-hover table-bordered">
|
<table class="table table-hover table-bordered">
|
||||||
<thead class="">
|
<thead class="">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="d-flex justify-content-between align-items-center">
|
<th style="min-width: 600px;" class="d-flex justify-content-between align-items-center">
|
||||||
{% trans 'Item' %}
|
{% trans 'Item' %}
|
||||||
{% if po_model.is_draft %}
|
{% if po_model.is_draft %}
|
||||||
<button type="button"
|
<button type="button"
|
||||||
@ -62,7 +62,7 @@
|
|||||||
<td id="{{ f.instance.html_id_quantity }}">{{ f.po_quantity|add_class:"form-control" }}</td>
|
<td id="{{ f.instance.html_id_quantity }}">{{ f.po_quantity|add_class:"form-control" }}</td>
|
||||||
<td>{{ f.entity_unit|add_class:"form-control" }}</td>
|
<td>{{ f.entity_unit|add_class:"form-control" }}</td>
|
||||||
<td class="text-end" id="{{ f.instance.html_id_total_amount }}">
|
<td class="text-end" id="{{ f.instance.html_id_total_amount }}">
|
||||||
{% currency_symbol %}{{ f.instance.po_total_amount | currency_format }}</td>
|
<span class="currency">{{CURRENCY}}</span>{{ f.instance.po_total_amount | currency_format }}</td>
|
||||||
<td>{{ f.po_item_status|add_class:"form-control" }}</td>
|
<td>{{ f.po_item_status|add_class:"form-control" }}</td>
|
||||||
{% if itemtxs_formset.can_delete %}
|
{% if itemtxs_formset.can_delete %}
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
@ -98,7 +98,7 @@
|
|||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th class="text-end">{% trans 'Total' %}</th>
|
<th class="text-end">{% trans 'Total' %}</th>
|
||||||
<th class="text-end">{% currency_symbol %}{{ po_model.po_amount | currency_format }}</th>
|
<th class="text-end"><span class="currency">{{CURRENCY}}</span>{{ po_model.po_amount | currency_format }}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
{% if itemtxs_formset.can_delete %}
|
{% if itemtxs_formset.can_delete %}
|
||||||
<th></th>
|
<th></th>
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
<td>{{ po.po_title }}</td>
|
<td>{{ po.po_title }}</td>
|
||||||
<td>{{ po.get_status_action_date }}</td>
|
<td>{{ po.get_status_action_date }}</td>
|
||||||
<td>{{ po.get_po_status_display }}</td>
|
<td>{{ po.get_po_status_display }}</td>
|
||||||
<td>{% currency_symbol %}{{ po.po_amount | currency_format }}</td>
|
<td><span class="currency">{{CURRENCY}}</span>{{ po.po_amount | currency_format }}</td>
|
||||||
<td class="has-text-centered">
|
<td class="has-text-centered">
|
||||||
<div class="dropdown is-right is-hoverable" id="bill-action-{{ po.uuid }}">
|
<div class="dropdown is-right is-hoverable" id="bill-action-{{ po.uuid }}">
|
||||||
<div class="dropdown-trigger">
|
<div class="dropdown-trigger">
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load trans from i18n %}
|
{% load trans from i18n %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load custom_filters %}
|
|
||||||
{% load django_ledger %}
|
{% load django_ledger %}
|
||||||
|
{% load custom_filters %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid mt-4">
|
<div class="container-fluid mt-4">
|
||||||
@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row text-center">
|
<div class="row text-center">
|
||||||
@ -31,7 +31,7 @@
|
|||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<h6 class="text-muted mb-2">{% trans 'PO Amount' %}</h6>
|
<h6 class="text-muted mb-2">{% trans 'PO Amount' %}</h6>
|
||||||
<h3 class="fw-light mb-0">
|
<h3 class="fw-light mb-0">
|
||||||
{% currency_symbol %}{{ po_model.po_amount | absolute | currency_format }}
|
<span class="currency">{{CURRENCY}}</span>{{ po_model.po_amount | absolute | currency_format }}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -39,24 +39,24 @@
|
|||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<h6 class="text-muted mb-2">{% trans 'Amount Received' %}</h6>
|
<h6 class="text-muted mb-2">{% trans 'Amount Received' %}</h6>
|
||||||
<h3 class="fw-light mb-0 text-success">
|
<h3 class="fw-light mb-0 text-success">
|
||||||
{% currency_symbol %}{{ po_model.po_amount_received | currency_format }}
|
<span class="currency">{{CURRENCY}}</span>{{ po_model.po_amount_received | currency_format }}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
{% po_item_table1 po_items %}
|
{% po_item_table1 po_items %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include "purchase_orders/includes/mark_as.html" %}
|
{% include "purchase_orders/includes/mark_as.html" %}
|
||||||
|
|||||||
@ -61,7 +61,7 @@
|
|||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th>{% currency_symbol %}{{ ce_cost_estimate__sum | currency_format }}</th>
|
<th><span class="currency">{{CURRENCY}}</span>{{ ce_cost_estimate__sum | currency_format }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -69,8 +69,8 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{ i.item_model__name }}</td>
|
<td>{{ i.item_model__name }}</td>
|
||||||
<td>{{ i.ce_quantity__sum }}</td>
|
<td>{{ i.ce_quantity__sum }}</td>
|
||||||
<td>{% currency_symbol %}{{ i.avg_unit_cost | currency_format }}</td>
|
<td><span class="currency">{{CURRENCY}}</span>{{ i.avg_unit_cost | currency_format }}</td>
|
||||||
<td>{% currency_symbol %}{{ i.ce_cost_estimate__sum | currency_format }}</td>
|
<td><span class="currency">{{CURRENCY}}</span>{{ i.ce_cost_estimate__sum | currency_format }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
<td class="has-text-centered">{{ item.po_unit_cost }}</td>
|
<td class="has-text-centered">{{ item.po_unit_cost }}</td>
|
||||||
<td class="has-text-centered">{{ item.po_quantity }}</td>
|
<td class="has-text-centered">{{ item.po_quantity }}</td>
|
||||||
<td class="{% if item.is_cancelled %}djl-is-strikethrough{% endif %} has-text-centered">
|
<td class="{% if item.is_cancelled %}djl-is-strikethrough{% endif %} has-text-centered">
|
||||||
{% currency_symbol %}{{ item.po_total_amount | currency_format }}</td>
|
<span class="currency">{{CURRENCY}}</span>{{ item.po_total_amount | currency_format }}</td>
|
||||||
<td class="has-text-weight-bold has-text-centered {% if item.is_cancelled %}has-text-danger{% endif %}">
|
<td class="has-text-weight-bold has-text-centered {% if item.is_cancelled %}has-text-danger{% endif %}">
|
||||||
{% if item.po_item_status %}
|
{% if item.po_item_status %}
|
||||||
{{ item.get_po_item_status_display }}
|
{{ item.get_po_item_status_display }}
|
||||||
@ -40,7 +40,7 @@
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td class="has-text-right">{% trans 'Total PO Amount' %}</td>
|
<td class="has-text-right">{% trans 'Total PO Amount' %}</td>
|
||||||
<td class="has-text-weight-bold has-text-centered">
|
<td class="has-text-weight-bold has-text-centered">
|
||||||
{% currency_symbol %}{{ po_model.po_amount | currency_format }}</td>
|
<span class="currency">{{CURRENCY}}</span>{{ po_model.po_amount | currency_format }}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -42,7 +42,6 @@
|
|||||||
<td class="align-middle white-space-nowrap ps-1">
|
<td class="align-middle white-space-nowrap ps-1">
|
||||||
<div>
|
<div>
|
||||||
<a class="fs-8 fw-bold" href="{% url 'user_detail' request.dealer.slug user.slug%}">{{ user.arabic_name }}</a>
|
<a class="fs-8 fw-bold" href="{% url 'user_detail' request.dealer.slug user.slug%}">{{ user.arabic_name }}</a>
|
||||||
{{user.dealer}}
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle white-space-nowrap align-items-center">{{ user.email }}</td>
|
<td class="align-middle white-space-nowrap align-items-center">{{ user.email }}</td>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user