diff --git a/inventory/forms.py b/inventory/forms.py index e4861464..ec7ac101 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 ( @@ -2120,11 +2120,17 @@ class AdditionalFinancesForm(forms.Form): class VatRateForm(forms.ModelForm): + rate = forms.DecimalField( + label="VAT Rate", + max_digits=5, + decimal_places=2, + validators=[vat_rate_validator] + ) + class Meta: model = VatRate fields = ["rate"] - 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 8187c6f4..928792a3 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -2336,12 +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) - 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) + + vat_rate_instance, created = models.VatRate.objects.get_or_create(dealer=dealer) + 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 @@ -2399,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 @@ +
{% csrf_token %} - {% if form.non_field_errors %}
{{ form.non_field_errors }}
{% endif %} - -
- - {{ form.lead|add_class:"form-control" }} - {% if form.lead.errors %}
{{ form.lead.errors }}
{% endif %} -
- -
- - {{ form.car|add_class:"form-control" }} - {% if form.car.errors %}
{{ form.car.errors }}
{% endif %} -
- -
- - {{ form.stage|add_class:"form-control" }} - {% if form.stage.errors %}
{{ form.stage.errors }}
{% endif %} -
- - {% comment %}
- -
- - {{ form.amount|add_class:"form-control" }} + {% if form.non_field_errors %} +
{{ form.non_field_errors }}
+ {% endif %} + +
{% trans "Main Information" %}
+
+
+ + {{ form.lead|add_class:"form-control form-select" }} + {% if form.lead.errors %}
{{ form.lead.errors }}
{% endif %}
- {% if form.amount.errors %}
{{ form.amount.errors }}
{% endif %} -
{% endcomment %} - - {% comment %}
- -
- - - {{ form.probability.value|default:'50' }}% - +
+ + {{ form.car|add_class:"form-control form-select" }} + {% if form.car.errors %}
{{ form.car.errors }}
{% endif %} +
+
+ + {{ form.stage|add_class:"form-control form-select" }} + {% if form.stage.errors %}
{{ form.stage.errors }}
{% endif %} +
+
+ +
+ {{ form.expected_close_date|add_class:"form-control" }} + +
+ {% if form.expected_close_date.errors %} +
{{ form.expected_close_date.errors }}
+ {% endif %}
- {% if form.probability.errors %}
{{ form.probability.errors }}
{% endif %}
- -
- -
- - {{ form.expected_revenue|add_class:"form-control" }} -
- {% if form.expected_revenue.errors %} -
{{ form.expected_revenue.errors }}
- {% endif %} -
{% endcomment %} - + {% comment %}
- -
- {{ form.expected_close_date|add_class:"form-control" }} - +
{% trans "Financial Information" %}
+
+
+ +
+ SAR + {{ form.amount|add_class:"form-control border-start-0" }} +
+ {% if form.amount.errors %} +
{{ form.amount.errors }}
+ {% endif %} +
+
+ +
+ + + {{ form.probability.value|default:'50' }}% + +
+ {% if form.probability.errors %} +
{{ form.probability.errors }}
+ {% endif %} +
+
+ +
+ SAR + {{ form.expected_revenue|add_class:"form-control" }} +
+ {% if form.expected_revenue.errors %} +
{{ form.expected_revenue.errors }}
+ {% endif %} +
- {% if form.expected_close_date.errors %} -
{{ form.expected_close_date.errors }}
- {% endif %}
- -
+ {% endcomment %} +
@@ -135,17 +147,20 @@
+

{% trans "Opportunity Guidelines" %}

- -{% endblock %} + +{% endblock %} \ No newline at end of file diff --git a/templates/inventory/car_detail.html b/templates/inventory/car_detail.html index 7cc24ee3..4b8b0c68 100644 --- a/templates/inventory/car_detail.html +++ b/templates/inventory/car_detail.html @@ -262,7 +262,11 @@ {% if car.marked_price %} {% trans "Cost Price"|capfirst %} + {% if request.is_dealer or request.is_accountant or request.manager%} {{ car.cost_price|floatformat:2 }} + {% else %} + {{ car.cost_price|floatformat:2 }} + {% endif %} {% trans "Marked Price"|capfirst %} @@ -270,7 +274,7 @@ - {% if perms.django_ledger.view_ledgermodel %} + {% if perms.django_ledger.change_ledgermodel %} {% if not car.get_transfer %} - {% trans "Save and Add Another" %} + {% trans "Add to Inventory" %} - + {% endcomment %}