add inline enter for discount and additionals in the estimate detail + more

This commit is contained in:
ismail 2025-07-14 15:33:49 +03:00
parent 0bc0d17f43
commit 50ae705e97
18 changed files with 293 additions and 104 deletions

Binary file not shown.

View File

@ -70,6 +70,7 @@ admin.site.register(models.Schedule)
admin.site.register(models.Notes)
admin.site.register(models.UserActivityLog)
admin.site.register(models.DealersMake)
admin.site.register(models.ExtraInfo)
@admin.register(models.Car)

View File

@ -30,6 +30,7 @@ from .models import (
Vendor,
Schedule,
Car,
VatRate,
CarTransfer,
CarFinance,
CustomCard,
@ -143,7 +144,7 @@ class StaffForm(forms.ModelForm):
)
class Meta:
model = Staff
fields = ["name", "arabic_name", "phone_number", "group"]
fields = ["name", "arabic_name", "phone_number", "address","image","group"]
# Dealer Form
@ -2091,3 +2092,16 @@ class CSVUploadForm(forms.Form):
# Reset file pointer for later processing
csv_file.file.seek(0)
return csv_file
class AdditionalFinancesForm(forms.Form):
additional_finances = forms.ModelMultipleChoiceField(
queryset=AdditionalServices.objects.all(),
widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}),
required=False,
)
class VatRateForm(forms.ModelForm):
class Meta:
model = VatRate
fields = ['rate']

View File

@ -1,9 +1,9 @@
import logging
from django.http import Http404, HttpResponseForbidden
from django.shortcuts import redirect
from inventory import models
from django.utils import timezone
# from django.http import Http404, HttpResponseForbidden
# from django.shortcuts import redirect
# from inventory import models
# from django.utils import timezone
from inventory.utils import get_user_type
@ -102,17 +102,24 @@ class InjectDealerMiddleware:
request.is_inventory = False
if hasattr(request.user, "dealer"):
request.is_dealer = True
request.dealer = request.user.dealer
elif hasattr(request.user, "staffmember"):
request.is_staff = True
request.staff = request.user.staffmember.staff
request.dealer = request.staff.dealer
staff = getattr(request.user.staffmember, "staff")
if "Accountant" in staff.groups.values_list("name", flat=True):
staff_groups = staff.groups.values_list("name", flat=True)
if "Accountant" in staff_groups:
request.is_accountant = True
elif "Manager" in staff.groups.values_list("name", flat=True):
elif "Manager" in staff_groups:
request.is_manager = True
elif "Sales" in staff.groups.values_list("name", flat=True):
elif "Sales" in staff_groups:
request.is_sales = True
elif "Inventory" in staff.groups.values_list("name", flat=True):
elif "Inventory" in staff_groups:
request.is_inventory = True
request.entity = request.dealer.entity
request.admin = request.dealer.entity.admin
except Exception:
pass
response = self.get_response(request)

View File

