Compare commits

...

5 Commits

12 changed files with 83 additions and 48 deletions

View File

@ -46,3 +46,19 @@ application = ProtocolTypeRouter(
), ),
} }
) )
try:
from django.conf import settings
from django.contrib.sites.models import Site
if not settings.DEBUG:
site = Site.objects.get(id=settings.SITE_ID)
if site.domain != settings.PRODUCTION_DOMAIN:
site.domain = settings.PRODUCTION_DOMAIN
site.name = settings.SITE_NAME
site.save()
except Exception as e:
# Log error but don't crash the app
if settings.DEBUG:
print(f"Site configuration error in WSGI: {e}")

View File

@ -43,14 +43,14 @@ class Command(BaseCommand):
) )
# Assign quotas to plans # Assign quotas to plans
PlanQuota.objects.create(plan=basic_plan, quota=users_quota, value=99999) PlanQuota.objects.create(plan=basic_plan, quota=users_quota, value=10000000)
PlanQuota.objects.create(plan=basic_plan, quota=cars_quota, value=99999) PlanQuota.objects.create(plan=basic_plan, quota=cars_quota, value=10000000)
PlanQuota.objects.create(plan=pro_plan, quota=users_quota, value=99999) PlanQuota.objects.create(plan=pro_plan, quota=users_quota, value=10000000)
PlanQuota.objects.create(plan=pro_plan, quota=cars_quota, value=99999) PlanQuota.objects.create(plan=pro_plan, quota=cars_quota, value=10000000)
PlanQuota.objects.create(plan=enterprise_plan, quota=users_quota, value=99999) PlanQuota.objects.create(plan=enterprise_plan, quota=users_quota, value=10000000)
PlanQuota.objects.create(plan=enterprise_plan, quota=cars_quota, value=99999) PlanQuota.objects.create(plan=enterprise_plan, quota=cars_quota, value=10000000)
# PlanQuota.objects.create(plan=pro_plan, quota=project_quota, value=50) # PlanQuota.objects.create(plan=pro_plan, quota=project_quota, value=50)
# PlanQuota.objects.create(plan=pro_plan, quota=storage_quota, value=100) # PlanQuota.objects.create(plan=pro_plan, quota=storage_quota, value=100)
@ -61,13 +61,13 @@ class Command(BaseCommand):
enterprise_pricing = Pricing.objects.create(name="1 Year", period=365) enterprise_pricing = Pricing.objects.create(name="1 Year", period=365)
PlanPricing.objects.create( PlanPricing.objects.create(
plan=basic_plan, pricing=basic_pricing, price=Decimal("2500.00") plan=basic_plan, pricing=basic_pricing, price=Decimal("2997.00")
) )
PlanPricing.objects.create( PlanPricing.objects.create(
plan=pro_plan, pricing=pro_pricing, price=Decimal("4500.00") plan=pro_plan, pricing=pro_pricing, price=Decimal("5395.00")
) )
PlanPricing.objects.create( PlanPricing.objects.create(
plan=enterprise_plan, pricing=enterprise_pricing, price=Decimal("8500.00") plan=enterprise_plan, pricing=enterprise_pricing, price=Decimal("9590.00")
) )
# # Create quotas # # Create quotas

View File

@ -169,8 +169,7 @@ class DealerSlugMiddleware:
"/ar/help_center/", "/ar/help_center/",
"/en/help_center/", "/en/help_center/",
] ]
print("------------------------------------")
print(request.path in paths)
if request.path in paths: if request.path in paths:
return None return None

View File

