diff --git a/inventory/forms.py b/inventory/forms.py index e314eea2..5235df73 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -8,7 +8,7 @@ from django import forms from plans.models import PlanPricing from django.contrib.auth import get_user_model -from inventory.validators import SaudiPhoneNumberValidator +from inventory.validators import SaudiPhoneNumberValidator, vat_rate_validator from .models import CustomGroup, Status, Stage from .mixins import AddClassMixin from django_ledger.forms.invoice import ( @@ -2130,28 +2130,16 @@ class AdditionalFinancesForm(forms.Form): class VatRateForm(forms.ModelForm): rate = forms.DecimalField( + label="VAT Rate", max_digits=5, decimal_places=2, - min_value=0, - max_value=100, - label=_("VAT Rate (%)"), - help_text=_("Enter VAT rate as percentage (e.g., 0.15 for 15%)") + validators=[vat_rate_validator] ) class Meta: model = VatRate fields = ["rate"] - def clean_rate(self): - rate = self.cleaned_data['rate'] - return rate / 100 - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if self.instance and self.instance.pk: - self.fields['rate'].initial = self.instance.rate * 100 - - class CustomSetPasswordForm(SetPasswordForm): new_password1 = forms.CharField( label="New Password", diff --git a/inventory/validators.py b/inventory/validators.py index 777a8391..791df1dc 100644 --- a/inventory/validators.py +++ b/inventory/validators.py @@ -1,5 +1,6 @@ from django.core.validators import RegexValidator from django.utils.translation import gettext_lazy as _ +from django.core.exceptions import ValidationError import re @@ -14,3 +15,10 @@ class SaudiPhoneNumberValidator(RegexValidator): # Remove any whitespace, dashes, or other separators cleaned_value = re.sub(r"[\s\-\(\)\.]", "", str(value)) super().__call__(cleaned_value) + +def vat_rate_validator(value): + if value < 0 or value > 1: + raise ValidationError( + _('%(value)s is not a valid VAT rate. It must be between 0 and 1.'), + params={'value': value}, + ) \ No newline at end of file diff --git a/inventory/views.py b/inventory/views.py index 0f1cb2ec..d053a04c 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -2336,21 +2336,28 @@ class DealerDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): return context +from .forms import VatRateForm +@login_required def dealer_vat_rate_update(request, slug): dealer = get_object_or_404(models.Dealer, slug=slug) - vat_rate, created = models.VatRate.objects.get_or_create(dealer=dealer) + + vat_rate_instance, created = models.VatRate.objects.get_or_create(dealer=dealer) - form = forms.VatRateForm(request.POST, instance=vat_rate) - - if form.is_valid(): - form.save() - messages.success(request, _("VAT rate updated successfully")) - else: - logger.error(form.errors) - messages.error(request, _("VAT rate update failed: %s") % form.errors.as_text()) - - return redirect("dealer_detail", slug=slug) + if request.method == "POST": + + form = VatRateForm(request.POST, instance=vat_rate_instance) + + if form.is_valid(): + form.save() + messages.success(request, _("VAT rate updated successfully")) + return redirect("dealer_detail", slug=slug) + else: + + messages.error(request, _("Please enter valid vat rate between 0 and 1.")) + redirect("dealer_detail", slug=slug) + return redirect("dealer_detail", slug=slug) + class DealerUpdateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView @@ -2408,11 +2415,6 @@ class StaffDetailView(LoginRequiredMixin, DetailView): -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")) - messages.success(request, _("VAT rate updated successfully")) - return redirect("dealer_detail", slug=slug) class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): diff --git a/static/images/car_images/29e73964e0b1d9d4c15581e2bb2635518d0e859b8662e6c046cff9b24f8053c0.png b/static/images/car_images/29e73964e0b1d9d4c15581e2bb2635518d0e859b8662e6c046cff9b24f8053c0.png new file mode 100644 index 00000000..e213d619 Binary files /dev/null and b/static/images/car_images/29e73964e0b1d9d4c15581e2bb2635518d0e859b8662e6c046cff9b24f8053c0.png differ diff --git a/staticfiles/images/car_images/29e73964e0b1d9d4c15581e2bb2635518d0e859b8662e6c046cff9b24f8053c0.png b/staticfiles/images/car_images/29e73964e0b1d9d4c15581e2bb2635518d0e859b8662e6c046cff9b24f8053c0.png new file mode 100644 index 00000000..e213d619 Binary files /dev/null and b/staticfiles/images/car_images/29e73964e0b1d9d4c15581e2bb2635518d0e859b8662e6c046cff9b24f8053c0.png differ diff --git a/templates/crm/leads/lead_detail.html b/templates/crm/leads/lead_detail.html index a52d691f..263d2024 100644 --- a/templates/crm/leads/lead_detail.html +++ b/templates/crm/leads/lead_detail.html @@ -804,6 +804,8 @@ + + {% include 'modal/delete_modal.html' %} {% include "components/email_modal.html" %} diff --git a/templates/crm/opportunities/opportunity_form.html b/templates/crm/opportunities/opportunity_form.html index 4eb2fcef..f264fdd4 100644 --- a/templates/crm/opportunities/opportunity_form.html +++ b/templates/crm/opportunities/opportunity_form.html @@ -1,7 +1,6 @@ {% extends 'base.html' %} {% load i18n static widget_tweaks custom_filters %} {% block title %} - {# Check if an 'object' exists in the context #} {% if object %} {% trans 'Update Opportunity' %} {% else %} @@ -27,99 +26,112 @@ +