diff --git a/inventory/management/commands/initial_services_offered.py b/inventory/management/commands/initial_services_offered.py index 1624712b..dd531df0 100644 --- a/inventory/management/commands/initial_services_offered.py +++ b/inventory/management/commands/initial_services_offered.py @@ -12,21 +12,21 @@ class Command(BaseCommand): Service.objects.create( name="call", price=0, - duration=datetime.timedelta(minutes=10), + duration=datetime.timedelta(minutes=60), currency="SAR", description="15 min call", ) Service.objects.create( name="meeting", price=0, - duration=datetime.timedelta(minutes=30), + duration=datetime.timedelta(minutes=90), currency="SAR", description="30 min meeting", ) Service.objects.create( name="email", price=0, - duration=datetime.timedelta(minutes=30), + duration=datetime.timedelta(minutes=90), currency="SAR", description="30 min visit", ) diff --git a/inventory/middleware.py b/inventory/middleware.py index 9e2e0d4f..992d7069 100644 --- a/inventory/middleware.py +++ b/inventory/middleware.py @@ -123,13 +123,18 @@ class DealerSlugMiddleware: response = self.get_response(request) return response def process_view(self, request, view_func, view_args, view_kwargs): - if request.path_info.startswith('/en/signup/') or \ + if request.path_info.startswith('/ar/signup/') or \ + request.path_info.startswith('/en/signup/') or \ + request.path_info.startswith('/ar/login/') or \ request.path_info.startswith('/en/login/') or \ + request.path_info.startswith('/ar/logout/') or \ request.path_info.startswith('/en/logout/') or \ request.path_info.startswith('/en/ledger/') or \ request.path_info.startswith('/ar/ledger/') or \ request.path_info.startswith('/en/notifications/') or \ - request.path_info.startswith('/ar/notifications/'): + request.path_info.startswith('/ar/notifications/') or \ + request.path_info.startswith('/en/appointment/') or \ + request.path_info.startswith('/ar/appointment/'): return None if not request.user.is_authenticated: diff --git a/inventory/models.py b/inventory/models.py index 4661ca7d..95a447e5 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -1836,6 +1836,7 @@ class Schedule(models.Model): ("completed", _("Completed")), ("canceled", _("Canceled")), ] + dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE) content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') @@ -1852,6 +1853,7 @@ class Schedule(models.Model): scheduled_type = models.CharField( max_length=200, choices=ScheduledType, default="Call" ) + completed = models.BooleanField(default=False, verbose_name=_("Completed")) duration = models.DurationField(default=timedelta(minutes=5)) notes = models.TextField(blank=True, null=True) status = models.CharField( @@ -1861,7 +1863,7 @@ class Schedule(models.Model): updated_at = models.DateTimeField(auto_now=True) def __str__(self): - return f"Scheduled {self.purpose} with {self.lead.full_name} on {self.scheduled_at}" + return f"Scheduled {self.purpose} on {self.scheduled_at}" @property def schedule_past_date(self): diff --git a/inventory/signals.py b/inventory/signals.py index 31012666..393dd871 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -950,7 +950,7 @@ def create_po_item_upload(sender,instance,created,**kwargs): def create_po_fulfilled_notification(sender,instance,created,**kwargs): if instance.po_status == "fulfilled": dealer = models.Dealer.objects.get(entity=instance.entity) - accountants = models.CustomGroup.objects.filter(dealer=dealer,name="Inventory").first().group.user_set.exclude(email=dealer.user.email) + accountants = models.CustomGroup.objects.filter(dealer=dealer,name="Inventory").first().group.user_set.exclude(email=dealer.user.email).distinct() for accountant in accountants: models.Notification.objects.create( user=accountant, @@ -963,7 +963,11 @@ def create_po_fulfilled_notification(sender,instance,created,**kwargs): @receiver(post_save, sender=models.Car) def car_created_notification(sender, instance, created, **kwargs): if created: +<<<<<<< HEAD accountants = models.CustomGroup.objects.filter(dealer=instance.dealer,name__in=["Manager","Accountant","Inventory"]).first().group.user_set.all().distinct() +======= + accountants = models.CustomGroup.objects.filter(dealer=instance.dealer,name__in=["Manager","Accountant"]).first().group.user_set.all().distinct() +>>>>>>> b23e84331fbb3a8ce0814a04b68cd3380b93d3a2 for accountant in accountants: models.Notification.objects.create( user=accountant, @@ -982,7 +986,7 @@ def po_fullfilled_notification(sender, instance, created, **kwargs): recipients = User.objects.filter( groups__customgroup__dealer=instance.dealer, groups__customgroup__name__in=["Manager", "Inventory"] - ) + ).distinct() for recipient in recipients: models.Notification.objects.create( user=recipient, @@ -1010,7 +1014,7 @@ def vendor_created_notification(sender, instance, created, **kwargs): @receiver(post_save, sender=models.SaleOrder) def sale_order_created_notification(sender, instance, created, **kwargs): if created: - recipients = models.CustomGroup.objects.filter(dealer=instance.dealer,name="Accountant").first().group.user_set.exclude(email=instance.dealer.user.email) + recipients = models.CustomGroup.objects.filter(dealer=instance.dealer,name="Accountant").first().group.user_set.exclude(email=instance.dealer.user.email).distinct() for recipient in recipients: models.Notification.objects.create( @@ -1035,7 +1039,7 @@ def lead_created_notification(sender, instance, created, **kwargs): def estimate_in_review_notification(sender, instance, created, **kwargs): if instance.is_review(): dealer = models.Dealer.objects.get(entity=instance.entity) - recipients = models.CustomGroup.objects.filter(dealer=dealer,name="Manager").first().group.user_set.exclude(email=dealer.user.email) + recipients = models.CustomGroup.objects.filter(dealer=dealer,name="Manager").first().group.user_set.exclude(email=dealer.user.email).distinct() for recipient in recipients: models.Notification.objects.create( user=recipient, @@ -1068,7 +1072,7 @@ def estimate_in_approve_notification(sender, instance, created, **kwargs): def bill_model_in_approve_notification(sender, instance, created, **kwargs): if instance.is_review(): dealer = models.Dealer.objects.get(entity=instance.ledger.entity) - recipients = models.CustomGroup.objects.filter(dealer=dealer,name="Manager").first().group.user_set.exclude(email=dealer.user.email) + recipients = models.CustomGroup.objects.filter(dealer=dealer,name="Manager").first().group.user_set.exclude(email=dealer.user.email).distinct() for recipient in recipients: models.Notification.objects.create( @@ -1083,7 +1087,7 @@ def bill_model_in_approve_notification(sender, instance, created, **kwargs): def bill_model_after_approve_notification(sender, instance, created, **kwargs): if instance.is_approved(): dealer = models.Dealer.objects.get(entity=instance.ledger.entity) - recipients = models.CustomGroup.objects.filter(dealer=dealer,name="Accountant").first().group.user_set.exclude(email=dealer.user.email) + recipients = models.CustomGroup.objects.filter(dealer=dealer,name="Accountant").first().group.user_set.exclude(email=dealer.user.email).distinct() for recipient in recipients: models.Notification.objects.create( diff --git a/inventory/urls.py b/inventory/urls.py index 1f8e5084..a5a857c1 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -120,6 +120,11 @@ urlpatterns = [ views.update_task, name="update_task", ), + path( + "//update-schedule/", + views.update_schedule, + name="update_schedule", + ), path( "/crm///add-task/", views.add_task, diff --git a/inventory/views.py b/inventory/views.py index 71a5d33f..d15e4db2 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -44,7 +44,7 @@ from django.http import ( JsonResponse, HttpResponseForbidden, ) -from django.forms import HiddenInput, ValidationError +from django.forms import CharField, HiddenInput, ValidationError from django.shortcuts import HttpResponse from django.db.models import Sum, F, Count @@ -4346,8 +4346,6 @@ def create_estimate(request, dealer_slug, slug=None): data = json.loads(request.body) title = data.get("title") customer_id = data.get("customer") - # terms = data.get("terms") - # customer = entity.get_customers().filter(pk=customer_id).first() customer = models.Customer.objects.filter(pk=int(customer_id)).first() items = data.get("item", []) @@ -4543,7 +4541,8 @@ def create_estimate(request, dealer_slug, slug=None): status="available", ) .annotate( - color=F("colors__exterior__rgb"), + exterior_color=F("colors__exterior__rgb"), + interior_color=F("colors__interior__rgb"), color_name=F("colors__exterior__arabic_name"), ) .values_list( @@ -4551,9 +4550,11 @@ def create_estimate(request, dealer_slug, slug=None): "id_car_model__arabic_name", "id_car_serie__arabic_name", "id_car_trim__arabic_name", - "color", + "exterior_color", + "interior_color", "color_name", "hash", + "id_car_make__logo" ) .annotate(hash_count=Count("hash")) .distinct() @@ -4566,10 +4567,12 @@ def create_estimate(request, dealer_slug, slug=None): "model": x[1], "serie": x[2], "trim": x[3], - "color": x[4], - "color_name": x[5], - "hash": x[6], - "hash_count": x[7], + "exterior_color": x[4], + "interior_color": x[5], + "color_name": x[6], + "hash": x[7], + "logo": settings.MEDIA_URL + x[8], + "hash_count": x[9], } for x in car_list ], @@ -4849,7 +4852,6 @@ def estimate_mark_as(request, dealer_slug, pk): "estimate_detail", dealer_slug=dealer.slug, pk=estimate.pk ) estimate.mark_as_review() - elif mark == "approved": if not estimate.can_approve(): messages.error(request, _("Quotation is not ready for approval")) @@ -5618,6 +5620,11 @@ class LeadDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): context["tasks"] = models.Tasks.objects.filter( content_type__model="lead", object_id=self.object.id ) + context["schedules"] = models.Schedule.objects.filter( + dealer=dealer, + content_type__model="lead", object_id=self.object.id, + scheduled_by=self.request.user + ) context["status_history"] = models.LeadStatusHistory.objects.filter( lead=self.object ) @@ -5630,6 +5637,7 @@ class LeadDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): context["activity_form"] = forms.ActivityForm() context["staff_task_form"] = forms.StaffTaskForm() context["note_form"] = forms.NoteForm() + context["schedule_form"] = forms.ScheduleForm() return context @@ -6099,6 +6107,7 @@ def lead_convert(request,dealer_slug, slug): @login_required +@require_POST @permission_required("inventory.add_schedule", raise_exception=True) def schedule_event(request, dealer_slug,content_type,slug): """ @@ -6145,6 +6154,7 @@ def schedule_event(request, dealer_slug,content_type,slug): form = forms.ScheduleForm(request.POST) if form.is_valid(): instance = form.save(commit=False) + instance.dealer = dealer instance.content_object = obj instance.scheduled_by = request.user @@ -6178,8 +6188,8 @@ def schedule_event(request, dealer_slug,content_type,slug): Appointment.objects.create( client=client, appointment_request=appointment_request, - phone=instance.phone, - address=instance.address_1, + phone=instance.customer.phone, + address=instance.customer.address_1, ) instance.save() @@ -6199,8 +6209,8 @@ def schedule_event(request, dealer_slug,content_type,slug): ) messages.error(request, f"Invalid form data: {str(form.errors)}") return redirect(request.META.get("HTTP_REFERER")) - form = forms.ScheduleForm() - return render(request, "crm/leads/schedule_lead.html", {"lead": lead, "form": form}) + # form = forms.ScheduleForm() + # return render(request, "crm/leads/schedule_lead.html", {"lead": lead, "form": form}) @login_required @@ -9320,6 +9330,16 @@ def update_task(request,dealer_slug, pk): # tasks = models.Tasks.objects.filter(content_type__model=content_type, object_id=obj.id) return render(request, "partials/task.html", {"task": task}) +@login_required +@permission_required("inventory.change_schedule", raise_exception=True) +def update_schedule(request,dealer_slug, pk): + task = get_object_or_404(models.Schedule, pk=pk) + + if request.method == "POST": + task.completed = False if task.completed else True + task.save() + + return render(request, "partials/task.html", {"task": task}) @login_required diff --git a/templates/components/schedule_modal.html b/templates/components/schedule_modal.html index 1a0b87a6..2b358da8 100644 --- a/templates/components/schedule_modal.html +++ b/templates/components/schedule_modal.html @@ -9,7 +9,7 @@