@ -1001,15 +1001,25 @@ def car_created_notification(sender, instance, created, **kwargs):
if created: if created:
accountants = ( accountants = (
models.CustomGroup.objects.filter( models.CustomGroup.objects.filter(
dealer=instance.dealer, name__in=["Manager", "Accountant"] dealer=instance.dealer, name__in=["Accountant"]
) )
.first() .first()
.group.user_set.all() .group.user_set.all()
.distinct() .distinct()
) )
for accountant in accountants: managers = (
models.CustomGroup.objects.filter(
dealer=instance.dealer, name__in=["Manager"]
)
.first()
.group.user_set.all()
.distinct()
)
recipients = accountants.union(managers)
for recipient in recipients:
models.Notification.objects.create( models.Notification.objects.create(
user=accountant, user=recipient,
message=_( message=_(
""" """
New Car {car_make}-{car_model}-{year}-{vin} has been added to the inventory. New Car {car_make}-{car_model}-{year}-{vin} has been added to the inventory.

View File

@ -1924,12 +1924,15 @@ def handle_payment(request, order):
"last_name": last_name, "last_name": last_name,
"phone": phone, "phone": phone,
} }
total = int(round(order.total())) * 100 try:
total = int((order.total() + order.tax * order.total() / 100) * 100)
except (AttributeError, TypeError):
raise ValueError("Order total or tax is invalid")
payload = json.dumps( payload = json.dumps(
{ {
"amount": total, "amount": total,
"currency": "SAR", "currency": "SAR",
"description": f"payment issued for {'email'}", "description": f"payment issued for {email}",
"callback_url": callback_url, "callback_url": callback_url,
"source": { "source": {
"type": "creditcard", "type": "creditcard",

View File

@ -52,7 +52,7 @@ from django.forms import CharField, HiddenInput, ValidationError
from django.shortcuts import HttpResponse from django.shortcuts import HttpResponse
from django.db.models import Sum, F, Count from django.db.models import Sum, F, Count
from django.db.models.functions import ExtractMonth from django.db.models.functions import ExtractMonth,Round
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.db.models import Value from django.db.models import Value
@ -690,6 +690,7 @@ def sales_dashboard(request,dealer_slug):
created__date__gte=start_date, created__date__gte=start_date,
created__date__lte=end_date created__date__lte=end_date
) )
total_leads=leads_filtered.count()
# ---------------------------------------------------- # ----------------------------------------------------
@ -747,6 +748,7 @@ def sales_dashboard(request,dealer_slug):
'total_new_cars_in_inventory': total_new_cars_in_inventory, 'total_new_cars_in_inventory': total_new_cars_in_inventory,
'total_used_cars_in_inventory': total_used_cars_in_inventory, 'total_used_cars_in_inventory': total_used_cars_in_inventory,
'aging_inventory_count': aging_inventory_count, 'aging_inventory_count': aging_inventory_count,
'total_leads':total_leads
} }
return render(request, 'dashboards/sales_dashboard.html', context) return render(request, 'dashboards/sales_dashboard.html', context)
@ -9734,8 +9736,12 @@ def ledger_unpost_all_journals(request, dealer_slug, entity_slug, pk):
@permission_required("inventory.change_dealer", raise_exception=True) @permission_required("inventory.change_dealer", raise_exception=True)
def pricing_page(request, dealer_slug): def pricing_page(request, dealer_slug):
dealer=get_object_or_404(models.Dealer, slug=dealer_slug) dealer=get_object_or_404(models.Dealer, slug=dealer_slug)
vat = models.VatRate.objects.filter(dealer=dealer).first()
if not dealer.active_plan: if not dealer.active_plan:
plan_list = PlanPricing.objects.all() plan_list = PlanPricing.objects.annotate(
price_with_tax=Round(F('price') * vat.rate + F('price'), 2)
).all()
form = forms.PaymentPlanForm() form = forms.PaymentPlanForm()
return render(request, "pricing_page.html", {"plan_list": plan_list, "form": form}) return render(request, "pricing_page.html", {"plan_list": plan_list, "form": form})
else: else:
@ -9784,7 +9790,6 @@ def payment_callback(request, dealer_slug):
logger.info(f"Received payment callback for dealer_slug: {dealer_slug}, payment_id: {payment_id}, status: {payment_status}") logger.info(f"Received payment callback for dealer_slug: {dealer_slug}, payment_id: {payment_id}, status: {payment_status}")
order = Order.objects.filter(user=dealer.user, status=1).first() # Status 1 = NEW order = Order.objects.filter(user=dealer.user, status=1).first() # Status 1 = NEW
print(order)
if payment_status == "paid": if payment_status == "paid":
logger.info(f"Payment successful for transaction ID {payment_id}. Processing order completion.") logger.info(f"Payment successful for transaction ID {payment_id}. Processing order completion.")
@ -9805,10 +9810,11 @@ def payment_callback(request, dealer_slug):
logger.debug(f"Billing info already exists for user {dealer.user}.") logger.debug(f"Billing info already exists for user {dealer.user}.")
if not hasattr(order.user, 'userplan'): if not hasattr(order.user, 'userplan'):
print(order.get_plan_pricing().pricing.period)
UserPlan.objects.create( UserPlan.objects.create(
user=order.user, user=order.user,
plan=order.plan, plan=order.plan,
expire=datetime.now().date() + timedelta(days=order.get_plan_pricing().pricing.period + 30) expire=datetime.now().date() + timedelta(days=order.get_plan_pricing().pricing.period)
) )
logger.info(f"Created new UserPlan for user {order.user} with plan {order.plan}.") logger.info(f"Created new UserPlan for user {order.user} with plan {order.plan}.")
else: else:

View File

@ -305,7 +305,7 @@
return true return true
} }
function validate_sa_phone_number(phone_number) { function validate_sa_phone_number(phone_number) {
const phone_numberRegex = /^05[0-10]{7}$/; const phone_numberRegex = /^05[0-9]{8}$/;
return phone_numberRegex.test(phone_number) && phone_numberRegex !== ''; return phone_numberRegex.test(phone_number) && phone_numberRegex !== '';
} }
function getAllFormData() { function getAllFormData() {
@ -372,11 +372,7 @@
hideLoading(); hideLoading();
const data = await response.json(); const data = await response.json();
if (response.ok) { if (response.ok) {
<<<<<<< HEAD
notify("success","{% trans 'Account created successfully'%}");
=======
notify("success","Account created successfully"); notify("success","Account created successfully");
>>>>>>> d3dcb85fa378e156b77550e8ab833ad10ffad51f
setTimeout(() => { setTimeout(() => {
window.location.href = "{% url 'account_login' %}"; window.location.href = "{% url 'account_login' %}";
}, 1000); }, 1000);

View File

@ -77,7 +77,7 @@
<div class="col-md-6"> <div class="col-md-6">
<div class="card h-100 shadow-sm border-0"> <div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0"> <div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">{% trans "Top Lead Sources" %}</h5> <h5 class="fw-bold mb-0 text-dark">{% trans "Top Lead Sources" %}&nbsp;&nbsp;&nbsp;&nbsp;{% trans "Total Leads: " %}{{total_leads}}</h5>
</div> </div>
<div class="card-body d-flex align-items-center justify-content-center" <div class="card-body d-flex align-items-center justify-content-center"
style="height: 400px"> style="height: 400px">

View File

@ -269,6 +269,7 @@
</tr> </tr>
<tr> <tr>
{% if perms.inventory.add_car%}
<td colspan="2"> <td colspan="2">
{% if not car.get_transfer %} {% if not car.get_transfer %}
<a href="{% url 'car_finance_update' request.dealer.slug car.slug %}" <a href="{% url 'car_finance_update' request.dealer.slug car.slug %}"
@ -277,11 +278,14 @@
<span class="badge bg-danger">{% trans "Cannot Edit, Car in Transfer." %}</span> <span class="badge bg-danger">{% trans "Cannot Edit, Car in Transfer." %}</span>
{% endif %} {% endif %}
</td> </td>
{% endif %}
</tr> </tr>
{% else %} {% else %}
{% if perms.inventory.add_car%}
<p>{% trans "No finance details available." %}</p> <p>{% trans "No finance details available." %}</p>
<a href="{% url 'car_finance_update' request.dealer.slug car.slug %}" <a href="{% url 'car_finance_update' request.dealer.slug car.slug %}"
class="btn btn-phoenix-success btn-sm mb-3">{% trans "Add" %}</a> class="btn btn-phoenix-success btn-sm mb-3">{% trans "Add" %}</a>
{% endif %}
{% endif %} {% endif %}
</table> </table>
</div> </div>

View File

@ -98,7 +98,6 @@
<th scope="col">{% trans 'Status' %}</th> <th scope="col">{% trans 'Status' %}</th>
<th scope="col">{% trans 'PO Amount' %}</th> <th scope="col">{% trans 'PO Amount' %}</th>
<th scope="col">{% trans 'Date Fulfilled' %}</th> <th scope="col">{% trans 'Date Fulfilled' %}</th>
<th scope="col">{% trans 'Created By' %}</th>
<th scope="col">{% trans 'Cars Purchased' %}</th> <th scope="col">{% trans 'Cars Purchased' %}</th>
<th scope="col">{% trans 'Vendor' %}</th> <th scope="col">{% trans 'Vendor' %}</th>
</tr> </tr>
@ -117,7 +116,6 @@
{% trans 'Not fulfilled' %} {% trans 'Not fulfilled' %}
{% endif %} {% endif %}
</td> </td>
<td>{% firstof po.created_by.get_full_name 'staff' %}</td>
<td>{{ po.po_quantity }}</td> <td>{{ po.po_quantity }}</td>
<td>{{ po.vendors_str }}</td> <td>{{ po.vendors_str }}</td>
</tr> </tr>

View File

@ -100,7 +100,7 @@
id="plan_{{ forloop.counter }}" id="plan_{{ forloop.counter }}"
value="{{ pp.id }}" value="{{ pp.id }}"
data-name="{{ pp.plan.name }}" data-name="{{ pp.plan.name }}"
data-price="{{ pp.price }}" data-price="{{ pp.price_with_tax }}"
autocomplete="off" autocomplete="off"
{% if forloop.first %}checked{% endif %}> {% if forloop.first %}checked{% endif %}>
<label class="btn w-100 p-0 pricing-card" for="plan_{{ forloop.counter }}"> <label class="btn w-100 p-0 pricing-card" for="plan_{{ forloop.counter }}">
@ -108,7 +108,7 @@
<div class="card-body p-4"> <div class="card-body p-4">
<h4 class="mb-3">{{ pp.plan.name|capfirst }}</h4> <h4 class="mb-3">{{ pp.plan.name|capfirst }}</h4>
<h5 class="mb-4"> <h5 class="mb-4">
{{ pp.price }} <span class="icon-saudi_riyal"></span><span class="fs-6 fw-normal">/ {{ pp.pricing.period }}</span> {% trans "days" %} {{pp.price_with_tax}} <span class="icon-saudi_riyal"></span><span class="fs-6 fw-normal">/ {{ pp.pricing.period }}</span> {% trans "days" %}
</h5> </h5>
<h5>{{ _("Include Haikal's") }}</h5> <h5>{{ _("Include Haikal's") }}</h5>
<ul class="fa-ul ps-3"> <ul class="fa-ul ps-3">
@ -146,8 +146,8 @@
id="first_name" id="first_name"
class="form-control form-control-sm" class="form-control form-control-sm"
required required
placeholder="{{ _("First Name") }}" placeholder='{{ _("First Name") }}'
value="{{ request.user.first_name }}"> value="{{ request.dealer.first_name }}">
</div> </div>
<label class="form-label" for="last_name">{{ _("Last Name") }}</label> <label class="form-label" for="last_name">{{ _("Last Name") }}</label>
<div class="input-group"> <div class="input-group">
@ -157,8 +157,8 @@
id="last_name" id="last_name"
class="form-control form-control-sm" class="form-control form-control-sm"
required required
placeholder="{{ _("Last Name") }}" placeholder='{{ _("Last Name") }}'
value="{{ request.user.last_name }}"> value="{{ request.dealer.last_name }}">
</div> </div>
<label class="form-label" for="email">{{ _("Email Address") }}</label> <label class="form-label" for="email">{{ _("Email Address") }}</label>
<div class="input-group"> <div class="input-group">
@ -169,7 +169,7 @@
class="form-control form-control-sm" class="form-control form-control-sm"
required required
placeholder="email@example.com" placeholder="email@example.com"
value="{{ request.user.email }}"> value="{{ request.dealer.email }}">
</div> </div>
<label class="form-label" for="phone">{{ _("Phone Number") }}</label> <label class="form-label" for="phone">{{ _("Phone Number") }}</label>
<div class="input-group"> <div class="input-group">
@ -179,19 +179,22 @@
id="phone" id="phone"
class="form-control form-control-sm" class="form-control form-control-sm"
dir="ltr" dir="ltr"
placeholder="{{ _("Phone Number") }}" maxlength="10"
value="{{ request.dealer.phone_number.raw_input }}" pattern="^05\d{8}$"
inputmode="numeric"
placeholder='{{ _("Phone Number") }}'
value="{{ request.dealer.phone_number }}"
required> required>
</div> </div>
<label class="form-label" for="company">{{ _("Company") }}</label> <label class="form-label" for="company">{{ _("Company") }}</label>
<div class="input-group"> <div class="input-group">
<span class="input-group-text"><i class="fas fa-building"></i></span> <span class="input-group-text"><i class="fas fa-building"></i></span>
<input type="text" <input type="text"
name="company" name="company"
id="company" id="company"
class="form-control form-control-sm" class="form-control form-control-sm"
placeholder="{{ _("Company") }}" placeholder='{{ _("Company") }}'
value="{{ request.dealer.get_local_name }}"> value="{{ request.dealer.name }}">
</div> </div>
</div> </div>
</div> </div>
@ -212,7 +215,7 @@
name="card_name" name="card_name"
id="card_name" id="card_name"
class="form-control form-control-sm" class="form-control form-control-sm"
placeholder="{{ _("Cardholder Name") }}" placeholder='{{ _("Cardholder Name") }}'
required> required>
<div class="invalid-feedback" id="card_name_feedback"> <div class="invalid-feedback" id="card_name_feedback">
{{ _("Please enter the cardholder name") }} {{ _("Please enter the cardholder name") }}
@ -225,7 +228,7 @@
name="card_number" name="card_number"
id="card_number" id="card_number"
class="form-control form-control-sm" class="form-control form-control-sm"
placeholder="{{ _("Card Number") }}" placeholder='{{ _("Card Number") }}'
maxlength="19" maxlength="19"
pattern="^\d{4}\s\d{4}\s\d{4}\s\d{4}$" pattern="^\d{4}\s\d{4}\s\d{4}\s\d{4}$"
inputmode="numeric" inputmode="numeric"
@ -242,7 +245,7 @@
name="card_expiry" name="card_expiry"
id="card_expiry" id="card_expiry"
class="form-control form-control-sm" class="form-control form-control-sm"
placeholder="{{ _("Expiry Date") }}" placeholder='{{ _("Expiry Date") }}'
maxlength="5" maxlength="5"
pattern="^(0[1-9]|1[0-2])\/\d{2}$" pattern="^(0[1-9]|1[0-2])\/\d{2}$"
inputmode="numeric" inputmode="numeric"
@ -259,7 +262,7 @@
name="card_cvv" name="card_cvv"
id="card_cvv" id="card_cvv"
class="form-control form-control-sm" class="form-control form-control-sm"
placeholder="{{ _("CVV") }}" placeholder='{{ _("CVV") }}'
maxlength="3" maxlength="3"
pattern="^\d{3}$" pattern="^\d{3}$"
inputmode="numeric" inputmode="numeric"

View File

@ -122,9 +122,9 @@
<div class="row d-flex justify-content-center align-items-center mt-5 mb-3 ms-6 ps-3"> <div class="row d-flex justify-content-center align-items-center mt-5 mb-3 ms-6 ps-3">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
{% if not items %} {% if not items.car %}
{% url "car_add" request.dealer.slug as create_car_url %} {% url "car_add" request.dealer.slug as create_car_url %}
{% include "message-illustration.html" with value1="Please add at least one car before creating a quotation." value2="Add car" message_image="images/logos/no-content-new.jpg" url=create_car_url %} {% include "message-illustration.html" with value1="Please add at least one car or complete the car info before creating a quotation." value2="Add car" message_image="images/logos/no-content-new.jpg" url=create_car_url %}
{% endif %} {% endif %}
</div> </div>
<div class="col"> <div class="col">