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

This commit is contained in:
Faheedkhan 2025-06-29 17:20:48 +03:00
commit 8f14f9bfc9
20 changed files with 155 additions and 138 deletions

View File

@ -1,21 +1,7 @@
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from inventory.tasks import create_coa_accounts
from inventory.models import Dealer
User = get_user_model()
from inventory.tasks import long_running_task
from django_q.tasks import async_task
class Command(BaseCommand):
def handle(self, *args, **kwargs):
# user = User.objects.last()
# 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)
async_task(long_running_task, 20)

View File

@ -126,7 +126,9 @@ class DealerSlugMiddleware:
request.path_info.startswith('/en/login/') or \
request.path_info.startswith('/en/logout/') 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
if not request.user.is_authenticated:
@ -141,6 +143,7 @@ class DealerSlugMiddleware:
return None
if dealer_slug.lower() != request.dealer.slug.lower():
print(dealer_slug)
logger.warning(f"Dealer slug mismatch: {dealer_slug} != {request.dealer.slug}")
raise Http404("Dealer slug mismatch")

View File

@ -3,6 +3,7 @@ from datetime import datetime
from django.conf import settings
from django.contrib.auth.models import Permission
from decimal import Decimal
from django.urls import reverse
from django.utils.text import slugify
from django.utils import timezone
from django.core.validators import MinValueValidator
@ -600,6 +601,8 @@ class Car(Base):
)
# 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):
self.slug = slugify(self.vin)
self.hash = self.get_hash

View File

@ -1,4 +1,6 @@
from decimal import Decimal
from django.urls import reverse
from inventory.tasks import create_coa_accounts, create_make_accounts
from django.contrib.auth.models import Group
from django.db.models.signals import post_save, post_delete
@ -18,7 +20,7 @@ from django_ledger.models import (
from . import models
from django.utils.timezone import now
from django.db import transaction
from django_q.tasks import async_task
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])
# Create COA accounts, background task
create_coa_accounts(instance.pk)
async_task(create_coa_accounts,instance.pk)
# create_settings(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.
:type kwargs: dict
"""
group_names = ["Inventory", "Accountant", "Sales"]
group_names = ["Inventory", "Accountant", "Sales","Manager"]
def create_groups():
for group_name in group_names:
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(
name=group_name, dealer=instance, group=group
@ -896,7 +898,26 @@ def update_finance_cost(sender, instance, created, **kwargs):
@receiver(post_save, sender=PurchaseOrderModel)
def create_po_item_upload(sender,instance,created,**kwargs):
if instance.po_status == "fulfilled":
for item in instance.get_itemtxs_data()[0]:
if instance.po_status == "fulfilled":
for item in instance.get_itemtxs_data()[0]:
dealer = models.Dealer.objects.get(entity=instance.entity)
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>
""",
)

View File

@ -1,13 +1,11 @@
from datetime import datetime
from django.db import transaction
from django_ledger.io import roles
from django.core.mail import send_mail
from background_task import background
from django.utils.translation import gettext_lazy as _
from inventory.models import DealerSettings, Dealer
from django_q.tasks import async_task
# @background
def create_settings(pk):
instance = Dealer.objects.get(pk=pk)
@ -34,7 +32,6 @@ def create_settings(pk):
)
@background
def create_coa_accounts(pk):
with transaction.atomic():
instance = Dealer.objects.select_for_update().get(pk=pk)
@ -772,8 +769,6 @@ def create_coa_accounts(pk):
except Exception as e:
print(e)
@background
def create_coa_accounts1(pk):
with transaction.atomic():
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
@background
def send_email(from_, to_, subject, message):
subject = subject
message = message
from_email = from_
recipient_list = [to_]
send_mail(subject, message, from_email, recipient_list)
async_task(send_mail,subject, message, from_email, recipient_list)
@background
def long_running_task(task_id, *args, **kwargs):
# @background
def long_running_task(duration):
"""Example background task"""
print(f"Starting task {task_id} with args: {args}, kwargs: {kwargs}")
# 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
print("Task completed")
return True

View File