@ -191,6 +191,7 @@ class UnitOfMeasure(models.TextChoices):
class VatRate(models.Model):
dealer = models.ForeignKey("Dealer", on_delete=models.CASCADE)
rate = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal("0.15"))
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
@ -563,7 +564,7 @@ class AdditionalServices(models.Model, LocalizedNameMixin):
@property
def price_(self):
vat = VatRate.objects.filter(is_active=True).first()
vat = VatRate.objects.filter(dealer=self.dealer,is_active=True).first()
return (
Decimal(self.price + (self.price * vat.rate))
if self.taxable
@ -889,6 +890,9 @@ class CarFinance(models.Model):
selling_price = models.DecimalField(
max_digits=14, decimal_places=2, verbose_name=_("Selling Price")
)
marked_price = models.DecimalField(
max_digits=14, decimal_places=2, verbose_name=_("Marked Price")
)
discount_amount = models.DecimalField(
max_digits=14,
decimal_places=2,
@ -921,7 +925,7 @@ class CarFinance(models.Model):
@property
def vat_amount(self):
vat = VatRate.objects.filter(is_active=True).first()
vat = VatRate.objects.filter(dealer=self.car.dealer, is_active=True).first()
if vat:
return (self.total_discount * Decimal(vat.rate)).quantize(Decimal("0.01"))
return Decimal("0.00")
@ -1190,6 +1194,9 @@ class Dealer(models.Model, LocalizedNameMixin):
return True
return False
@property
def vat_rate(self):
return VatRate.objects.get(dealer=self,is_active=True).rate
class Meta:
verbose_name = _("Dealer")
verbose_name_plural = _("Dealers")
@ -1223,6 +1230,12 @@ class Staff(models.Model, LocalizedNameMixin):
staff_type = models.CharField(
choices=StaffTypes.choices, max_length=255, verbose_name=_("Staff Type")
)
address = models.CharField(
max_length=200, blank=True, null=True, verbose_name=_("Address")
)
image = models.ImageField(
upload_to="staff/", blank=True, null=True, verbose_name=_("Image")
)
active = models.BooleanField(default=True, verbose_name=_("Active"))
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated"))
@ -3164,7 +3177,13 @@ class ExtraInfo(models.Model):
- JSON data storage
- Tracking fields
"""
# Primary GenericForeignKey (main linked object)
dealer = models.ForeignKey(
Dealer,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="extra_info"
)
content_type = models.ForeignKey(
ContentType,
on_delete=models.CASCADE,
@ -3234,8 +3253,8 @@ class ExtraInfo(models.Model):
related_content_type=related_content_type,
related_object_id=staff.pk
)
return [x.content_object.sale_orders.first() for x in qs if x.content_object.sale_orders.first()]
# qs = qs.select_related("customer","estimate","invoice")
return [x.content_object.sale_orders.select_related("customer","estimate","invoice").first() for x in qs if x.content_object.sale_orders.first()]
@classmethod
def get_invoices(cls, staff=None, is_dealer=False):
if not staff and not is_dealer:

View File

@ -215,7 +215,7 @@ class PurchaseOrderModelUpdateView(LoginRequiredMixin,
if form.has_changed():
po_items_qs = ItemTransactionModel.objects.for_po(
entity_slug=self.kwargs['entity_slug'],
user_model=dealer.entity.admin,
user_model=self.request.admin,
po_pk=po_model.uuid,
).select_related('bill_model')
@ -419,17 +419,16 @@ class BillModelUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateVie
def get_form(self, form_class=None):
form_class = self.get_form_class()
dealer = get_object_or_404(Dealer,slug=self.kwargs['dealer_slug'])
entity_model = dealer.entity
entity_model = self.request.dealer.entity
if self.request.method == 'POST' and self.action_update_items:
return form_class(
entity_model=entity_model,
user_model=dealer.entity.admin,
user_model=self.request.admin,
instance=self.object
)
form = form_class(
entity_model=entity_model,
user_model=dealer.entity.admin,
user_model=self.request.admin,
**self.get_form_kwargs()
)
try:

View File

@ -672,6 +672,7 @@ def create_dealer_settings(sender, instance, created, **kwargs):
:return: None
"""
if created:
models.VatRate.objects.create(dealer=instance)
models.DealerSettings.objects.create(
dealer=instance,
invoice_cash_account=instance.entity.get_all_accounts()

View File

@ -51,6 +51,11 @@ urlpatterns = [
views.DealerUpdateView.as_view(),
name="dealer_update",
),
path(
"dealers/<slug:slug>/dealer_vat_rate_update/",
views.dealer_vat_rate_update,
name="dealer_vat_rate_update",
),
# path('dealers/<int:pk>/delete/', views.DealerDeleteView.as_view(), name='dealer_delete'),
# CRM URLs
path(
@ -683,6 +688,17 @@ urlpatterns = [
views.preview_sale_order,
name="preview_sale_order",
),
path(
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/update_estimate_discount/",
views.update_estimate_discount,
name="update_estimate_discount",
),
path(
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/update_estimate_additionals/",
views.update_estimate_additionals,
name="update_estimate_additionals",
),
###############################################
# Invoice
###############################################

View File

@ -22,6 +22,7 @@ from django_ledger.models.items import ItemModel
from django.utils.translation import get_language
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import gettext_lazy as _
from django.contrib.contenttypes.models import ContentType
from django_ledger.models.transactions import TransactionModel
from django_ledger.models.journal_entry import JournalEntryModel
@ -996,13 +997,15 @@ class CarFinanceCalculator:
ADDITIONAL_SERVICES_KEY = "additional_services"
def __init__(self, model):
self.dealer = models.Dealer.objects.get(entity=model.entity)
self.model = model
self.vat_rate = self._get_vat_rate()
self.item_transactions = self._get_item_transactions()
self.additional_services = self._get_additional_services()
self.extra_info = models.ExtraInfo.objects.get(dealer=self.dealer,content_type=ContentType.objects.get_for_model(model),object_id=model.pk)
def _get_vat_rate(self):
vat = models.VatRate.objects.filter(is_active=True).first()
vat = models.VatRate.objects.filter(dealer=self.dealer,is_active=True).first()
if not vat:
raise ObjectDoesNotExist("No active VAT rate found")
return vat.rate
@ -1025,7 +1028,6 @@ class CarFinanceCalculator:
car_finance = self._get_nested_value(item, self.CAR_FINANCE_KEY)
car_info = self._get_nested_value(item, self.CAR_INFO_KEY)
unit_price = Decimal(car_finance.get("selling_price", 0))
print(item.item_model.car.finances)
return {
"item_number": item.item_model.item_number,
"vin": car_info.get("vin"),
@ -1037,6 +1039,7 @@ class CarFinanceCalculator:
"mileage": car_info.get("mileage"),
"cost_price": car_finance.get("cost_price"),
"selling_price": car_finance.get("selling_price"),
"marked_price": car_finance.get("marked_price"),
"discount": car_finance.get("discount_amount"),
"quantity": quantity,
"unit_price": unit_price,
@ -1070,13 +1073,15 @@ class CarFinanceCalculator:
Decimal(x.get("price_")) for x in self._get_additional_services()
)
total_discount = sum(
Decimal(
self._get_nested_value(item, self.CAR_FINANCE_KEY, "discount_amount")
)
for item in self.item_transactions
)
total_price_discounted = total_price - total_discount
total_discount = self.extra_info.data.get("discount")
# total_discount = sum(
# Decimal(
# self._get_nested_value(item, self.CAR_FINANCE_KEY, "discount_amount")
# )
# for item in self.item_transactions
# )
total_price_discounted = total_price - Decimal(total_discount)
total_vat_amount = total_price_discounted * self.vat_rate
return {
@ -1085,7 +1090,7 @@ class CarFinanceCalculator:
), # total_price_before_discount,
"total_price": round(total_price_discounted, 2), # total_price_discounted,
"total_vat_amount": round(total_vat_amount, 2), # total_vat_amount,
"total_discount": round(total_discount, 2),
"total_discount": round(Decimal(total_discount)),
"total_additionals": round(total_additionals, 2), # total_additionals,
"grand_total": round(
total_price_discounted + total_vat_amount + total_additionals, 2
@ -1139,7 +1144,6 @@ def get_item_transactions(txs):
data["customer"] = tx.invoice_model.customer
if bool(data):
transactions.append(data)
print(data)
return transactions
@ -1291,7 +1295,7 @@ def handle_account_process(invoice, amount, finance_data):
exc_info=True
)
print(e)
car.finances.is_sold = True
car.finances.save()
car.item_model.save()
@ -1428,7 +1432,7 @@ def handle_payment(request, order):
print("Failed to process payment:", data)
#
data = response.json()
print(data)
# order.status = AbstractOrder.STATUS.NEW
order.save()
#

View File

@ -1782,7 +1782,6 @@ class CarUpdateView(
def get_form(self, form_class=None):
form = super().get_form(form_class)
dealer = get_user_type(self.request)
print(dealer.get_vendors())
form.fields["vendor"].queryset = dealer.vendors.all()
return form
@ -2321,12 +2320,16 @@ class DealerDetailView(LoginRequiredMixin, PermissionRequiredMixin,DetailView):
context["cars_count"] = cars_count
context["allowed_users"] = dealer.user_quota
context["allowed_cars"] = dealer.car_quota
context["vatform"] = forms.VatRateForm(initial={"rate": dealer.vat_rate})
context["quota_display"] = (
f"{staff_count}/{dealer.user_quota}" if dealer.user_quota else "0"
)
return context
def dealer_vat_rate_update(request,slug):
dealer = get_object_or_404(models.Dealer,slug=slug)
models.VatRate.objects.filter(dealer=dealer).update(rate=request.POST.get("rate"))
return redirect("dealer_detail", slug=slug)
class DealerUpdateView(LoginRequiredMixin,PermissionRequiredMixin, SuccessMessageMixin, UpdateView):
"""
@ -4495,11 +4498,14 @@ class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
if any([self.request.is_dealer ,self.request.is_manager ,self.request.is_accountant]):
qs = models.ExtraInfo.objects.filter(
dealer=dealer,
content_type=ContentType.objects.get_for_model(EstimateModel),
related_content_type=ContentType.objects.get_for_model(models.Staff),
)
print(qs)
elif self.request.is_staff and self.request.is_sales:
qs = models.ExtraInfo.objects.filter(
dealer=dealer,
content_type=ContentType.objects.get_for_model(EstimateModel),
related_content_type=ContentType.objects.get_for_model(models.Staff),
related_object_id=staff.pk,
@ -4696,15 +4702,18 @@ def create_estimate(request, dealer_slug, slug=None):
if staff:=getattr(request.user.staffmember,'staff',None):
models.ExtraInfo.objects.create(
dealer=dealer,
content_object=estimate,
related_object=staff,
created_by=request.user,
)
else:
models.ExtraInfo.objects.create(
dealer=dealer,
content_object=estimate,
related_object=request.user,
created_by=request.user,
data={"vat_rate": 0.15,"discount": 0},
)
url = reverse(
@ -4809,14 +4818,22 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
permission_required = ["django_ledger.view_estimatemodel"]
def get_context_data(self, **kwargs):
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
estimate = kwargs.get("object")
if estimate.get_itemtxs_data():
calculator = CarFinanceCalculator(estimate)
finance_data = calculator.get_finance_data()
print(finance_data)
invoice_obj = InvoiceModel.objects.all().filter(ce_model=estimate).first()
kwargs["data"] = finance_data
kwargs["invoice"] = invoice_obj
try:
cf = estimate.get_itemtxs_data()[0].first().item_model.car.finances
selected_items = cf.additional_services.filter(dealer=dealer)
form = forms.AdditionalFinancesForm()
form.initial["additional_finances"] = selected_items
kwargs["additionals_form"] = form
except Exception as e:
logger.error(e)
return super().get_context_data(**kwargs)
@ -4896,6 +4913,31 @@ def create_sale_order(request, dealer_slug, pk):
)
@login_required
@require_POST
def update_estimate_discount(request,dealer_slug,pk):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
estimate = get_object_or_404(EstimateModel, pk=pk)
extra_info = models.ExtraInfo.objects.get(dealer=dealer,content_type=ContentType.objects.get_for_model(EstimateModel),object_id=estimate.pk)
discount_amount = request.POST.get("discount_amount",0)
extra_info.data.update({"discount":Decimal(discount_amount)})
extra_info.save()
return redirect("estimate_detail", dealer_slug=dealer_slug, pk=pk)
@login_required
@require_POST
def update_estimate_additionals(request,dealer_slug,pk):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
form = forms.AdditionalFinancesForm(request.POST)
if request.method == "POST":
if form.is_valid():
estimate = get_object_or_404(EstimateModel, pk=pk)
car = estimate.get_itemtxs_data()[0].first().item_model.car
car.finances.additional_services.set(form.cleaned_data["additional_finances"])
car.finances.save()
return redirect("estimate_detail", dealer_slug=dealer_slug, pk=pk)
class SaleOrderDetail(LoginRequiredMixin,PermissionRequiredMixin,DetailView):
model = models.SaleOrder
template_name = "sales/orders/order_details.html"
@ -5796,11 +5838,10 @@ class LeadListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
| Q(next_action__icontains=query)
| Q(staff__name__icontains=query))
if self.request.is_dealer:
if self.request.is_dealer :#or self.request.is_manager:
return qs
if self.request.user.is_staff:
staff = getattr(self.request.user.staffmember, "staff", None)
return qs.filter(staff=staff)
if self.request.is_staff:
return qs.filter(staff=self.request.staff)
return models.Lead.objects.none()
@ -5856,7 +5897,7 @@ class LeadDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
context["transfer_form"] = forms.LeadTransferForm()
context["transfer_form"].fields[
"transfer_to"
].queryset = models.Staff.objects.filter(
].queryset = models.Staff.objects.select_related("staff_member","staff_member__user").filter(
dealer=dealer,staff_member__user__groups__permissions__codename__contains="can_reassign_lead").exclude(staff_member__user=self.request.user).distinct()
context["activity_form"] = forms.ActivityForm()
@ -5976,15 +6017,12 @@ def lead_create(request,dealer_slug):
qs = form.fields["id_car_make"].queryset.filter(
is_sa_import=True, pk__in=dealer_make_list
)
form.fields["staff"].queryset = form.fields["staff"].queryset.filter(
dealer=dealer,staff_member__user__groups__permissions__codename__contains="add_lead").distinct()
form.fields["staff"].queryset = form.fields["staff"].queryset.select_related("staff_member","staff_member__user").filter(dealer=dealer,staff_member__user__groups__permissions__codename__contains="add_lead").distinct()
if hasattr(request.user.staffmember, "staff"):
staff = request.user.staffmember.staff
form.initial["staff"] = staff
if request.is_staff:
form.initial["staff"] = request.staff
form.fields["staff"].widget.attrs.update({"readonly":"true","required":"true"})
form.fields["staff"].queryset = models.Staff.objects.filter(dealer=dealer,pk=staff.pk)
form.fields["staff"].queryset = models.Staff.objects.filter(dealer=dealer,pk=request.staff.pk)
form.fields["id_car_make"].queryset = qs
form.fields["id_car_make"].choices = [
(obj.id_car_make, obj.get_local_name()) for obj in qs
@ -5998,7 +6036,7 @@ def lead_create(request,dealer_slug):
@permission_required("inventory.view_lead", raise_exception=True)
def lead_tracking(request,dealer_slug):
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
staff = models.Staff.objects.filter(dealer=dealer, staff_member__user=request.user).first()
staff = models.Staff.objects.select_related("staff_member","staff_member__user").filter(dealer=dealer, staff_member__user=request.user).first()
if staff:
qs = models.Lead.objects.filter(dealer=dealer,staff=staff)
@ -6142,7 +6180,7 @@ class LeadUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
form.fields[
"id_car_model"
].queryset = form.instance.id_car_make.carmodel_set.all()
form.fields["staff"].queryset = form.fields["staff"].queryset.filter(
form.fields["staff"].queryset = form.fields["staff"].queryset.select_related("staff_member","staff_member__user").filter(
dealer=dealer,staff_member__user__groups__permissions__codename__contains="add_lead").distinct()
return form
@ -7077,8 +7115,8 @@ class ItemServiceCreateView(
permission_required = ["inventory.add_additionalservices"]
def form_valid(self, form):
vat = models.VatRate.objects.get(is_active=True)
dealer = get_user_type(self.request)
vat = models.VatRate.objects.get(dealer=dealer,is_active=True)
form.instance.dealer = dealer
if form.instance.taxable:
form.instance.price = (form.instance.price * vat.rate) + form.instance.price
@ -7124,8 +7162,8 @@ class ItemServiceUpdateView(
def form_valid(self, form):
vat = models.VatRate.objects.get(is_active=True)
dealer = get_user_type(self.request)
vat = models.VatRate.objects.get(dealer=dealer,is_active=True)
form.instance.dealer = dealer
if form.instance.taxable:
form.instance.price = (form.instance.price * vat.rate) + form.instance.price
@ -7772,7 +7810,7 @@ class OrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
def get_queryset(self):
dealer = get_user_type(self.request)
qs = super().get_queryset()
qs = super().get_queryset().select_related("customer","estimate","invoice")
return qs.filter(estimate__entity=dealer.entity)
@ -8877,8 +8915,9 @@ class LedgerModelListView(LoginRequiredMixin,PermissionRequiredMixin, ListView,
def get_queryset(self):
qs = super().get_queryset()
dealer = get_user_type(self.request)
qs = qs.filter(entity=dealer.entity)
# dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
# dealer = get_user_type(self.request)
qs = qs.filter(entity=self.request.entity)
qs = qs.select_related("billmodel", "invoicemodel")
qs = qs.order_by("-created")
if self.show_all:
@ -8891,8 +8930,7 @@ class LedgerModelListView(LoginRequiredMixin,PermissionRequiredMixin, ListView,
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
dealer = get_user_type(self.request)
context["entity_slug"] = dealer.entity.slug
context["entity_slug"] = self.request.dealer.entity.slug
return context
@ -8939,16 +8977,14 @@ class LedgerModelCreateView(LedgerModelCreateViewBase):
permission_required = ["django_ledger.add_ledgermodel"]
def get_form(self, form_class=None):
dealer = get_user_type(self.request)
return LedgerModelCreateForm(
entity_slug=dealer.entity.slug,
user_model=dealer.entity.admin,
entity_slug=self.request.dealer.entity.slug,
user_model=self.request.entity.admin,
**self.get_form_kwargs(),
)
def form_valid(self, form):
dealer = get_user_type(self.request)
form.field["entity"] = dealer.entity
form.field["entity"] = self.request.dealer.entity
return super().form_valid(form)
def get_success_url(self):
@ -10499,12 +10535,20 @@ def upload_cars(request, dealer_slug, pk=None):
vendor=vendor,
receiving_date=receiving_date,
)
if po_item:
models.CarFinance.objects.create(
car=car,
cost_price=po_item.item.unit_cost,
marked_price=0,
selling_price=0
)
car.add_colors(exterior=exterior, interior=interior)
cars_created += 1
logger.debug(
f"User {user_username} created Car ID: {car.pk} (VIN: {car.vin}). "
f"Count: {cars_created}."
)
if po_item:
po_item.status = "uploaded"
po_item.save()

9
static/css/all.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -45,7 +45,7 @@
}
.form-control, .form-select {
text-align: center;
/* text-align: center; */
display: flex;
align-items: center;
justify-content: center;

6
static/js/fontawesome.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -26,19 +26,19 @@
<meta name="msapplication-TileImage" content="{% static 'images/logos/logo-d.png' %}">
<meta name="theme-color" content="#ffffff">
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
{% comment %} <script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script> {% endcomment %}
<script src="{% static 'js/config.js' %}"></script>
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
<link href="{% static 'vendors/mapbox-gl/mapbox-gl.css' %}" rel="stylesheet">
<link href="{% static 'vendors/swiper/swiper-bundle.min.css' %}" rel="stylesheet">
{% comment %} <link href="{% static 'vendors/mapbox-gl/mapbox-gl.css' %}" rel="stylesheet"> {% endcomment %}
{% comment %} <link href="{% static 'vendors/swiper/swiper-bundle.min.css' %}" rel="stylesheet"> {% endcomment %}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&amp;display=swap" rel="stylesheet">
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
{% comment %} <link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet"> {% endcomment %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@emran-alhaddad/saudi-riyal-font/index.css">
<link href="{% static 'vendors/flatpickr/flatpickr.min.css' %}" rel="stylesheet">
{% comment %} <link href="{% static 'vendors/flatpickr/flatpickr.min.css' %}" rel="stylesheet"> {% endcomment %}
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
{% comment %} <link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css"> {% endcomment %}
{% if LANGUAGE_CODE == 'ar' %}
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
@ -46,9 +46,9 @@
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
{% endif %}
<script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/echarts.js' %}"></script>
{% comment %} <script src="{% static 'js/main.js' %}"></script> {% endcomment %}
{% comment %} <script src="{% static 'js/jquery.min.js' %}"></script> {% endcomment %}
{% comment %} <script src="{% static 'js/echarts.js' %}"></script> {% endcomment %}
{% block customCSS %}
@ -79,9 +79,9 @@
{% include 'footer.html' %}
</div>
</main>
<script src="{% static 'js/djetler.bundle.js' %}"></script>
<script src="{% static 'js/js-utils.js' %}"></script>
<script src="{% static 'js/modal/show_modal.js' %}"></script>
{% comment %} <script src="{% static 'js/djetler.bundle.js' %}"></script>
<script src="{% static 'js/js-utils.js' %}"></script> {% endcomment %}
{% comment %} <script src="{% static 'js/modal/show_modal.js' %}"></script> {% endcomment %}
<!-- ===============================================-->
<!-- JavaScripts-->
@ -90,31 +90,30 @@
<!--1-->
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
<script src="{% static 'vendors/anchorjs/anchor.min.js' %}"></script>
<script src="{% static 'vendors/is/is.min.js' %}"></script>
{% comment %} <script src="{% static 'vendors/anchorjs/anchor.min.js' %}"></script>
<script src="{% static 'vendors/is/is.min.js' %}"></script> {% endcomment %}
<!--2-->
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
<script src="{% static 'vendors/lodash/lodash.min.js' %}"></script>
<script src="{% static 'vendors/list.js/list.min.js' %}"></script>
{% comment %} <script src="{% static 'vendors/list.js/list.min.js' %}"></script> {% endcomment %}
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
{% comment %} <script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script> {% endcomment %}
<script src="{% static 'js/phoenix.js' %}"></script>
<script src="{% static 'js/apexcharts.js' %}"></script>
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
<script src="{% static 'js/crm-analytics.js' %}"></script>
<script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
{% comment %} <script src="{% static 'js/apexcharts.js' %}"></script> {% endcomment %}
{% comment %} <script src="{% static 'vendors/echarts/echarts.min.js' %}"></script> {% endcomment %}
{% comment %} <script src="{% static 'js/crm-analytics.js' %}"></script> {% endcomment %}
{% comment %} <script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
<script src="{% static 'js/crm-dashboard.js' %}"></script>
<script src="{% static 'js/projectmanagement-dashboard.js' %}"></script>
<script src="{% static 'js/projectmanagement-dashboard.js' %}"></script> {% endcomment %}
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
<script src="{% static 'vendors/turf.min.js' %}"></script>
{% comment %} <script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script> {% endcomment %}
{% comment %} <script src="{% static 'vendors/turf.min.js' %}"></script> {% endcomment %}
<script src="{% static 'vendors/htmx.min.js' %}"></script>
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
<script src="{% static 'vendors/flatpickr/flatpickr.min.js' %}"></script>
{% comment %} <script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
<script src="{% static 'vendors/flatpickr/flatpickr.min.js' %}"></script> {% endcomment %}
<script>
{% if entity_slug %}
let entitySlug = "{{ view.kwargs.entity_slug }}"
{% endif %}

View File

@ -1,5 +1,5 @@
{% extends 'base.html' %}
{% load i18n static custom_filters%}
{% load i18n static custom_filters crispy_forms_filters %}
{%block title%}{%trans 'Profile'%} {%endblock%}
{% block content %}
<div class="container-fluid">
@ -180,6 +180,26 @@
</div>
</div>
</div>
<div class="col-12 col-lg-6">
<div class="card h-100">
<div class="bg-holder" style="background-image:url({% static 'images/bg/bg-left-20.png' %});background-position:left bottom;background-size:auto;"></div>
<div class="card-body d-flex flex-column justify-content-center position-relative">
<h4 class="mb-3">{{ _("VAT") }}</h4>
<div class="d-flex justify-content-center ">
<div class="text-center me-3">
<div class="row">
<form action="{% url 'dealer_vat_rate_update' request.dealer.slug %}" method="post">
{% csrf_token %}
{{vatform|crispy}}
<button class="btn btn-sm btn-phoenix-primary" type="submit">{% trans 'Update' %}</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,5 +1,6 @@
{% extends "base.html" %}
{% load i18n custom_filters%}
{% load crispy_forms_filters %}
{% block title %}{{ _("View Quotation") }}{% endblock title %}
@ -191,15 +192,22 @@
</tr>
{% endfor %}
<tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Vat" %} ({{data.vat|percentage}})</td>
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Vat" %} ({{data.vat}})</td>
<td class="align-middle text-start fw-semibold">
<span id="grand-total">+ {{data.total_vat_amount|floatformat}}<span class="icon-saudi_riyal"></span></span>
</td>
</tr>
<tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Discount Amount" %}</td>
<td class="align-middle text-start text-danger fw-semibold ">
<span id="grand-total">- {{data.total_discount|floatformat}}<span class="icon-saudi_riyal"></span></span>
<td class="align-middle text-start text-danger fw-semibold">
<form action="{% url 'update_estimate_discount' request.dealer.slug estimate.pk %}" method="post">
{% csrf_token %}
<div class="input-group input-group-sm">
<input type="number" class="form-control" name="discount_amount" value="{{data.total_discount}}" step="0.01" style="width: 1px;">
<span class="input-group-text"><span class="icon-saudi_riyal"></span></span>
<button type="submit" class="btn btn-sm btn-phoenix-primary ms-n2">{% trans "Update" %}</button>
</div>
</form>
</td>
</tr>
<tr class="bg-body-secondary total-sum">
@ -208,6 +216,7 @@
{% for service in data.additionals %}
<small><span class="fw-semibold">+ {{service.name}} - {{service.price_|floatformat}}<span class="icon-saudi_riyal"></span></span></small><br>
{% endfor %}
<button class="btn btn-phoenix-primary btn-xs ms-auto" type="button" data-bs-toggle="modal" data-bs-target="#additionalModal"><span class="fas fa-plus me-1"></span>{{ _("Add") }}</button>
</td>
</tr>
<tr class="bg-body-secondary total-sum">
@ -226,6 +235,27 @@
</section>
<!-- <section> close ============================-->
<!-- ============================================-->
<!-- add update Modal -->
<div class="modal fade" id="additionalModal" tabindex="-1" aria-labelledby="additionalModalLabel" aria-hidden="true">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header justify-content-between align-items-start gap-5 px-4 pt-4 pb-3 border-0">
<h4 class="modal-title" id="additionalModalLabel">{% trans 'Additional Services' %}</h4>
<button class="btn p-0 text-body-quaternary fs-6" data-bs-dismiss="modal" aria-label="Close">
<span class="fas fa-times"></span>
</button>
</div>
<div class="modal-body">
<form action="{% url 'update_estimate_additionals' request.dealer.slug estimate.pk %}" method="post">
{% csrf_token %}
{{additionals_form|crispy}}
<button type="submit" class="btn btn-phoenix-primary">{% trans 'Update' %}</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block customJS %}

View File

@ -39,6 +39,8 @@
{{ form.arabic_name|as_crispy_field }}
{{ form.email|as_crispy_field }}
{{ form.phone_number|as_crispy_field }}
{{ form.address|as_crispy_field }}
{{ form.image|as_crispy_field }}
{{ form.group|as_crispy_field }}
{% for error in form.errors %}
<div class="text-danger">{{ error }}</div>

View File

@ -28,21 +28,21 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
<link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet">
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
{% comment %} <link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet"> {% endcomment %}
{% comment %} <link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet"> {% endcomment %}
{% comment %} <link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css"> {% endcomment %}
{% if LANGUAGE_CODE == 'en' %}
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
{% else %}
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
{% endif %}
{% if LANGUAGE_CODE == 'ar' %}
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
{% else %}
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
{% endif %}
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
<script src="{% static 'js/config.js' %}"></script>
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
{% comment %} <script src="{% static 'js/sweetalert2.all.min.js' %}"></script> {% endcomment %}
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
</head>
@ -52,9 +52,27 @@
{% block content %}
{% endblock content %}
{% comment %}
<script src="{% static 'vendors/anchorjs/anchor.min.js' %}"></script>
<script src="{% static 'vendors/is/is.min.js' %}"></script>
<script src="{% static 'vendors/list.js/list.min.js' %}"></script>
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
<script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script> {% endcomment %}
<script src="{% static 'vendors/popper/popper.min.js' %}"></script>
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
<script src="{% static 'vendors/lodash/lodash.min.js' %}"></script>
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
<script src="{% static 'vendors/typed.js/typed.umd.js' %}"></script>
<script src="{% static 'js/phoenix.js' %}"></script>
{% comment %} <script src="{% static 'vendors/popper/popper.min.js' %}"></script>
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
<script src="{% static 'vendors/anchorjs/anchor.min.js' %}"></script>
<script src="{% static 'vendors/is/is.min.js' %}"></script>
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
@ -68,7 +86,7 @@
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
<script src="{% static 'vendors/typed.js/typed.umd.js' %}"></script>
<script src="{% static 'vendors/typed.js/typed.umd.js' %}"></script> {% endcomment %}
{% block customJS %}
{% endblock customJS %}