diff --git a/inventory/forms.py b/inventory/forms.py index bc9b43ac..35d6e9c6 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -930,4 +930,6 @@ class DealerSettingsForm(forms.ModelForm): class Meta: model = DealerSettings fields = "__all__" - \ No newline at end of file + +class LeadTransferForm(forms.Form): + transfer_to = forms.ModelChoiceField(label="to",queryset=Staff.objects.all()) \ No newline at end of file diff --git a/inventory/migrations/0049_alter_lead_status_alter_leadstatushistory_new_status_and_more.py b/inventory/migrations/0049_alter_lead_status_alter_leadstatushistory_new_status_and_more.py new file mode 100644 index 00000000..b611f7cf --- /dev/null +++ b/inventory/migrations/0049_alter_lead_status_alter_leadstatushistory_new_status_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.17 on 2025-02-26 08:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0048_remove_dealersettings_bill_payable_account_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='lead', + name='status', + field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], db_index=True, default='new', max_length=50, verbose_name='Status'), + ), + migrations.AlterField( + model_name='leadstatushistory', + name='new_status', + field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], max_length=50, verbose_name='New Status'), + ), + migrations.AlterField( + model_name='leadstatushistory', + name='old_status', + field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], max_length=50, verbose_name='Old Status'), + ), + migrations.AlterField( + model_name='opportunity', + name='status', + field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], default='new', max_length=20, verbose_name='Status'), + ), + ] diff --git a/inventory/models.py b/inventory/models.py index 8c3ea81e..9b5894c5 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -15,6 +15,8 @@ from django_ledger.io.io_core import get_localdate from django.core.exceptions import ValidationError from phonenumber_field.modelfields import PhoneNumberField from django.utils.timezone import now + +from inventory.utils import get_user_type from .mixins import LocalizedNameMixin from django_ledger.models import EntityModel, ItemModel,EstimateModel,InvoiceModel,AccountModel from django.contrib.contenttypes.fields import GenericForeignKey @@ -443,11 +445,12 @@ class Car(models.Model): hash_object.update(f"{self.id_car_make.name}{self.id_car_model.name}{self.year}{self.id_car_serie.name}{self.id_car_trim.name}{color}".encode('utf-8')) return hash_object.hexdigest() - def mark_as_sold(self,user): + def mark_as_sold(self,request): + dealer = get_user_type(request) self.cancel_reservation() - self.status = CarStatusChoices.SOLD + self.status = CarStatusChoices.SOLD self.save() - Activity.objects.create(content_object=self, notes="Car Sold",created_by=user,activity_type=ActionChoices.SALE_CAR) + Activity.objects.create(dealer=dealer,content_object=self, notes="Car Sold",created_by=request.user,activity_type=ActionChoices.SALE_CAR) def cancel_reservation(self): if self.reservations.exists(): @@ -997,6 +1000,8 @@ class Status(models.TextChoices): PENDING = "pending", _("Pending") IN_PROGRESS = "in_progress", _("In Progress") QUALIFIED = "qualified", _("Qualified") + CONTACTED = "contacted", _("Contacted") + CONVERTED = "converted", _("Converted") CANCELED = "canceled", _("Canceled") @@ -1245,6 +1250,7 @@ class Lead(models.Model): customer.additional_info = {} customer.additional_info.update({"info":self.to_dict()}) + customer.additional_info.update({"stage":"qualified"}) self.customer = customer self.status = Status.QUALIFIED customer.save() diff --git a/inventory/signals.py b/inventory/signals.py index 6b9caa72..d39f65ad 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -670,16 +670,12 @@ def create_customer_user(sender, instance, created, **kwargs): username=instance.email, email=instance.email, ) - instance.additional_info["customer_info"] = to_dict(instance) - # customer_info = instance.additional_info.get("customer_info",None) - # user.first_name = customer_info.get("first_name", None) if customer_info else "" - # user.last_name = customer_info.get("last_name", None) if customer_info else "" - + instance.additional_info.update({"user_info": to_dict(user)}) user.set_unusable_password() user.save() instance.user = user - instance.save() - + instance.save() + # Create Item @receiver(post_save, sender=models.Car) @@ -894,5 +890,5 @@ def check_users_quota(sender, instance, **kwargs): if allowed_users is None: raise ValidationError(_("The user quota for staff members is not defined. Please contact support.")) current_staff_count = instance.dealer.staff.count() - if current_staff_count == allowed_users: + if current_staff_count > allowed_users: raise ValidationError(_("You have reached the maximum number of staff users allowed for your plan.")) diff --git a/inventory/urls.py b/inventory/urls.py index 0f0a7818..9267f249 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -121,6 +121,11 @@ urlpatterns = [ views.schedule_lead, name="schedule_lead", ), + path( + "crm/leads//transfer/", + views.lead_transfer, + name="lead_transfer", + ), path( "crm/opportunities//add_note/", @@ -364,7 +369,7 @@ urlpatterns = [ ), path( "organizations//delete/", - views.OrganizationDeleteView.as_view(), + views.OrganizationDeleteView, name="organization_delete", ), # Representative URLs diff --git a/inventory/views.py b/inventory/views.py index f3c0dab6..a7bef890 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -18,7 +18,7 @@ from django.db import transaction from django.db.models import Func from django.contrib import messages from django.http import JsonResponse -from django.forms import HiddenInput +from django.forms import HiddenInput, ValidationError from django.shortcuts import HttpResponse from django.db.models import Sum, F, Count from django.core.paginator import Paginator @@ -462,14 +462,12 @@ class AjaxHandlerView(LoginRequiredMixin, View): ) manufacturer_name, model_name, year_model = result.values() - make = get_make(manufacturer_name) - model = get_model(model_name, make) + car_make = get_make(manufacturer_name) + car_model = get_model(model_name, car_make) logger.info( f"VIN decoded using {decoding_method}: Make={manufacturer_name}, Model={model_name}, Year={year_model}" - ) - car_model = model - car_make = make + ) if not car_make: return JsonResponse( @@ -1148,15 +1146,12 @@ class DealerDetailView(LoginRequiredMixin, DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - dealer = self.object # Fetch current staff count from the annotated queryset staff_count = dealer.staff_count - # Get the quota value dynamically quota_dict = get_user_quota(dealer.user) allowed_users = quota_dict.get("Users", None) # Fetch quota or default to None - context["staff_count"] = staff_count context["allowed_users"] = allowed_users context["quota_display"] = f"{staff_count}/{allowed_users}" if allowed_users is not None else "N/A" @@ -1185,9 +1180,7 @@ class CustomerListView(LoginRequiredMixin, ListView): def get_queryset(self): query = self.request.GET.get("q") dealer = get_user_type(self.request) - customers = dealer.entity.get_customers().filter(additional_info__type="customer") - return apply_search_filters(customers, query) def get_context_data(self, **kwargs): @@ -1251,16 +1244,12 @@ def add_activity_to_customer(request, pk): def CustomerCreateView(request): form = forms.CustomerForm() - if request.method == "POST": form = forms.CustomerForm(request.POST) dealer = get_user_type(request) - if form.is_valid(): - email = form.cleaned_data["email"] - - # Check if customer with this email already exists - if dealer.entity.get_customers().filter(email=email).exists(): + if form.is_valid(): + if dealer.entity.get_customers().filter(email=form.cleaned_data["email"]).exists(): messages.error(request, _("Customer with this email already exists.")) else: # Create customer name @@ -1269,7 +1258,7 @@ def CustomerCreateView(request): f"{form.cleaned_data['middle_name']} " f"{form.cleaned_data['last_name']}" ) - + customer_dict = { x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"} # Create customer instance try: customer = dealer.entity.create_customer( @@ -1278,19 +1267,12 @@ def CustomerCreateView(request): "customer_name": customer_name, "address_1": form.cleaned_data["address"], "phone": form.cleaned_data["phone_number"], - "email": email, - + "email": form.cleaned_data["email"], } ) - - customer.additional_info = { - "customer_info": { - "first_name": form.cleaned_data["first_name"], - "middle_name": form.cleaned_data["middle_name"], - "last_name": form.cleaned_data["last_name"], - }, - "type": "customer", - } + customer.additional_info = {} + customer.additional_info["customer_info"] = customer_dict + customer.additional_info["type"] = "customer" customer.save() messages.success(request, _("Customer created successfully.")) @@ -1328,7 +1310,16 @@ def CustomerUpdateView(request, pk): instance.email = customer_dict["email"] customer_dict["pk"] = str(instance.pk) - instance.additional_info["customer_info"] = customer_dict + instance.additional_info.update({"customer_info": customer_dict}) + try: + user = User.objects.filter(pk=int(instance.additional_info['user_info']['id'])).first() + if user: + user.username = customer_dict["email"] + user.email = customer_dict["email"] + user.save() + except Exception as e: + raise Exception(e) + instance.save() messages.success(request, _("Customer updated successfully.")) return redirect("customer_list") @@ -1653,7 +1644,9 @@ class OrganizationDetailView(DetailView): def OrganizationCreateView(request): if request.method == "POST": form = forms.OrganizationForm(request.POST) - # upload logo + if CustomerModel.objects.filter(email=request.POST["email"]).exists(): + messages.error(request, _("An organization with this email already exists.")) + return redirect("organization_create") organization_dict = { x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken" @@ -1686,13 +1679,13 @@ def OrganizationCreateView(request): def OrganizationUpdateView(request,pk): - organization = get_object_or_404(CustomerModel, pk=pk) + organization = get_object_or_404(CustomerModel, pk=pk) if request.method == "POST": form = forms.OrganizationForm(request.POST) - + organization_dict = { x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken" - } + } dealer = get_user_type(request) instance = dealer.entity.get_customers().get( @@ -1702,8 +1695,15 @@ def OrganizationUpdateView(request,pk): instance.address_1 = organization_dict["address"] instance.phone = organization_dict["phone_number"] instance.email = organization_dict["email"] - - organization_dict["logo"] = organization.additional_info["organization_info"]["logo"] + + image = request.FILES.get("logo") + if image: + file_name = default_storage.save("images/{}".format(image.name), image) + file_url = default_storage.url(file_name) + organization_dict["logo"] = file_url + else: + organization_dict["logo"] = organization.additional_info["organization_info"]["logo"] + organization_dict["pk"] = str(instance.pk) instance.additional_info["organization_info"] = organization_dict instance.additional_info["type"] = "organization" @@ -1711,19 +1711,23 @@ def OrganizationUpdateView(request,pk): messages.success(request, _("Organization created successfully.")) return redirect("organization_list") else: - form = forms.OrganizationForm( + form = forms.OrganizationForm( initial=organization.additional_info["organization_info"] or {} ) - form.fields.pop("logo", None) + # form.fields.pop("logo", None) return render(request, "organizations/organization_form.html", {"form": form}) -class OrganizationDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): - model = models.Organization - template_name = "organizations/organization_confirm_delete.html" - success_url = reverse_lazy("organization_list") - success_message = "Organization deleted successfully." - +# class OrganizationDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): +# model = models.Organization +# template_name = "organizations/organization_confirm_delete.html" +# success_url = reverse_lazy("organization_list") +# success_message = "Organization deleted successfully." +def OrganizationDeleteView(request, pk): + organization = get_object_or_404(CustomerModel, pk=pk) + organization.delete() + messages.success(request, _("Organization deleted successfully.")) + return redirect("organization_list") class RepresentativeListView(LoginRequiredMixin, ListView): model = models.Representative @@ -2119,7 +2123,7 @@ def create_estimate(request,pk=None): form.initial['customer'] = customer car_list = models.Car.objects.filter(dealer=dealer,colors__isnull=False,finances__isnull=False,status="available").annotate(color=F('colors__exterior__rgb'),color_name=F('colors__exterior__name')).values_list( - 'id_car_make__name', 'id_car_model__name','id_car_serie__name','id_car_trim__name','color','color_name','hash').distinct() + 'id_car_make__name', 'id_car_model__name','id_car_serie__name','id_car_trim__name','color','color_name','hash').annotate(hash_count=Count('hash')).distinct() context = { "form": form, "items": [ @@ -2130,7 +2134,8 @@ def create_estimate(request,pk=None): 'trim':x[3], 'color':x[4], 'color_name':x[5], - 'hash': x[6] + 'hash': x[6], + 'hash_count': x[7] } for x in car_list ], @@ -2174,9 +2179,8 @@ def create_sale_order(request, pk): item.item_model.additional_info['car_info']['status'] = 'sold' item.item_model.save() except KeyError: - pass - - models.Car.objects.get(vin=item.item_model.name).mark_as_sold(user=request.user) + pass + models.Car.objects.get(vin=item.item_model.name).mark_as_sold(request) messages.success(request, "Sale Order created successfully") return redirect("estimate_detail", pk=pk) @@ -2586,11 +2590,14 @@ class LeadListView(ListView): def get_queryset(self): dealer = get_user_type(self.request) - staff = getattr(self.request.user, "staff", None) - - if staff: - return models.Lead.objects.filter(dealer=dealer, staff=staff) - return models.Lead.objects.filter(dealer=dealer) + print(dealer.user) + staffmember = getattr(self.request.user, "staffmember", None) + if staffmember: + qs = models.Lead.objects.filter(dealer=dealer) + if staffmember.staff.staff_type == models.StaffTypes.MANAGER or self.request.user == dealer.user: + return qs + return qs.filter(staff=staffmember.staff) + return models.Lead.objects.none() class LeadDetailView(DetailView): @@ -2615,6 +2622,7 @@ class LeadDetailView(DetailView): context["status_history"] = models.LeadStatusHistory.objects.filter( lead=self.object ) + context["transfer_form"] = forms.LeadTransferForm() return context @@ -2638,10 +2646,11 @@ def lead_create(request): instance = form.save(commit=False) dealer = get_user_type(request) instance.dealer = dealer - staff = None - if hasattr(request.user, "staffmember"): - staff = request.user.staffmember.staff - instance.staff = staff + # staff = None + # if hasattr(request.user, "staffmember"): + # staff = request.user.staffmember.staff + # instance.staff = staff + instance.staff = form.cleaned_data.get("staff") # creating customer in ledger customer = dealer.entity.get_customers().filter(email=instance.email).first() @@ -2655,6 +2664,8 @@ def lead_create(request): "sales_tax_rate": 0.15, } ) + customer.additional_info.update({'stage': 'lead'}) + customer.save() instance.customer = customer instance.save() messages.success(request, "Lead created successfully!") @@ -2769,24 +2780,27 @@ def schedule_lead(request, pk): instance = form.save(commit=False) instance.lead = lead instance.scheduled_by = request.user - instance.save() # Create AppointmentRequest service = Service.objects.filter(name=instance.scheduled_type).first() if not service: messages.error(request, "Service not found!") return redirect("lead_list") - # staff_member = StaffMember.objects.filter(user=request.user).first() staff_member = request.user.staffmember - appointment_request = AppointmentRequest.objects.create( - date=instance.scheduled_at.date(), - start_time=instance.scheduled_at.time(), - end_time=(instance.scheduled_at + instance.duration).time(), - service=service, - staff_member=staff_member, - ) - client = get_object_or_404(User, email=lead.email) + try: + appointment_request = AppointmentRequest.objects.create( + date=instance.scheduled_at.date(), + start_time=instance.scheduled_at.time(), + end_time=(instance.scheduled_at + instance.duration).time(), + service=service, + staff_member=staff_member, + ) + except ValidationError as e: + messages.error(request, str(e)) + return redirect("schedule_lead", pk=lead.pk) + + client = get_object_or_404(User, email=lead.email) # Create Appointment Appointment.objects.create( client=client, @@ -2795,6 +2809,7 @@ def schedule_lead(request, pk): address=lead.address, ) + instance.save() messages.success(request, "Lead scheduled and appointment created successfully!") return redirect("lead_list") else: @@ -2805,20 +2820,33 @@ def schedule_lead(request, pk): return render(request, "crm/leads/schedule_lead.html", {"lead": lead, "form": form}) +@login_required +def lead_transfer(request,pk): + lead = get_object_or_404(models.Lead, pk=pk) + if request.method == "POST": + form = forms.LeadTransferForm(request.POST) + if form.is_valid(): + lead.staff = form.cleaned_data["transfer_to"] + lead.save() + messages.success(request, "Lead transferred successfully!") + else: + messages.error(request, f"Invalid form data: {str(form.errors)}") + return redirect("lead_list") + @login_required def send_lead_email(request, pk,email_pk=None): lead = get_object_or_404(models.Lead, pk=pk) status = request.GET.get("status") + dealer = get_user_type(request) if status == 'draft': models.Email.objects.create(content_object=lead, created_by=request.user,from_email="manager@tenhal.com", to_email=request.GET.get("to"), subject=request.GET.get("subject"), message=request.GET.get("message"),status=models.EmailStatus.DRAFT) - models.Activity.objects.create(content_object=lead, notes="Email Draft",created_by=request.user,activity_type=models.ActionChoices.EMAIL) + models.Activity.objects.create(dealer=dealer,content_object=lead, notes="Email Draft",created_by=request.user,activity_type=models.ActionChoices.EMAIL) messages.success(request, _("Email Draft successfully!")) response = HttpResponse(redirect("lead_detail", pk=lead.pk)) response['HX-Redirect'] = reverse('lead_detail', args=[lead.pk]) return response - dealer = get_user_type(request) - lead.status = models.Status.IN_PROGRESS + lead.status = models.Status.CONTACTED lead.save() # lead.convert_to_customer(dealer.entity) @@ -3140,7 +3168,7 @@ class BillDetailView(LoginRequiredMixin, DetailView): kwargs["transactions"] = transactions kwargs["grand_total"] = grand_total - print(dir(txs[0])) + return super().get_context_data(**kwargs) diff --git a/static/images/images/tenhal_hero.png b/static/images/images/tenhal_hero.png new file mode 100644 index 00000000..c4892c6c Binary files /dev/null and b/static/images/images/tenhal_hero.png differ diff --git a/static/images/images/web-design.jpg b/static/images/images/web-design.jpg new file mode 100644 index 00000000..11a96ea4 Binary files /dev/null and b/static/images/images/web-design.jpg differ diff --git a/static/images/sold.png b/static/images/sold.png new file mode 100644 index 00000000..29aa281e Binary files /dev/null and b/static/images/sold.png differ diff --git a/templates/base.html b/templates/base.html index 00b375de..0de361a5 100644 --- a/templates/base.html +++ b/templates/base.html @@ -105,8 +105,7 @@ - - + diff --git a/templates/crm/leads/lead_detail.html b/templates/crm/leads/lead_detail.html index e162e3ee..3437c00b 100644 --- a/templates/crm/leads/lead_detail.html +++ b/templates/crm/leads/lead_detail.html @@ -1,6 +1,15 @@ {% extends 'base.html' %} {% load i18n static %} {% block content %} +{% load crispy_forms_tags %} + +{% block customCSS %} + +{% endblock customCSS %}
@@ -32,7 +41,7 @@

