diff --git a/inventory/forms.py b/inventory/forms.py index b01426ea..4af30c0b 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -144,11 +144,10 @@ class StaffForm(forms.ModelForm): queryset=CustomGroup.objects.all(), required=True, ) - name = forms.CharField( - label=_("Full Name")) + class Meta: model = Staff - fields = ["first_name","last_name","name", "arabic_name", "phone_number", "address", "logo", "group"] + fields = ["first_name","last_name", "arabic_name", "phone_number", "address", "logo", "group"] # Dealer Form diff --git a/inventory/models.py b/inventory/models.py index 8a725729..ee42f924 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -594,7 +594,7 @@ class AdditionalServices(models.Model, LocalizedNameMixin): if self.taxable else self.price ) - + @property def service_tax(self): vat = VatRate.objects.filter(dealer=self.dealer, is_active=True).first() @@ -896,18 +896,18 @@ class Car(Base): def vat_amount(self): vat = VatRate.objects.filter(dealer=self.dealer,is_active=True).first() return Decimal(self.final_price) * (vat.rate) - + @property def total_services_and_car_vat(self): return self.vat_amount+self.get_additional_services()['services_vat'] - + @property def final_price_plus_vat(self): return Decimal(self.final_price) + Decimal(self.vat_amount) - + @property def final_price_plus_services_plus_vat(self): - return Decimal(self.final_price_plus_vat) + Decimal(self.get_additional_services()['total_']) #total services with vat and car_sell price with vat + return Decimal(self.final_price_plus_vat) + Decimal(self.get_additional_services()['total_']) #total services with vat and car_sell price with vat # to be used after invoice is created @property def invoice(self): @@ -929,8 +929,8 @@ class Car(Base): except ExtraInfo.DoesNotExist: return Decimal(0) - - + + # def get_discount_amount(self,estimate,user): # try: # instance = models.ExtraInfo.objects.get( @@ -1419,7 +1419,7 @@ class StaffTypes(models.TextChoices): # AGENT = "agent", _("Agent") -class Staff(models.Model, LocalizedNameMixin): +class Staff(models.Model): # staff_member = models.OneToOneField( # StaffMember, on_delete=models.CASCADE, related_name="staff" # ) @@ -1429,7 +1429,7 @@ class Staff(models.Model, LocalizedNameMixin): dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="staff") first_name = models.CharField(max_length=255, verbose_name=_("First Name")) last_name = models.CharField(max_length=255, verbose_name=_("Last Name")) - name = models.CharField(max_length=255, verbose_name=_("Name")) + arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name")) phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number")) staff_type = models.CharField( @@ -1456,7 +1456,7 @@ class Staff(models.Model, LocalizedNameMixin): def save(self, *args, **kwargs): if not self.slug: - base_slug = slugify(f"{self.name}") + base_slug = slugify(f"{self.first_name}-{self.last_name}") self.slug = base_slug counter = 1 @@ -1471,6 +1471,9 @@ class Staff(models.Model, LocalizedNameMixin): objects = StaffUserManager() + @property + def fullname(self): + return self.first_name + " " + self.last_name def deactivate_account(self): self.active = False self.user.is_active = False @@ -1530,7 +1533,6 @@ class Staff(models.Model, LocalizedNameMixin): verbose_name = _("Staff") verbose_name_plural = _("Staff") indexes = [ - models.Index(fields=["name"]), models.Index(fields=["staff_type"]), ] permissions = [] @@ -1542,7 +1544,7 @@ class Staff(models.Model, LocalizedNameMixin): ] def __str__(self): - return f"{self.name}" + return f"{self.first_name} {self.last_name}" class Sources(models.TextChoices): diff --git a/inventory/views.py b/inventory/views.py index 996b0ad9..a151b5f2 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -458,7 +458,7 @@ def general_dashboard(request,dealer_slug): total_vat_collected_from_cars = cars_sold_filtered.aggregate( total=Sum(F('selling_price') * VAT_RATE) )['total'] or 0 - + net_profit_from_cars = total_revenue_from_cars - total_cost_of_cars_sold total_discount = cars_sold_filtered.aggregate(total=Sum('discount_amount'))['total'] or 0 @@ -519,7 +519,7 @@ def general_dashboard(request,dealer_slug): monthly_cars_sold_json = json.dumps(monthly_cars_sold) monthly_revenue_json = json.dumps(monthly_revenue) monthly_net_profit_json = json.dumps(monthly_net_profit) - + # ---------------------------------------------------- # Sales by MAKE # ---------------------------------------------------- @@ -529,9 +529,9 @@ def general_dashboard(request,dealer_slug): sales_by_make_labels = [data['id_car_make__name'] for data in sales_by_make_data] sales_by_make_counts = [data['car_count'] for data in sales_by_make_data] - - + + # ---------------------------------------------------- # DATA FOR CAR SALES BY MODELS (for the new interactive chart) # ---------------------------------------------------- @@ -555,15 +555,15 @@ def general_dashboard(request,dealer_slug): # If no make is selected, pass an empty list or some default data sales_data_by_model = [] - - + + # 1. Inventory by Make (Pie Chart) inventory_by_make_data = active_cars.values('id_car_make__name').annotate( car_count=Count('id_car_make__name') ).order_by('-car_count') - + inventory_by_make_labels = [data['id_car_make__name'] for data in inventory_by_make_data] inventory_by_make_counts = [data['car_count'] for data in inventory_by_make_data] @@ -582,7 +582,7 @@ def general_dashboard(request,dealer_slug): else: # Default data inventory_data_by_model = [] - + context = { 'start_date': start_date, 'end_date': end_date, @@ -631,14 +631,14 @@ def general_dashboard(request,dealer_slug): 'monthly_revenue_json': monthly_revenue_json, 'monthly_net_profit_json': monthly_net_profit_json, - + # Sales Chart Data 'sales_by_make_labels_json': json.dumps(sales_by_make_labels), 'sales_by_make_counts_json': json.dumps(sales_by_make_counts), 'all_makes_sold': all_makes_sold, 'selected_make_sales': selected_make_sales, 'sales_data_by_model_json': json.dumps(list(sales_data_by_model)), - + # New Inventory Chart Data 'inventory_by_make_labels_json': json.dumps(inventory_by_make_labels), 'inventory_by_make_counts_json': json.dumps(inventory_by_make_counts), @@ -646,9 +646,9 @@ def general_dashboard(request,dealer_slug): 'selected_make_inventory': selected_make_inventory, 'inventory_data_by_model_json': json.dumps(list(inventory_data_by_model)), - + } - + return render(request, 'dashboards/general_dashboard.html', context) @@ -670,7 +670,7 @@ def sales_dashboard(request,dealer_slug): else: start_date = today_local - timedelta(days=30) end_date = today_local - + # Filter leads by date range and dealer leads_filtered = models.Lead.objects.filter( dealer=dealer, @@ -678,7 +678,7 @@ def sales_dashboard(request,dealer_slug): created__date__lte=end_date ) - + # ---------------------------------------------------- # 2. Lead Sources Chart Logic # ---------------------------------------------------- @@ -748,7 +748,7 @@ def aging_inventory_list_view(request, dealer_slug): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) today_local = timezone.localdate() aging_threshold_days = 60 - + # Get filter parameters from the request selected_make = request.GET.get('make') selected_model = request.GET.get('model') @@ -762,7 +762,7 @@ def aging_inventory_list_view(request, dealer_slug): receiving_date__date__lt=today_local - timedelta(days=aging_threshold_days) ).exclude(status='sold') total_aging_inventory_value=aging_cars_queryset.aggregate(total=Sum('cost_price'))['total'] - + # Apply filters to the queryset if they exist. Chaining is fine here. if selected_make: aging_cars_queryset = aging_cars_queryset.filter(id_car_make__name=selected_make) @@ -774,7 +774,7 @@ def aging_inventory_list_view(request, dealer_slug): aging_cars_queryset = aging_cars_queryset.filter(id_car_year__year=selected_year) if selected_stock_type: aging_cars_queryset = aging_cars_queryset.filter(stock_type=selected_stock_type) - + # Get distinct values for filter dropdowns based on the initial, unfiltered aging cars queryset. # This ensures all possible filter options are always available. @@ -782,19 +782,19 @@ def aging_inventory_list_view(request, dealer_slug): dealer=dealer, receiving_date__date__lt=today_local - timedelta(days=aging_threshold_days) ).exclude(status='sold') - + all_makes = aging_base_queryset.values_list('id_car_make__name', flat=True).distinct().order_by('id_car_make__name') all_models = aging_base_queryset.values_list('id_car_model__name', flat=True).distinct().order_by('id_car_model__name') all_series = aging_base_queryset.values_list('id_car_serie__name', flat=True).distinct().order_by('id_car_serie__name') all_stock_types = aging_base_queryset.values_list('stock_type', flat=True).distinct().order_by('stock_type') all_years = aging_base_queryset.values_list('year', flat=True).distinct().order_by('-year') - + # # Set up pagination paginator = Paginator(aging_cars_queryset, 10) page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) - + # Iterate only on the cars for the current page to add the age attribute. for car in page_obj.object_list: car.age_in_days = (today_local - car.receiving_date.date()).days @@ -803,7 +803,7 @@ def aging_inventory_list_view(request, dealer_slug): "is_paginated": page_obj.has_other_pages, "cars": page_obj.object_list, 'selected_make': selected_make, - 'selected_model': selected_model, + 'selected_model': selected_model, 'selected_series': selected_series, # Corrected variable name 'selected_year': selected_year, 'selected_stock_type': selected_stock_type, @@ -813,9 +813,9 @@ def aging_inventory_list_view(request, dealer_slug): 'all_stock_types': all_stock_types, 'all_years': all_years, 'total_aging_inventory_value':total_aging_inventory_value - + } - + return render(request, 'dashboards/aging_inventory_list.html', context) def terms_and_privacy(request): @@ -3720,7 +3720,7 @@ class UserUpdateView( staff = form.save(commit=False) print(form.cleaned_data) - staff.name = form.cleaned_data["name"] + # staff.name = form.cleaned_data["name"] staff.arabic_name = form.cleaned_data["arabic_name"] staff.phone_number = form.cleaned_data["phone_number"] for customgroup in form.cleaned_data["group"]: @@ -5027,7 +5027,7 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView # calculator = CarFinanceCalculator(estimate) # finance_data = calculator.get_finance_data() finance_data = get_finance_data(estimate,dealer) - + invoice_obj = InvoiceModel.objects.all().filter(ce_model=estimate).first() kwargs["data"] = finance_data @@ -5036,7 +5036,7 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView car = estimate.get_itemtxs_data()[0].first().item_model.car selected_items = car.additional_services.filter(dealer=dealer) form = forms.AdditionalFinancesForm() - form.fields["additional_finances"].queryset = form.fields["additional_finances"].queryset.filter(dealer=dealer) # + form.fields["additional_finances"].queryset = form.fields["additional_finances"].queryset.filter(dealer=dealer) # form.initial["additional_finances"] = selected_items kwargs["additionals_form"] = form except Exception as e: @@ -7046,7 +7046,7 @@ def send_lead_email(request, dealer_slug, slug, email_pk=None): تحياتي، {lead.dealer.arabic_name} {lead.dealer.address} -{lead.dealer.phone_number} +{lead.dealer.phone_number} ----- Dear {lead.full_name}, @@ -11065,7 +11065,7 @@ def purchase_report_csv_export(request,dealer_slug): def car_sale_report_view(request, dealer_slug): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) cars_sold = models.Car.objects.filter(dealer=dealer, status='sold') - + # Get filter parameters from the request selected_make = request.GET.get('make') @@ -11089,8 +11089,8 @@ def car_sale_report_view(request, dealer_slug): # # Calculate summary data for the filtered results total_cars_sold=cars_sold.count() - total_revenue_from_cars = sum([ car.final_price for car in cars_sold]) - total_vat_on_cars=sum([car.vat_amount for car in cars_sold]) + total_revenue_from_cars = sum([ car.final_price for car in cars_sold]) + total_vat_on_cars=sum([car.vat_amount for car in cars_sold]) total_revenue_from_additonals=sum([car.get_additional_services()['total'] for car in cars_sold]) total_vat_from_additonals=sum([car.get_additional_services()['services_vat'] for car in cars_sold]) @@ -11104,11 +11104,11 @@ def car_sale_report_view(request, dealer_slug): base_sold_cars_queryset = models.Car.objects.filter(dealer=dealer, status='sold') makes =base_sold_cars_queryset.values_list('id_car_make__name', flat=True).distinct() models_qs =base_sold_cars_queryset.values_list('id_car_model__name', flat=True).distinct() - + series =base_sold_cars_queryset.values_list('id_car_serie__name', flat=True).distinct() stock_types=base_sold_cars_queryset.values_list('stock_type', flat=True).distinct() years = base_sold_cars_queryset.values_list('year', flat=True).distinct().order_by('-year') - + context = { 'cars_sold': cars_sold, 'total_cars_sold':total_cars_sold, @@ -11182,7 +11182,7 @@ def car_sale_report_csv_export(request, dealer_slug): additional_services = car.get_additional_services() services_total_price = additional_services['total'] services_vat_amount = additional_services['services_vat'] - + # Checking for the invoice number to avoid errors on cars without one invoice_number = None sold_date = None diff --git a/templates/crm/leads/lead_detail.html b/templates/crm/leads/lead_detail.html index 825b7396..f30fa126 100644 --- a/templates/crm/leads/lead_detail.html +++ b/templates/crm/leads/lead_detail.html @@ -109,7 +109,7 @@ {% if lead.staff == request.staff %} {{ _("Me") }} {% elif LANGUAGE_CODE == "en" %} - {{ lead.staff.name|capfirst }} + {{ lead.staff.fullname|capfirst }} {% else %} {{ lead.staff.arabic_name }} {% endif %} diff --git a/templates/crm/leads/lead_list.html b/templates/crm/leads/lead_list.html index 2592ffa1..c8ca9ea2 100644 --- a/templates/crm/leads/lead_list.html +++ b/templates/crm/leads/lead_list.html @@ -199,7 +199,7 @@ {% if lead.staff == request.staff %} {{ _("Me") }} {% elif LANGUAGE_CODE == "en" %} - {{ lead.staff.name|capfirst }} + {{ lead.staff.fullname|capfirst }} {% else %} {{ lead.staff.arabic_name }} {% endif %} diff --git a/templates/crm/opportunities/opportunity_list copy.html b/templates/crm/opportunities/opportunity_list copy.html index 7250543d..fa7d6fb7 100644 --- a/templates/crm/opportunities/opportunity_list copy.html +++ b/templates/crm/opportunities/opportunity_list copy.html @@ -84,7 +84,7 @@
{{ opportunity.staff.name }}
+{{ opportunity.staff.fullname }}