diff --git a/car_inventory/urls.py b/car_inventory/urls.py index 84efa986..0f1085b0 100644 --- a/car_inventory/urls.py +++ b/car_inventory/urls.py @@ -33,6 +33,5 @@ urlpatterns += i18n_patterns( # path('', include(tf_urls)), ) - +# if not settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) -urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/inventory/models.py b/inventory/models.py index 920d23df..556529c3 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -61,7 +61,7 @@ from encrypted_model_fields.fields import ( # from simple_history.models import HistoricalRecords from plans.models import Invoice -from django_extensions.db.fields import RandomCharField +from django_extensions.db.fields import RandomCharField,AutoSlugField logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) @@ -74,15 +74,7 @@ class Base(models.Model): primary_key=True, verbose_name=_("Primary Key"), ) - slug = models.SlugField( - null=True, - blank=True, - unique=True, - verbose_name=_("Slug"), - help_text=_( - "Slug for the object. If not provided, it will be generated automatically." - ), - ) + slug = RandomCharField(length=8, unique=True) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At")) @@ -744,7 +736,7 @@ class Car(Base): ) def save(self, *args, **kwargs): - self.slug = slugify(self.vin) + # self.slug = slugify(self.vin) self.hash = self.get_hash super(Car, self).save(*args, **kwargs) @@ -1388,7 +1380,7 @@ class Dealer(models.Model, LocalizedNameMixin): blank=True, null=True, verbose_name=_("Logo"), - default="", + default="default-image/user.jpg", ) thumbnail = ImageSpecField( source="logo", @@ -1530,7 +1522,7 @@ class Staff(models.Model): blank=True, null=True, verbose_name=_("Image"), - default="", + default="default-image/user.jpg", ) thumbnail = ImageSpecField( source="logo", @@ -1758,7 +1750,7 @@ class Customer(models.Model): blank=True, null=True, verbose_name=_("Image"), - default="", + default="default-image/user.jpg", ) thumbnail = ImageSpecField( source="image", @@ -1912,7 +1904,7 @@ class Organization(models.Model, LocalizedNameMixin): blank=True, null=True, verbose_name=_("Logo"), - default="", + default="default-image/user.jpg", ) thumbnail = ImageSpecField( source="logo", @@ -2732,7 +2724,7 @@ class Vendor(models.Model, LocalizedNameMixin): blank=True, null=True, verbose_name=_("Logo"), - default="", + default="default-image/user.jpg", ) thumbnail = ImageSpecField( source="logo", @@ -2758,6 +2750,21 @@ class Vendor(models.Model, LocalizedNameMixin): models.Index(fields=["crn"], name="vendor_crn_idx"), models.Index(fields=["vrn"], name="vendor_vrn_idx"), ] + constraints = [ + models.UniqueConstraint( + fields=["dealer", "crn"], name="unique_crn_per_dealer" + ), + models.UniqueConstraint( + fields=["dealer", "vrn"], name="unique_vrn_per_dealer" + ), + models.UniqueConstraint( + fields=["dealer", "email"], name="unique_email_per_dealer" + ), + models.UniqueConstraint( + fields=["dealer", "phone_number"], name="unique_phone_number_per_dealer" + ), + ] + def __str__(self): return self.name diff --git a/inventory/urls.py b/inventory/urls.py index e2d76dcf..f8348b41 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -342,11 +342,6 @@ urlpatterns = [ views.CarDetailView.as_view(), name="car_detail", ), - path( - "/cars//estimate/", - views.create_estimate_for_car, - name="create_estimate_for_car", - ), path("cars//history/", views.car_history, name="car_history"), path( "/cars//update/", @@ -794,7 +789,7 @@ urlpatterns = [ ), path( "/sales/estimates/create//", - views.estimate_create_from_opportunity, + views.create_estimate, name="estimate_create_from_opportunity", ), path( @@ -943,7 +938,7 @@ urlpatterns = [ views.ItemServiceUpdateView.as_view(), name="item_service_update", ), - + path( "/items/services//detail/", views.ItemServiceDetailView.as_view(), @@ -967,7 +962,7 @@ urlpatterns = [ ), path( "/items/expeneses//detail/", - views.ItemExpenseDetailView.as_view(), + views.ItemExpenseDetailView.as_view(), name="item_expense_detail", ), # Bills diff --git a/inventory/views.py b/inventory/views.py index dae5beac..fe6584f2 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -12,6 +12,8 @@ import tempfile import numpy as np from time import sleep +from weasyprint import HTML + # from rich import print from random import randint from decimal import Decimal @@ -3665,7 +3667,7 @@ class UserCreateView( # return self.form_invalid(form) email = form.cleaned_data["email"] - if models.Staff.objects.filter(dealer=dealer,user__email=email).exists() or models.Dealer.objects.filter(user__email=email).exists(): + if models.Staff.objects.filter(user__email=email).exists() or models.Dealer.objects.filter(user__email=email).exists(): messages.error( self.request, _( @@ -5118,7 +5120,29 @@ class EstimatePrintView(EstimateDetailView): uses a dedicated, stripped-down print template. """ template_name = "sales/estimates/estimate_preview.html" - + + def get(self, request, *args, **kwargs): + + self.object = self.get_object() + context = self.get_context_data(object=self.object) + + + # lang = request.GET.get('lang', 'ar') + + + template_path = "sales/estimates/estimate_preview.html" + + + html_string = render_to_string(template_path, context) + + + pdf_file = HTML(string=html_string).write_pdf() + + + response = HttpResponse(pdf_file, content_type='application/pdf') + response['Content-Disposition'] = f'attachment; filename="estimate_{self.object.estimate_number}.pdf"' + + return response @login_required @@ -10982,7 +11006,45 @@ class PurchaseOrderDetailView(LoginRequiredMixin, PermissionRequiredMixin, Detai if i["po_item_status"] != "cancelled" ) return context + def get(self, request, *args, **kwargs): + self.object = self.get_object() + context = self.get_context_data(object=self.object) + po_items_qs, item_data = self.object.get_itemtxs_data( + queryset=self.object.itemtransactionmodel_set.all().select_related( + "item_model", "bill_model" + ) + ) + + if self.object.po_status == 'fulfilled': + context['po_items_list']=po_items_qs + context['vendor']=po_items_qs.first().bill_model.vendor + context['dealer']=request.dealer + + # Check if PDF format is requested + if request.GET.get('format') == 'pdf': + # Use a separate, print-friendly template for the PDF + if request.GET.get('lang')=='en': + html_string = render_to_string( + "purchase_orders/po_detail_en_pdf.html", + context + ) + else: + html_string=render_to_string( + "purchase_orders/po_detail_ar_pdf.html", + context + ) + + + + # Use WeasyPrint to generate the PDF + pdf = HTML(string=html_string).write_pdf() + + response = HttpResponse(pdf, content_type="application/pdf") + response["Content-Disposition"] = f'attachment; filename="PO_{self.object.po_number}.pdf"' + return response + # If not a PDF request, return the standard HTML response + return self.render_to_response(context) class PurchaseOrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): model = PurchaseOrderModel diff --git a/templates/crm/notifications_history.html b/templates/crm/notifications_history.html index 2e7a9853..9405399a 100644 --- a/templates/crm/notifications_history.html +++ b/templates/crm/notifications_history.html @@ -73,7 +73,7 @@ body { {% endif %}
-
+
{{ notification.message|safe }}

@@ -100,7 +100,7 @@ body {

-
+

{% trans "Status" %}