{{ lead.first_name }} {{ lead.last_name }}

{% if lead.staff %} -

{{ _("Assigned to")}}: {{ lead.staff.user.get_full_name }}

+

{{ _("Assigned to")}}: {{ lead.staff }}

{% else %}

{{ _("Not Assigned")}}

{% endif %} @@ -123,12 +132,35 @@
-
diff --git a/templates/crm/leads/lead_list.html b/templates/crm/leads/lead_list.html index a84f45ea..e69a1a84 100644 --- a/templates/crm/leads/lead_list.html +++ b/templates/crm/leads/lead_list.html @@ -123,6 +123,8 @@ {{_("In Progress")}} {% elif lead.status == "qualified" %} {{_("Qualified")}} + {% elif lead.status == "contacted" %} + {{_("Contacted")}} {% elif lead.status == "canceled" %} {{_("Canceled")}} {% endif %} @@ -136,6 +138,7 @@ {{ lead.phone_number }} {% if lead.get_latest_schedule %} + {{lead.get_latest_schedule.scheduled_type}} at
{% if lead.get_latest_schedule.scheduled_type == "Call" %} @@ -175,29 +178,31 @@ {{ _("No") }} {% endif %} - -
- - + {% endif %} {% endfor %} diff --git a/templates/header.html b/templates/header.html index 0fe05859..ee159461 100644 --- a/templates/header.html +++ b/templates/header.html @@ -429,12 +429,8 @@ {% else %} {% endif %} -
- {% if user.dealer %} -
{{ user.dealer.get_local_name }}
- {% else %} -
{{ user.staff.get_local_name }}
- {% endif %} +
+
{{ user }}
diff --git a/templates/inventory/car_detail.html b/templates/inventory/car_detail.html index 28720d3e..f781e725 100644 --- a/templates/inventory/car_detail.html +++ b/templates/inventory/car_detail.html @@ -3,10 +3,16 @@ {% block title %}{{ _("Car Details") }}{% endblock %} {% block customCSS %} {% endblock customCSS %} @@ -42,10 +48,10 @@ {% endif %} -
+
-
+