@ -644,4 +644,4 @@ def inventory_table(context, queryset):
"inventory_list": queryset,
}
ctx.update(queryset.aggregate(inventory_total_value=Sum("total_value")))
return ctx
return ctx

View File

@ -14,7 +14,6 @@ urlpatterns = [
path("<slug:dealer_slug>/", views.HomeView.as_view(), name="home"),
# Tasks
path("<slug:dealer_slug>/tasks/", views.task_list, name="task_list"),
path("legal/", views.terms_and_privacy, name="terms_and_privacy"),
# path('tasks/<int:task_id>/detail/', views.task_detail, name='task_detail'),
# Dashboards
@ -203,38 +202,29 @@ urlpatterns = [
),
# 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(
"crm/notifications/",
"/notifications/",
views.NotificationListView.as_view(),
name="notifications_history",
),
path(
"crm/fetch_notifications/",
views.fetch_notifications,
name="fetch_notifications",
),
path(
"crm/notifications/<int:notification_id>/mark_as_read/",
"notifications/<int:notification_id>/mark_as_read/",
views.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"),
#######################################################
# Vendor URLs

View File

@ -26,7 +26,7 @@ from django_ledger.models import (
from django.utils.translation import get_language
from appointment.models import StaffMember
from django.contrib.auth.models import User
from django_q.tasks import async_task
import secrets
@ -152,7 +152,7 @@ def send_email(from_, to_, subject, message):
message = message
from_email = from_
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):

View File

@ -22,7 +22,6 @@ from urllib.parse import urlparse, urlunparse
from inventory.mixins import DealerSlugMixin
from inventory.models import Status as LeadStatus
from django.db import IntegrityError
from background_task.models import Task
from django.views.generic import FormView
from django.views.decorators.http import require_http_methods
from django.db.models.deletion import RestrictedError
@ -2669,9 +2668,17 @@ class GroupCreateView(
def form_valid(self, form):
dealer = get_user_type(self.request)
instance = form.save(commit=False)
group = Group.objects.create(name=f"{dealer.slug}_{instance.name}")
instance.dealer = dealer
instance.group = group
group_name = f"{dealer.slug}_{instance.name}"
group,created = Group.objects.get_or_create(name=group_name)
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()
return super().form_valid(form)
@ -2950,10 +2957,10 @@ class UserCreateView(
staff.staff_member = staff_member
staff.dealer = dealer
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()
if group:
staff.add_group(group)
staff.add_group(group.group)
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("user_list", args=[self.request.dealer.slug])
@ -8776,20 +8783,8 @@ def payment_callback(request,dealer_slug):
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):
print("hi")
def event_stream():
last_id = request.GET.get("last_id", 0)
while True:
@ -8832,7 +8827,7 @@ def mark_notification_as_read(request, notification_id):
notification = get_object_or_404(
models.Notification, id=notification_id, user=request.user
)
notification.read = True
notification.is_read = True
notification.save()
return JsonResponse({"status": "success"})
@ -8840,9 +8835,9 @@ def mark_notification_as_read(request, notification_id):
@login_required
def mark_all_notifications_as_read(request):
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
@ -9736,7 +9731,7 @@ def upload_cars(request, dealer_slug, pk=None):
if not csv_file.name.endswith(".csv"):
messages.error(request, "Please upload a CSV file")
return redirect("upload_cars", dealer_slug=dealer_slug)
return response
try:
# Read the file content
file_content = csv_file.read().decode("utf-8")
@ -9782,10 +9777,16 @@ def upload_cars(request, dealer_slug, pk=None):
po_item.save()
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:
messages.error(request, f"Error processing CSV: {str(e)}")
return response
form = forms.CSVUploadForm()
form.fields["vendor"].queryset = dealer.vendors.all()

View File

@ -4,18 +4,21 @@ arrow==1.3.0
asgiref==3.8.1
attrs==25.3.0
Babel==2.15.0
beautifulsoup4==4.13.4
blessed==1.21.0
cattrs==24.1.3
certifi==2025.1.31
cffi==1.17.1
charset-normalizer==3.4.1
click==8.2.1
colorama==0.4.6
crispy-bootstrap5==2024.10
cryptography==44.0.2
cssbeautifier==1.15.4
defusedxml==0.7.1
diff-match-patch==20241021
distro==1.9.0
Django==5.1.7
Django==5.2.3
django-allauth==65.6.0
django-appointment==3.8.0
django-background-tasks==1.2.8
@ -24,17 +27,20 @@ django-ckeditor==6.7.2
django-cors-headers==4.7.0
django-countries==7.6.1
django-crispy-forms==2.3
django-easy-audit==1.3.7
django-extensions==3.2.3
django-filter==25.1
django-import-export==4.3.7
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-ordered-model==3.7.4
django-phonenumber-field==8.0.0
django-picklefield==3.3
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-sequences==3.0
django-tables2==2.7.5
@ -42,29 +48,47 @@ django-treebeard==4.7.1
django-widget-tweaks==1.5.0
djangorestframework==3.15.2
djhtml==3.0.7
djlint==1.36.4
docopt==0.6.2
Faker==37.1.0
EditorConfig==0.17.0
Faker==37.3.0
fleming==0.7.0
fonttools==4.57.0
fpdf==1.7.2
fpdf2==2.8.3
greenlet==3.2.2
h11==0.14.0
httpcore==1.0.7
httpx==0.28.1
icalendar==6.1.2
idna==3.10
jiter==0.9.0
jsbeautifier==1.15.4
json5==0.12.0
jsonpatch==1.33
jsonpointer==3.0.0
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
Markdown==3.7
Markdown==3.8
markdown-it-py==3.0.0
mdurl==0.1.2
num2words==0.5.14
numpy==2.2.4
ofxtools==0.9.5
ollama==0.4.8
openai==1.68.2
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
pillow==10.4.0
pillow==11.2.1
pycparser==2.22
pydantic==2.10.6
pydantic_core==2.27.2
@ -74,24 +98,29 @@ python-slugify==8.0.4
python-stdnum==1.20
pytz==2025.2
pyvin==0.0.2
PyYAML==6.0.2
pyzbar==0.1.9
redis==3.5.3
regex==2024.11.6
requests==2.32.3
requests-toolbelt==1.0.0
rich==14.0.0
ruff==0.11.10
setuptools==80.3.0
six==1.17.0
sniffio==1.3.1
soupsieve==2.7
SQLAlchemy==2.0.41
sqlparse==0.5.3
suds==1.2.0
swapper==1.3.0
tablib==3.8.0
tenacity==9.1.2
text-unidecode==1.3
tqdm==4.67.1
types-python-dateutil==2.9.0.20241206
types-python-dateutil==2.9.0.20250516
typing_extensions==4.13.0
tzdata==2025.2
urllib3==2.3.0
wcwidth==0.2.13
langchain
langchain_ollama
django-easy-audit==1.3.7
zstandard==0.23.0

View File

@ -243,7 +243,7 @@
<span class="fas fa-ellipsis-h fs-10"></span>
</button>
<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>
<a href="{% url 'permenant_delete_account' request.dealer.slug 'staff' obj.slug %}"><button class="dropdown-item text-danger">{% trans "Permenantly Delete" %}</button></a>
</div>

View File

@ -4,7 +4,9 @@
<div class="content">
<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 %}
<div class="mx-n4 mx-lg-n6 mb-5 border-bottom">
{% 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>
</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>
{% endfor %}
</div>

View File

@ -21,7 +21,7 @@
<div class="card-header p-2">
<div class="d-flex justify-content-between">
<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 class="card-body p-0">
@ -72,7 +72,7 @@
data.notifications.forEach(notification => {
seenNotificationIds.add(notification.id);
if (notification.unread) {
if (!notification.is_read) {
unreadCount++;
}
});
@ -136,7 +136,7 @@
}
function createNotificationElement(data) {
const isRead = data.read ? 'read' : 'unread';
const isRead = data.is_read ? 'read' : 'unread';
return `
<div class="px-2 px-sm-3 py-3 notification-card position-relative ${isRead} border-bottom"
data-notification-id="${data.id}">
@ -223,7 +223,7 @@
if (e.target.classList.contains('mark-as-read')) {
e.preventDefault();
const notificationId = e.target.getAttribute('data-notification-id');
fetch(`/notifications/${notificationId}/mark-read/`, {
fetch(`/notifications/${notificationId}/mark_as_read/`, {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token }}',

View File

@ -82,7 +82,7 @@
<div class="card border-light h-100">
<div class="card-body">
<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>
@ -101,7 +101,7 @@
<div class="card border-light h-100">
<div class="card-body">
<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>
@ -120,7 +120,7 @@
<div class="card border-light h-100">
<div class="card-body">
<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>
@ -128,7 +128,7 @@
<div class="card border-light h-100">
<div class="card-body">
<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>
@ -148,7 +148,7 @@
<div class="card-body">
<h5 class="h6 text-muted mb-2">{% trans 'PO Amount' %}</h5>
<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">
<i class="fas fa-check-circle me-1"></i>{% trans 'Fulfilled' %}
</span>

View File

@ -18,7 +18,7 @@
<table class="table table-hover table-bordered">
<thead class="">
<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' %}
{% if po_model.is_draft %}
<button type="button"
@ -62,7 +62,7 @@
<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 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>
{% if itemtxs_formset.can_delete %}
<td class="text-center">
@ -98,7 +98,7 @@
<th></th>
<th></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>
{% if itemtxs_formset.can_delete %}
<th></th>

View File

@ -21,7 +21,7 @@
<td>{{ po.po_title }}</td>
<td>{{ po.get_status_action_date }}</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">
<div class="dropdown is-right is-hoverable" id="bill-action-{{ po.uuid }}">
<div class="dropdown-trigger">

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %}
{% load trans from i18n %}
{% load static %}
{% load custom_filters %}
{% load django_ledger %}
{% load custom_filters %}
{% block content %}
<div class="container-fluid mt-4">
@ -23,7 +23,7 @@
</div>
</div>
<div class="col-lg-12">
<div class="col-lg-12">
<div class="card mb-4">
<div class="card-body">
<div class="row text-center">
@ -31,7 +31,7 @@
<div class="p-2">
<h6 class="text-muted mb-2">{% trans 'PO Amount' %}</h6>
<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>
</div>
</div>
@ -39,24 +39,24 @@
<div class="p-2">
<h6 class="text-muted mb-2">{% trans 'Amount Received' %}</h6>
<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>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-12">
<div class="col-lg-12">
<div class="table-responsive">
{% po_item_table1 po_items %}
</div>
</div>
</div>
</div>
</div>
{% include "purchase_orders/includes/mark_as.html" %}

View File

@ -61,7 +61,7 @@
<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>
</tfoot>
<tbody>
@ -69,8 +69,8 @@
<tr>
<td>{{ i.item_model__name }}</td>
<td>{{ i.ce_quantity__sum }}</td>
<td>{% currency_symbol %}{{ 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.avg_unit_cost | currency_format }}</td>
<td><span class="currency">{{CURRENCY}}</span>{{ i.ce_cost_estimate__sum | currency_format }}</td>
</tr>
{% endfor %}
</tbody>

View File

@ -20,7 +20,7 @@
<td class="has-text-centered">{{ item.po_unit_cost }}</td>
<td class="has-text-centered">{{ item.po_quantity }}</td>
<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 %}">
{% if item.po_item_status %}
{{ item.get_po_item_status_display }}
@ -40,7 +40,7 @@
<td></td>
<td class="has-text-right">{% trans 'Total PO Amount' %}</td>
<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>
</tr>

View File

@ -42,7 +42,6 @@
<td class="align-middle white-space-nowrap ps-1">
<div>
<a class="fs-8 fw-bold" href="{% url 'user_detail' request.dealer.slug user.slug%}">{{ user.arabic_name }}</a>
{{user.dealer}}
</div>
</td>
<td class="align-middle white-space-nowrap align-items-center">{{ user.email }}</td>