{% trans 'Car Details' %}

@@ -389,6 +395,9 @@ {% endif %}
+ {% if car.status == 'sold' %} + + {% endif %}
@@ -450,8 +459,7 @@ {% trans 'Yes' %}
- -
+
@@ -470,7 +478,6 @@
- {% endblock %} diff --git a/templates/inventory/car_list_view.html b/templates/inventory/car_list_view.html index d2c2aa26..4e0fe4ac 100644 --- a/templates/inventory/car_list_view.html +++ b/templates/inventory/car_list_view.html @@ -110,6 +110,7 @@ {{ _("Model") }} {{ _("Year") }} {{ _("Trim") }} + {{ _("Color") }} {{ _("Age") }} {{ _("Status") }} @@ -133,6 +134,16 @@

{{car.id_car_trim }}

+ +
+
+ {{ car.colors.exterior.get_local_name }} +
+
+ {{ car.colors.interior.get_local_name }} +
+
+

{{car.receiving_date|timesince}}

diff --git a/templates/organizations/organization_list.html b/templates/organizations/organization_list.html index 6fc20b4b..03ce73ca 100644 --- a/templates/organizations/organization_list.html +++ b/templates/organizations/organization_list.html @@ -111,12 +111,13 @@
{{ org.customer_name }}

{{ org.customer_name}} +
- {{ org.additional_info.info.crn }} - {{ org.additional_info.info.vrn }} + {{ org.additional_info.organization_info.crn }} + {{ org.additional_info.organization_info.vrn }} {{ org.phone }} {{ org.address_1 }} diff --git a/templates/sales/estimates/estimate_detail.html b/templates/sales/estimates/estimate_detail.html index 6a59290c..215198e8 100644 --- a/templates/sales/estimates/estimate_detail.html +++ b/templates/sales/estimates/estimate_detail.html @@ -58,7 +58,22 @@
-

{% trans 'Quotation' %}

+
+

{% trans 'Quotation' %}

+
+ {% if estimate.status == 'draft' %} + {% trans "Draft" %} + {% elif estimate.status == 'in_review' %} + {% trans "In Review" %} + {% elif estimate.status == 'approved' %} + {% trans "Approved" %} + {% elif estimate.status == 'completed' %} + {% trans "completed" %} + {% elif estimate.status == 'canceled' %} + {% trans "canceled" %} + {% endif %} +
+
{% if estimate.invoicemodel_set.first %} View Invoice @@ -190,7 +205,7 @@ {% trans "Additional Services" %} {% for service in data.additionals %} - + {{service.name}} - {{service.total}}
+ + {{service.name}} - {{service.price_}}
{% endfor %} diff --git a/templates/sales/estimates/estimate_form.html b/templates/sales/estimates/estimate_form.html index 5c56e417..6a6dcb87 100644 --- a/templates/sales/estimates/estimate_form.html +++ b/templates/sales/estimates/estimate_form.html @@ -18,7 +18,7 @@