Merge branch 'main' of http://10.10.1.136:3000/ismail/haikal into frontend

This commit is contained in:
Faheedkhan 2025-06-25 17:07:35 +03:00
commit 4ab2931729
20 changed files with 188 additions and 171 deletions

View File

@ -86,146 +86,149 @@ urlpatterns = [
# path('dealers/<int:pk>/delete/', views.DealerDeleteView.as_view(), name='dealer_delete'),
# CRM URLs
path(
"customers/create/", views.CustomerCreateView.as_view(), name="customer_create"
"<slug:dealer_slug>/customers/create/", views.CustomerCreateView.as_view(), name="customer_create"
),
path("customers/", views.CustomerListView.as_view(), name="customer_list"),
path("<slug:dealer_slug>/customers/", views.CustomerListView.as_view(), name="customer_list"),
path(
"customers/<slug:slug>/",
"<slug:dealer_slug>/customers/<slug:slug>/",
views.CustomerDetailView.as_view(),
name="customer_detail",
),
path(
"customers/<slug:slug>/add-note/",
"<slug:dealer_slug>/customers/<slug:slug>/add-note/",
views.add_note_to_customer,
name="add_note_to_customer",
),
path(
"customers/<slug:slug>/update/",
"<slug:dealer_slug>/customers/<slug:slug>/update/",
views.CustomerUpdateView.as_view(),
name="customer_update",
),
path(
"customers/<slug:slug>/delete/", views.delete_customer, name="customer_delete"
"<slug:dealer_slug>/customers/<slug:slug>/delete/", views.delete_customer, name="customer_delete"
),
path(
"customers/<slug:slug>/opportunities/create/",
"<slug:dealer_slug>/customers/<slug:slug>/opportunities/create/",
views.OpportunityCreateView.as_view(),
name="create_opportunity",
),
path("crm/leads/create/", views.lead_create, name="lead_create"),
########################################
#####Lead
########################################
path("<slug:dealer_slug>/crm/leads/create/", views.lead_create, name="lead_create"),
path(
"crm/leads/<slug:slug>/view/",
"<slug:dealer_slug>/crm/leads/<slug:slug>/view/",
views.LeadDetailView.as_view(),
name="lead_detail",
),
path("update-lead-actions/", views.update_lead_actions, name="update_lead_actions"),
path("crm/leads/lead_tracking/", views.lead_tracking, name="lead_tracking"),
path("crm/leads/lead_view/", views.lead_view, name="lead_view"),
path("crm/leads/", views.LeadListView.as_view(), name="lead_list"),
path("<slug:dealer_slug>/update-lead-actions/", views.update_lead_actions, name="update_lead_actions"),
path("<slug:dealer_slug>/crm/leads/lead_tracking/", views.lead_tracking, name="lead_tracking"),
path("<slug:dealer_slug>/crm/leads/lead_view/", views.lead_view, name="lead_view"),
path("<slug:dealer_slug>/crm/leads/", views.LeadListView.as_view(), name="lead_list"),
path(
"crm/leads/<slug:slug>/update/",
"<slug:dealer_slug>/crm/leads/<slug:slug>/update/",
views.LeadUpdateView.as_view(),
name="lead_update",
),
path("crm/leads/<slug:slug>/delete/", views.LeadDeleteView, name="lead_delete"),
path("<slug:dealer_slug>/crm/leads/<slug:slug>/delete/", views.LeadDeleteView, name="lead_delete"),
path(
"crm/leads/<slug:slug>/lead-convert/", views.lead_convert, name="lead_convert"
"<slug:dealer_slug>/crm/leads/<slug:slug>/lead-convert/", views.lead_convert, name="lead_convert"
),
path(
"crm/leads/<int:pk>/delete-note/", views.delete_note, name="delete_note_to_lead"
"<slug:dealer_slug>/crm/leads/<int:pk>/delete-note/", views.delete_note, name="delete_note_to_lead"
),
path(
"crm/<int:pk>/update-note/",
"<slug:dealer_slug>/crm/<int:pk>/update-note/",
views.update_note,
name="update_note",
),
path(
"crm/<str:content_type>/<slug:slug>/add-note/",
"<slug:dealer_slug>/crm/<str:content_type>/<slug:slug>/add-note/",
views.add_note,
name="add_note",
),
path(
"crm/<int:pk>/update-task/",
"<slug:dealer_slug>/crm/<int:pk>/update-task/",
views.update_task,
name="update_task",
),
path(
"crm/<str:content_type>/<slug:slug>/add-task/",
"<slug:dealer_slug>/crm/<str:content_type>/<slug:slug>/add-task/",
views.add_task,
name="add_task",
),
path(
"crm/<str:content_type>/<slug:slug>/add-activity/",
"<slug:dealer_slug>/crm/<str:content_type>/<slug:slug>/add-activity/",
views.add_activity,
name="add_activity",
),
path(
"crm/leads/<slug:slug>/send_lead_email/",
"<slug:dealer_slug>/crm/leads/<slug:slug>/send_lead_email/",
views.send_lead_email,
name="send_lead_email",
),
path(
"crm/leads/<slug:slug>/send_lead_email/<int:email_pk>",
"<slug:dealer_slug>/crm/leads/<slug:slug>/send_lead_email/<int:email_pk>",
views.send_lead_email,
name="send_lead_email_with_template",
),
path(
"crm/leads/<slug:slug>/schedule/",
"<slug:dealer_slug>/crm/leads/<slug:slug>/schedule/",
views.schedule_lead,
name="schedule_lead",
),
path(
"crm/leads/schedule/<int:pk>/cancel/",
"<slug:dealer_slug>/crm/leads/schedule/<int:pk>/cancel/",
views.schedule_cancel,
name="schedule_cancel",
),
path(
"crm/leads/<slug:slug>/transfer/",
"<slug:dealer_slug>/crm/leads/<slug:slug>/transfer/",
views.lead_transfer,
name="lead_transfer",
),
path(
"crm/opportunities/<slug:slug>/add_note/",
"<slug:dealer_slug>/crm/opportunities/<slug:slug>/add_note/",
views.add_note_to_opportunity,
name="add_note_to_opportunity",
),
path(
"crm/opportunities/create/",
"<slug:dealer_slug>/crm/opportunities/create/",
views.OpportunityCreateView.as_view(),
name="opportunity_create",
),
path(
"crm/opportunities/<slug:slug>/create/",
"<slug:dealer_slug>/crm/opportunities/<slug:slug>/create/",
views.OpportunityCreateView.as_view(),
name="lead_opportunity_create",
),
path(
"crm/opportunities/<slug:slug>/create/",
"<slug:dealer_slug>/crm/opportunities/<slug:slug>/create/",
views.OpportunityCreateView.as_view(),
name="opportunity_create",
),
path(
"crm/opportunities/<slug:slug>/",
"<slug:dealer_slug>/crm/opportunities/<slug:slug>/",
views.OpportunityDetailView.as_view(),
name="opportunity_detail",
),
path(
"crm/opportunities/<slug:slug>/edit/",
"<slug:dealer_slug>/crm/opportunities/<slug:slug>/edit/",
views.OpportunityUpdateView.as_view(),
name="update_opportunity",
),
path(
"crm/opportunities/",
"<slug:dealer_slug>/crm/opportunities/",
views.OpportunityListView.as_view(),
name="opportunity_list",
),
path(
"crm/opportunities/<int:pk>/delete/",
"<slug:dealer_slug>/crm/opportunities/<int:pk>/delete/",
views.delete_opportunity,
name="delete_opportunity",
),
path(
"crm/opportunities/<slug:slug>/opportunity_update_status/",
"<slug:dealer_slug>/crm/opportunities/<slug:slug>/opportunity_update_status/",
views.opportunity_update_status,
name="opportunity_update_status",
),
@ -495,25 +498,25 @@ urlpatterns = [
),
# Organization URLs
path(
"organizations/create/",
"<slug:dealer_slug>/organizations/create/",
views.OrganizationCreateView.as_view(),
name="organization_create",
),
path(
"organizations/", views.OrganizationListView.as_view(), name="organization_list"
"<slug:dealer_slug>/organizations/", views.OrganizationListView.as_view(), name="organization_list"
),
path(
"organizations/<slug:slug>/",
"<slug:dealer_slug>/organizations/<slug:slug>/",
views.OrganizationDetailView.as_view(),
name="organization_detail",
),
path(
"organizations/<slug:slug>/update/",
"<slug:dealer_slug>/organizations/<slug:slug>/update/",
views.OrganizationUpdateView.as_view(),
name="organization_update",
),
path(
"organizations/<slug:slug>/delete/",
"<slug:dealer_slug>/organizations/<slug:slug>/delete/",
views.OrganizationDeleteView,
name="organization_delete",
),

View File

@ -2200,7 +2200,7 @@ class CustomerDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
@login_required
def add_note_to_customer(request, slug):
def add_note_to_customer(request,dealer_slug, slug):
"""
This function allows authenticated users to add a note to a specific customer. The
note creation is handled by a form, which is validated after submission. If the form
@ -2217,6 +2217,7 @@ def add_note_to_customer(request, slug):
POST request, it renders the note form template with context including
the form and customer.
"""
# get_object_or_404(models.Dealer, slug=dealer_slug)
customer = get_object_or_404(models.Customer, slug=slug)
if request.method == "POST":
form = forms.NoteForm(request.POST)
@ -2226,9 +2227,9 @@ def add_note_to_customer(request, slug):
note.created_by = request.user
note.save()
return redirect("customer_detail", slug=customer.slug)
else:
form = forms.NoteForm()
return redirect("customer_detail", dealer_slug=dealer_slug,slug=customer.slug)
form = forms.NoteForm()
return render(
request, "customers/note_form.html", {"form": form, "customer": customer}
)
@ -2324,6 +2325,8 @@ class CustomerCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView
form.instance.customer_model = customer
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("customer_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]})
class CustomerUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
@ -2361,10 +2364,11 @@ class CustomerUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView
form.instance.update_user_model()
form.instance.update_customer_model()
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("customer_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]})
@login_required
def delete_customer(request, slug):
def delete_customer(request, dealer_slug ,slug):
"""
Deletes a customer from the system and deactivates the corresponding user account.
@ -2383,7 +2387,7 @@ def delete_customer(request, slug):
customer = get_object_or_404(models.Customer, slug=slug)
customer.deactivate_account()
messages.success(request, _("Customer deactivated successfully"))
return redirect("customer_list")
return redirect("customer_list", dealer_slug=dealer_slug)
class VendorListView(LoginRequiredMixin, ListView):
@ -3087,7 +3091,6 @@ class OrganizationDetailView(LoginRequiredMixin, DetailView):
template_name = "organizations/organization_detail.html"
context_object_name = "organization"
class OrganizationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
"""
# Handles the creation of a new organization via a web form. This view allows the
@ -3136,6 +3139,9 @@ class OrganizationCreateView(LoginRequiredMixin, PermissionRequiredMixin, Create
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("organization_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]})
class OrganizationUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
"""
@ -3167,9 +3173,11 @@ class OrganizationUpdateView(LoginRequiredMixin, PermissionRequiredMixin, Update
form.instance.update_customer_model()
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("organization_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]})
@login_required
def OrganizationDeleteView(request, slug):
def OrganizationDeleteView(request,dealer_slug, slug):
"""
Handles the deletion of an organization based on the provided primary key (pk). Looks up
the organization and its corresponding user by email, attempts to delete both, and provides
@ -3186,7 +3194,7 @@ def OrganizationDeleteView(request, slug):
organization = get_object_or_404(models.Organization, slug=slug)
organization.deactivate_account()
messages.success(request, _("Organization Deactivated successfully"))
return redirect("organization_list")
return redirect(reverse_lazy("organization_list", kwargs={"dealer_slug": dealer_slug}))
class RepresentativeListView(LoginRequiredMixin, ListView):
@ -5130,7 +5138,7 @@ class LeadDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
@login_required
@permission_required("inventory.add_lead", raise_exception=True)
def lead_create(request):
def lead_create(request,dealer_slug):
"""
Handles the creation of a new lead in the inventory system.
@ -5146,7 +5154,7 @@ def lead_create(request):
:return: An HTTP response object rendering the lead creation form or redirecting to the lead list page upon success.
:rtype: HttpResponse
"""
dealer = get_user_type(request)
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
if request.method == "POST":
form = forms.LeadForm(request.POST)
@ -5201,7 +5209,7 @@ def lead_create(request):
instance.next_action = LeadStatus.NEW
instance.save()
messages.success(request, _("Lead created successfully"))
return redirect("lead_list")
return redirect("lead_list", dealer_slug=dealer.slug)
else:
messages.error(
request, f"Lead was not created ... : {str(form.errors)}"
@ -5234,7 +5242,7 @@ def lead_create(request):
form.fields["staff"].queryset = form.fields["staff"].queryset.filter(
dealer=dealer, staff_type="sales"
)
print(form.fields["staff"].queryset)
if hasattr(request.user.staffmember, "staff"):
form.initial["staff"] = request.user.staffmember.staff
form.fields["staff"].widget.attrs["disabled"] = True
@ -5248,8 +5256,8 @@ def lead_create(request):
return render(request, "crm/leads/lead_form.html", {"form": form})
def lead_tracking(request):
dealer = get_user_type(request)
def lead_tracking(request,dealer_slug):
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
new = models.Lead.objects.filter(dealer=dealer, status="new")
follow_up = models.Lead.objects.filter(
dealer=dealer, next_action__in=["call", "meeting"]
@ -5268,7 +5276,7 @@ def lead_tracking(request):
# @require_POST
def update_lead_actions(request):
def update_lead_actions(request,dealer_slug):
try:
lead_id = request.POST.get("lead_id")
current_action = request.POST.get("current_action")
@ -5350,11 +5358,13 @@ class LeadUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
"id_car_model"
].queryset = form.instance.id_car_make.carmodel_set.all()
return form
def get_success_url(self):
return reverse_lazy("lead_list",kwargs={"dealer_slug":self.kwargs.get("dealer_slug")})
@login_required
@permission_required("inventory.delete_lead", raise_exception=True)
def LeadDeleteView(request, slug):
def LeadDeleteView(request,dealer_slug, slug):
"""
Handles the deletion of a Lead along with its associated customer and potentially
a related user account in the system. Ensures proper permissions and login
@ -5373,11 +5383,11 @@ def LeadDeleteView(request, slug):
print(e)
lead.delete()
messages.success(request, _("Lead deleted successfully"))
return redirect("lead_list")
return redirect("lead_list",dealer_slug=dealer_slug)
@login_required
def add_note_to_lead(request, slug):
def add_note_to_lead(request,dealer_slug, slug):
"""
Adds a note to a specific lead. This view is accessible only to authenticated
users. The function handles the POST request to create a new note associated
@ -5402,14 +5412,14 @@ def add_note_to_lead(request, slug):
note.created_by = request.user
note.save()
messages.success(request, _("Note added successfully"))
return redirect("lead_detail", slug=lead.slug)
return redirect("lead_detail",dealer_slug=dealer_slug, slug=lead.slug)
else:
form = forms.NoteForm()
return render(request, "crm/note_form.html", {"form": form, "lead": lead})
@login_required
def add_note_to_opportunity(request, slug):
def add_note_to_opportunity(request,dealer_slug, slug):
"""
Add a note to a specific opportunity identified by its primary key.
@ -5424,7 +5434,7 @@ def add_note_to_opportunity(request, slug):
:return: A redirect response to the detailed view of the opportunity.
"""
opportunity = get_object_or_404(models.Opportunity, slug=slug)
dealer = get_user_type(request)
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
if request.method == "POST":
notes = request.POST.get("notes")
if not notes:
@ -5437,11 +5447,11 @@ def add_note_to_opportunity(request, slug):
note=notes,
)
messages.success(request, _("Note added successfully"))
return redirect("opportunity_detail", slug=opportunity.slug)
return redirect("opportunity_detail", dealer_slug=dealer_slug,slug=opportunity.slug)
@login_required
def delete_note(request, pk):
def delete_note(request,dealer_slug, pk):
"""
Deletes a specific note created by the currently logged-in user and redirects
to the lead detail page. If the note does not exist or the user is not the creator,
@ -5460,12 +5470,12 @@ def delete_note(request, pk):
lead = models.Lead.objects.get(pk=lead_pk)
note.delete()
messages.success(request, _("Note deleted successfully."))
return redirect("lead_detail", slug=lead.slug)
return redirect("lead_detail", dealer_slug=dealer_slug,slug=lead.slug)
@login_required
@permission_required("inventory.change_lead", raise_exception=True)
def lead_convert(request, slug):
def lead_convert(request,dealer_slug, slug):
"""
Converts a lead into a customer and creates a corresponding opportunity.
@ -5483,7 +5493,7 @@ def lead_convert(request, slug):
:rtype: HttpResponse
"""
lead = get_object_or_404(models.Lead, slug=slug)
dealer = get_user_type(request)
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
if hasattr(lead, "opportunity"):
messages.error(request, _("Lead is already converted to customer"))
else:
@ -5497,12 +5507,12 @@ def lead_convert(request, slug):
staff=lead.staff,
)
messages.success(request, _("Lead converted to customer successfully"))
return redirect("lead_list")
return redirect("lead_list",dealer_slug=dealer_slug)
@login_required
@permission_required("inventory.add_lead", raise_exception=True)
def schedule_lead(request, slug):
def schedule_lead(request, dealer_slug,slug):
"""
Handles the scheduling of a lead for an appointment.
@ -5523,8 +5533,8 @@ def schedule_lead(request, slug):
if not request.is_staff:
messages.error(request, _("You do not have permission to schedule lead"))
return redirect("lead_list")
dealer = get_user_type(request)
return redirect("lead_list", dealer_slug=dealer_slug)
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
lead = get_object_or_404(models.Lead, slug=slug, dealer=dealer)
if request.method == "POST":
form = forms.ScheduleForm(request.POST)
@ -5547,7 +5557,7 @@ def schedule_lead(request, slug):
)
except ValidationError as e:
messages.error(request, str(e))
return redirect("schedule_lead", slug=lead.slug)
return redirect("schedule_lead", dealer_slug=lead.dealer.slug, slug=lead.slug)
client = get_object_or_404(User, email=lead.email)
# Create Appointment
@ -5564,17 +5574,17 @@ def schedule_lead(request, slug):
if lead.opportunity:
return redirect("opportunity_detail", slug=lead.opportunity.slug)
except models.Lead.opportunity.RelatedObjectDoesNotExist:
return redirect("lead_list")
return redirect("lead_list", dealer_slug=lead.dealer.slug)
else:
messages.error(request, f"Invalid form data: {str(form.errors)}")
return redirect("schedule_lead", slug=lead.slug)
return redirect("schedule_lead", dealer_slug=dealer_slug,slug=lead.slug)
form = forms.ScheduleForm()
return render(request, "crm/leads/schedule_lead.html", {"lead": lead, "form": form})
@login_required
@permission_required("inventory.change_lead", raise_exception=True)
def lead_transfer(request, slug):
def lead_transfer(request,dealer_slug, slug):
"""
Handles the transfer of a lead to a different staff member. This view is accessible
only to authenticated users with the 'inventory.change_lead' permission. If the
@ -5586,6 +5596,7 @@ def lead_transfer(request, slug):
:param pk: The primary key of the lead to be transferred.
:return: An HTTP redirect response to the lead list view.
"""
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
lead = get_object_or_404(models.Lead, slug=slug)
if request.method == "POST":
form = forms.LeadTransferForm(request.POST)
@ -5595,11 +5606,11 @@ def lead_transfer(request, slug):
messages.success(request, _("Lead transferred successfully"))
else:
messages.error(request, f"Invalid form data: {str(form.errors)}")
return redirect("lead_list")
return redirect("lead_list", dealer_slug=dealer.slug)
@login_required
def send_lead_email(request, slug, email_pk=None):
def send_lead_email(request,dealer_slug, slug, email_pk=None):
"""
Handles sending emails related to a lead. Supports creating drafts, sending emails, and rendering
an email composition page. Changes on the lead or email objects, such as marking a lead as contacted
@ -5619,9 +5630,9 @@ def send_lead_email(request, slug, email_pk=None):
or email composition rendering, a response object is returned to render the respective page.
Type: HttpResponse
"""
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
lead = get_object_or_404(models.Lead, slug=slug)
status = request.GET.get("status")
dealer = get_user_type(request)
if status == "draft":
models.Email.objects.create(
content_object=lead,
@ -5643,17 +5654,17 @@ def send_lead_email(request, slug, email_pk=None):
try:
if lead.opportunity:
response = HttpResponse(
redirect("opportunity_detail", slug=lead.opportunity.slug)
redirect("opportunity_detail", dealer_slug=dealer_slug,slug=lead.opportunity.slug)
)
response["HX-Redirect"] = reverse(
"opportunity_detail", args=[lead.opportunity.slug]
)
else:
response = HttpResponse(redirect("lead_detail", slug=lead.slug))
response["HX-Redirect"] = reverse("lead_detail", args=[lead.slug])
response = HttpResponse(redirect("lead_detail", dealer_slug=dealer_slug,slug=lead.slug))
response["HX-Redirect"] = reverse("lead_detail", dealer_slug=dealer_slug,slug=lead.slug)
return response
except models.Lead.opportunity.RelatedObjectDoesNotExist:
return redirect("lead_list")
return redirect("lead_list",dealer_slug=dealer.slug)
if request.method == "POST":
email_pk = request.POST.get("email_pk")
@ -5677,7 +5688,6 @@ def send_lead_email(request, slug, email_pk=None):
request.POST.get("subject"),
request.POST.get("message"),
)
dealer = get_user_type(request)
models.Activity.objects.create(
dealer=dealer,
content_object=lead,
@ -5688,9 +5698,9 @@ def send_lead_email(request, slug, email_pk=None):
messages.success(request, _("Email sent successfully"))
try:
if lead.opportunity:
return redirect("opportunity_detail", slug=lead.opportunity.slug)
return redirect("opportunity_detail", dealer_slug=dealer_slug,slug=lead.opportunity.slug)
except models.Lead.opportunity.RelatedObjectDoesNotExist:
return redirect("lead_list")
return redirect("lead_list",dealer_slug=dealer_slug)
msg = f"""
السلام عليكم
Dear {lead.full_name},
@ -5784,7 +5794,7 @@ class OpportunityCreateView(CreateView, SuccessMessageMixin, LoginRequiredMixin)
def get_initial(self):
initial = super().get_initial()
dealer = get_user_type(self.request)
dealer = get_object_or_404(models.Dealer,slug=self.kwargs.get("dealer_slug"))
if self.kwargs.get("slug", None):
lead = models.Lead.objects.get(slug=self.kwargs.get("slug"), dealer=dealer)
initial["lead"] = lead
@ -5792,7 +5802,7 @@ class OpportunityCreateView(CreateView, SuccessMessageMixin, LoginRequiredMixin)
return initial
def form_valid(self, form):
dealer = get_user_type(self.request)
dealer = get_object_or_404(models.Dealer,slug=self.kwargs.get("dealer_slug"))
instance = form.save(commit=False)
instance.dealer = dealer
instance.staff = instance.lead.staff
@ -5802,7 +5812,7 @@ class OpportunityCreateView(CreateView, SuccessMessageMixin, LoginRequiredMixin)
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("opportunity_detail", kwargs={"slug": self.object.slug})
return reverse_lazy("opportunity_detail", kwargs={"dealer_slug":self.kwargs.get("dealer_slug"),"slug": self.object.slug})
class OpportunityUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
@ -5834,7 +5844,7 @@ class OpportunityUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView)
success_message = "Opportunity updated successfully."
def get_success_url(self):
return reverse_lazy("opportunity_detail", kwargs={"slug": self.object.slug})
return reverse_lazy("opportunity_detail", kwargs={"dealer_slug":self.kwargs.get("dealer_slug"),"slug": self.object.slug})
class OpportunityDetailView(LoginRequiredMixin, DetailView):
@ -5861,7 +5871,7 @@ class OpportunityDetailView(LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form = forms.OpportunityStatusForm()
url = reverse("opportunity_update_status", args=[self.object.slug])
url = reverse("opportunity_update_status", kwargs={"dealer_slug": self.kwargs["dealer_slug"], "slug": self.object.slug})
form.fields["status"].widget.attrs["hx-get"] = url
form.fields["stage"].widget.attrs["hx-get"] = url
form.fields["stage"].initial = self.object.stage
@ -5955,7 +5965,7 @@ class OpportunityListView(LoginRequiredMixin, ListView):
@login_required
def delete_opportunity(request, pk):
def delete_opportunity(request,dealer_slug, pk):
"""
Deletes an opportunity object from the database and redirects to the opportunity
list view. If the opportunity with the specified primary key is not found, a 404
@ -5968,14 +5978,15 @@ def delete_opportunity(request, pk):
:return: An HTTP response redirecting to the opportunity list view.
:rtype: HttpResponse
"""
get_object_or_404(models.Dealer, slug=dealer_slug)
opportunity = get_object_or_404(models.Opportunity, pk=pk)
opportunity.delete()
messages.success(request, _("Opportunity deleted successfully"))
return redirect("opportunity_list")
return redirect("opportunity_list",dealer_slug=dealer_slug)
@login_required
def opportunity_update_status(request, slug):
def opportunity_update_status(request,dealer_slug, slug):
"""
Update the status and/or stage of a specific Opportunity instance. This is a
view function, which is generally tied to a URL endpoint in a Django application.
@ -6009,7 +6020,7 @@ def opportunity_update_status(request, slug):
opportunity.stage = stage
opportunity.save()
messages.success(request, _("Opportunity status updated successfully"))
response = HttpResponse(redirect("opportunity_detail", slug=opportunity.slug))
response = HttpResponse(redirect("opportunity_detail",dealer_slug=dealer_slug, slug=opportunity.slug))
response["HX-Refresh"] = "true"
return response
@ -8160,7 +8171,7 @@ def DealerSettingsView(request, slug):
@login_required
def schedule_cancel(request, pk):
def schedule_cancel(request,dealer_slug, pk):
"""
Cancel a schedule by updating its status to "Canceled". The function is protected
by a login requirement, ensuring only authenticated users can execute it. It
@ -8174,6 +8185,7 @@ def schedule_cancel(request, pk):
:return: An HTTP response object with a 200 status code upon successful execution.
:rtype: HttpResponse
"""
get_object_or_404(models.Dealer, slug=dealer_slug)
schedule = get_object_or_404(models.Schedule, pk=pk)
schedule.status = "Canceled"
schedule.save()
@ -8850,14 +8862,14 @@ def notifications_history(request):
# return render(request, 'activity_history.html')
def add_activity(request, content_type, slug):
def add_activity(request,dealer_slug, content_type, slug):
try:
model = apps.get_model(f"inventory.{content_type}")
except LookupError:
raise Http404("Model not found")
obj = get_object_or_404(model, slug=slug)
dealer = get_user_type(request)
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
if request.method == "POST":
form = forms.ActivityForm(request.POST)
if form.is_valid():
@ -8872,17 +8884,17 @@ def add_activity(request, content_type, slug):
messages.success(request, _("Activity added successfully"))
else:
messages.error(request, _("Activity form is not valid"))
return redirect(f"{content_type}_detail", slug=slug)
return redirect(f"{content_type}_detail",dealer_slug=dealer_slug, slug=slug)
def add_task(request, content_type, slug):
def add_task(request,dealer_slug, content_type, slug):
try:
model = apps.get_model(f"inventory.{content_type}")
except LookupError:
raise Http404("Model not found")
obj = get_object_or_404(model, slug=slug)
dealer = get_user_type(request)
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
if request.method == "POST":
form = forms.StaffTaskForm(request.POST)
if form.is_valid():
@ -8895,12 +8907,11 @@ def add_task(request, content_type, slug):
task.save()
messages.success(request, _("Task added successfully"))
else:
print(form.errors)
messages.error(request, _("Task form is not valid"))
return redirect(f"{content_type}_detail", slug=slug)
return redirect(f"{content_type}_detail",dealer_slug=dealer_slug, slug=slug)
def update_task(request, pk):
def update_task(request,dealer_slug, pk):
task = get_object_or_404(models.Tasks, pk=pk)
if request.method == "POST":
@ -8912,14 +8923,14 @@ def update_task(request, pk):
return render(request, "partials/task.html", {"task": task})
def add_note(request, content_type, slug):
def add_note(request,dealer_slug, content_type, slug):
try:
model = apps.get_model(f"inventory.{content_type}")
except LookupError:
raise Http404("Model not found")
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
obj = get_object_or_404(model, slug=slug)
dealer = get_user_type(request)
if request.method == "POST":
form = forms.NoteForm(request.POST)
if form.is_valid():
@ -8931,20 +8942,19 @@ def add_note(request, content_type, slug):
note.save()
messages.success(request, _("Note added successfully"))
else:
print(form.errors)
messages.error(request, _("Note form is not valid"))
return redirect(f"{content_type}_detail", slug=slug)
return redirect(f"{content_type}_detail",dealer_slug=dealer_slug, slug=slug)
def update_note(request, pk):
def update_note(request,dealer_slug, pk):
note = get_object_or_404(models.Notes, pk=pk)
lead = get_object_or_404(models.Lead, pk=note.content_object.id)
dealer = get_user_type(request)
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
if request.method == "POST":
note.note = request.POST.get("note")
note.save()
messages.success(request, _("Note updated successfully"))
return redirect("lead_detail", slug=lead.slug)
return redirect("lead_detail",dealer_slug=dealer_slug, slug=lead.slug)
else:
messages.error(request, _("Note form is not valid"))
notes = models.Notes.objects.filter(

View File

@ -10,7 +10,7 @@
</button>
</div>
<div class="modal-body">
<form action="{% url 'add_activity' content_type=content_type slug=slug %}" method="post" class="add_activity_form">
<form action="{% url 'add_activity' dealer_slug=request.dealer.slug content_type=content_type slug=slug %}" method="post" class="add_activity_form">
{% csrf_token %}
<div class="mb-2 form-group">
<select class="form-select" name="activity_type" id="activity_type">

View File

@ -10,7 +10,7 @@
</button>
</div>
<div class="modal-body">
<form action="{% url 'add_task' content_type slug %}" method="post" class="add_task_form">
<form action="{% url 'add_task' request.dealer.slug content_type slug %}" method="post" class="add_task_form">
{% csrf_token %}
{{ staff_task_form|crispy }}
<button type="submit" class="btn btn-phoenix-success w-100">{% trans 'Save' %}</button>

View File

@ -181,7 +181,7 @@
<div class="modal fade" id="exampleModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form class="modal-content" action="{% url 'lead_transfer' lead.slug %}" method="post">
<form class="modal-content" action="{% url 'lead_transfer' request.dealer.slug lead.slug %}" method="post">
{% csrf_token %}
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Reassign Lead To Another Employee</h5>
@ -252,7 +252,7 @@
<div class="tab-pane fade active show" id="tab-opportunity" role="tabpanel" aria-labelledby="opportunity-tab">
<div class="mb-1 d-flex justify-content-between align-items-center">
<h3 class="mb-4" id="scrollspyTask">{{ _("Opportunities") }} <span class="fw-light fs-7">({{ lead.get_opportunities.count}})</span></h3>
<a href="{% url 'opportunity_create' %}" class="btn btn-phoenix-primary btn-sm" type="button"> <i class="fa-solid fa-plus me-2"></i>{{ _("Add Opportunity") }}</a>
<a href="{% url 'lead_opportunity_create' request.dealer.slug lead.slug %}" class="btn btn-phoenix-primary btn-sm" type="button"> <i class="fa-solid fa-plus me-2"></i>{{ _("Add Opportunity") }}</a>
</div>
<div class="border-top border-bottom border-translucent" id="leadDetailsTable">
@ -272,7 +272,7 @@
<td class="align-middle text-start fw-bold text-body-tertiary ps-1">{{opportunity.car}}</td>
<td class="align-middle text-start fw-bold text-body-tertiary ps-1">{{opportunity.probability}}</td>
<td class="align-middle text-start fw-bold text-body-tertiary ps-1">{{opportunity.priority|capfirst}}</td>
<td class="align-middle text-start fw-bold text-body-tertiary ps-1"><a class="btn btn-sm btn-phoenix-primary" href="{% url 'opportunity_detail' opportunity.slug %}">View</a></td>
<td class="align-middle text-start fw-bold text-body-tertiary ps-1"><a class="btn btn-sm btn-phoenix-primary" href="{% url 'opportunity_detail' request.dealer.slug opportunity.slug %}">View</a></td>
</tr>
{% endfor %}
</tbody>
@ -316,14 +316,14 @@
class="btn btn-sm btn-phoenix-primary me-2"
data-pk="{{ note.pk }}"
data-note="{{ note.note|escapejs }}"
data-url="{% url 'update_note' note.pk %}"
data-url="{% url 'update_note' request.dealer.slug note.pk %}"
data-bs-toggle="modal"
data-bs-target="#noteModal"
data-note-title="{{ _('Update') }}<i class='fas fa-pen-square text-primary ms-2'></i>">
{{ _("Update") }}
</a>
<button class="btn btn-phoenix-danger btn-sm delete-btn"
data-url="{% url 'delete_note_to_lead' note.pk %}"
data-url="{% url 'delete_note_to_lead' request.dealer.slug note.pk %}"
data-message="Are you sure you want to delete this note?"
data-bs-toggle="modal" data-bs-target="#deleteModal">
<i class="fas fa-trash"></i>
@ -340,7 +340,7 @@
<div class="tab-pane fade" id="tab-emails" role="tabpanel" aria-labelledby="emails-tab">
<div class="mb-1 d-flex justify-content-between align-items-center">
<h3 class="mb-0" id="scrollspyEmails">{{ _("Emails") }}</h3>
<a href="{% url 'send_lead_email' lead.slug %}">
<a href="{% url 'send_lead_email' request.dealer.slug lead.slug %}">
<button type="button" class="btn btn-sm btn-phoenix-primary">
<span class="fas fa-plus me-1"></span>
{% trans 'Send Email' %}
@ -535,7 +535,7 @@
</button>
</div>
<div class="modal-body">
<form action="{% url 'add_task' 'lead' lead.slug %}" method="post" class="add_task_form">
<form action="{% url 'add_task' request.dealer.slug 'lead' lead.slug %}" method="post" class="add_task_form">
{% csrf_token %}
{{ staff_task_form|crispy }}
<button type="submit" class="btn btn-phoenix-success w-100">{% trans 'Save' %}</button>
@ -555,7 +555,7 @@
</button>
</div>
<div class="modal-body">
<form action="{% url 'add_note' 'lead' lead.slug %}" method="post" class="add_note_form">
<form action="{% url 'add_note' request.dealer.slug 'lead' lead.slug %}" method="post" class="add_note_form">
{% csrf_token %}
{{ note_form|crispy }}
<button type="submit" class="btn btn-phoenix-success w-100">{% trans 'Save' %}</button>
@ -578,7 +578,7 @@
function reset_form() {
document.querySelector('#id_note').value = ""
let form = document.querySelector('.add_note_form')
form.action = "{% url 'add_note' 'lead' lead.slug %}"
form.action = "{% url 'add_note' request.dealer.slug 'lead' lead.slug %}"
}
let Toast = Swal.mixin({
toast: true,
@ -631,7 +631,7 @@
}
});
fetch("{% url 'update_lead_actions' %}", {
fetch("{% url 'update_lead_actions' request.dealer.slug %}", {
method: 'POST',
body: formData,
headers: {

View File

@ -13,7 +13,7 @@
<div class="d-md-flex justify-content-between">
{% if perms.inventory.add_lead %}
<div>
<a href="{% url 'lead_create' %}" class="btn btn-sm btn-phoenix-primary"><span class="fas fa-plus me-2"></span>{{ _("Add Lead") }}</a>
<a href="{% url 'lead_create' request.dealer.slug %}" class="btn btn-sm btn-phoenix-primary"><span class="fas fa-plus me-2"></span>{{ _("Add Lead") }}</a>
</div>
{% endif %}
</div>
@ -93,7 +93,7 @@
<p>{% trans "Are you sure you want to delete this lead?" %}</p>
</div>
<div class="modal-footer flex justify-content-center border-top-0">
<a type="button" class="btn btn-sm btn-phoenix-danger w-100" href="{% url 'lead_delete' lead.slug %}">
<a type="button" class="btn btn-sm btn-phoenix-danger w-100" href="{% url 'lead_delete' request.dealer.slug lead.slug %}">
{% trans "Yes" %}
</a>
</div>
@ -105,7 +105,7 @@
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="name align-middle white-space-nowrap ps-0">
<div class="d-flex align-items-center">
<div><a class="fs-8 fw-bold" href="{% url 'lead_detail' lead.slug %}">{{lead.full_name|capfirst}}</a>
<div><a class="fs-8 fw-bold" href="{% url 'lead_detail' request.dealer.slug lead.slug %}">{{lead.full_name|capfirst}}</a>
<div class="d-flex align-items-center">
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2"></p>
{% if lead.status == "new" %}
@ -210,15 +210,15 @@
</button>
<div class="dropdown-menu dropdown-menu-end py-2">
{% if perms.inventory.change_lead %}
<a href="{% url 'lead_update' lead.slug %}" class="dropdown-item text-success-dark">{% trans "Edit" %}</a>
<a href="{% url 'lead_update' request.dealer.slug lead.slug %}" class="dropdown-item text-success-dark">{% trans "Edit" %}</a>
{% endif %}
<button class="dropdown-item text-primary" onclick="openActionModal('{{ lead.pk }}', '{{ lead.action }}', '{{ lead.next_action }}', '{{ lead.next_action_date|date:"Y-m-d\TH:i" }}')">
{% trans "Update Actions" %}
</button>
<a href="{% url 'send_lead_email' lead.slug %}" class="dropdown-item text-success-dark">{% trans "Send Email" %}</a>
<a href="{% url 'schedule_lead' lead.slug %}" class="dropdown-item text-success-dark">{% trans "Schedule Event" %}</a>
<a href="{% url 'send_lead_email' request.dealer.slug lead.slug %}" class="dropdown-item text-success-dark">{% trans "Send Email" %}</a>
<a href="{% url 'schedule_lead' request.dealer.slug lead.slug %}" class="dropdown-item text-success-dark">{% trans "Schedule Event" %}</a>
{% if not lead.opportunity %}
<a href="{% url 'lead_opportunity_create' lead.slug %}" class="dropdown-item text-success-dark">{% trans "Convert to Opportunity" %}</a>
<a href="{% url 'lead_opportunity_create' request.dealer.slug lead.slug %}" class="dropdown-item text-success-dark">{% trans "Convert to Opportunity" %}</a>
{% endif %}
<div class="dropdown-divider"></div>
{% if perms.inventory.delete_lead %}
@ -310,7 +310,7 @@
}
});
fetch("{% url 'update_lead_actions' %}", {
fetch("{% url 'update_lead_actions' request.dealer.slug %}", {
method: 'POST',
body: formData,
headers: {

View File

@ -8,7 +8,7 @@
<div class="card email-content">
<h5 class="card-header">Send Mail</h5>
<div class="card-body">
<form class="d-flex flex-column h-100" action="{% url 'send_lead_email' lead.slug %}" method="post">
<form class="d-flex flex-column h-100" action="{% url 'send_lead_email' request.dealer.slug lead.slug %}" method="post">
{% csrf_token %}
<div class="row g-3 mb-2">
<div class="col-12">
@ -27,7 +27,7 @@
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex gap-2">
<a href="{{ request.META.HTTP_REFERER }}" class="btn btn-phoenix-danger">Discard</a>
<a hx-boost="true" hx-push-url='false' hx-include="#message,#subject,#to" href="{% url 'send_lead_email' lead.slug %}?status=draft" class="btn btn-phoenix-success">Save as Draft</a>
<a hx-boost="true" hx-push-url='false' hx-include="#message,#subject,#to" href="{% url 'send_lead_email' request.dealer.slug lead.slug %}?status=draft" class="btn btn-phoenix-success">Save as Draft</a>
<button class="btn btn-phoenix-primary fs-10" type="submit">Send<span class="fa-solid fa-paper-plane ms-1"></span></button>
</div>
</div>

View File

@ -14,7 +14,7 @@
>
{{ _("Save") }}
</button>
<a href="{% url 'lead_list' %}" class="btn btn-phoenix-secondary">
<a href="{% url 'lead_list' request.dealer.slug %}" class="btn btn-phoenix-secondary">
{{ _("Cancel") }}
</a>
</form>

View File

@ -6,6 +6,6 @@
<form method="post">
{% csrf_token %}
<button type="submit">Yes, delete</button>
<a href="{% url 'opportunity_list' %}">Cancel</a>
<a href="{% url 'opportunity_list' request.dealer.slug %}">Cancel</a>
</form>
{% endblock %}

View File

@ -15,11 +15,11 @@
{% if opportunity.estimate %}
<a class="dropdown-item" href="{% url 'estimate_detail' opportunity.estimate.pk %}">{{ _("View Quotation")}}</a>
{% else %}
<a class="dropdown-item" href="{% url 'estimate_create_from_opportunity' opportunity.slug %}">{{ _("Create Quotation")}}</a>
<a class="dropdown-item" href="{% url 'estimate_create_from_opportunity' request.dealer.slug opportunity.slug %}">{{ _("Create Quotation")}}</a>
{% endif %}
</li>
<li><a class="dropdown-item" href="{% url 'update_opportunity' opportunity.slug %}">Update Opportunity</a></li>
<li><a class="dropdown-item" href="{% url 'update_opportunity' opportunity.slug %}">Update Stage</a></li>
<li><a class="dropdown-item" href="{% url 'update_opportunity' request.dealer.slug opportunity.slug %}">Update Opportunity</a></li>
<li><a class="dropdown-item" href="{% url 'update_opportunity' request.dealer.slug opportunity.slug %}">Update Stage</a></li>
<li><a class="dropdown-item text-danger" href="">Delete Opportunity</a></li>
</ul>
</div>
@ -392,7 +392,7 @@
</div>
<div class="tab-pane fade" id="tab-notes" role="tabpanel" aria-labelledby="notes-tab">
<h2 class="mb-4">Notes</h2>
<form action="{% url 'add_note_to_opportunity' opportunity.slug %}" method="post">
<form action="{% url 'add_note_to_opportunity' request.dealer.slug opportunity.slug %}" method="post">
{% csrf_token %}
<textarea class="form-control mb-3" id="notes" rows="4" name="notes" required> </textarea>
<button type="submit" class="btn btn-phoenix-primary mb-3">Add Note</button>
@ -428,7 +428,7 @@
<button class="btn btn-link p-0 ms-3 fs-9 text-primary fw-bold text-decoration-none"><span class="fas fa-sort me-1 fw-extra-bold fs-10"></span>Sorting</button>
</div>
<div class="col-auto">
<a href="{% url 'schedule_lead' opportunity.lead.slug %}" class="btn btn-phoenix-primary"><span class="fa-solid fa-plus me-2"></span>Add Meeting </a>
<a href="{% url 'schedule_lead' request.dealer.slug opportunity.lead.slug %}" class="btn btn-phoenix-primary"><span class="fa-solid fa-plus me-2"></span>Add Meeting </a>
</div>
</div>
<div class="row g-3">
@ -456,7 +456,7 @@
</div>
<div class="col-auto">
<a href="{% url 'schedule_lead' opportunity.lead.slug %}" class="btn btn-phoenix-primary"><span class="fa-solid fa-plus me-2"></span>Add Call</a>
<a href="{% url 'schedule_lead' request.dealer.slug opportunity.lead.slug %}" class="btn btn-phoenix-primary"><span class="fa-solid fa-plus me-2"></span>Add Call</a>
</div>
</div>
<pre>{{opportunity.get_all_notes}}</pre>
@ -504,7 +504,7 @@
<div class="tab-pane fade" id="tab-emails" role="tabpanel" aria-labelledby="emails-tab">
<h2 class="mb-4">Emails</h2>
<div class="d-flex justify-content-end">
<a href="{% url 'send_lead_email' opportunity.lead.slug %}">
<a href="{% url 'send_lead_email' request.dealer.slug opportunity.lead.slug %}">
<button type="button" class="btn btn-sm btn-phoenix-primary">
<span class="fas fa-plus me-1"></span>
{% trans 'Send Email' %}

View File

@ -21,7 +21,7 @@
</h2>
</div>
<div class="col-auto">
<a href="{% url 'opportunity_list' %}" class="btn btn-phoenix-secondary">
<a href="{% url 'opportunity_list' request.dealer.slug %}" class="btn btn-phoenix-secondary">
<span class="fas fa-arrow-left me-2"></span>{% trans "Back to list" %}
</a>
</div>

View File

@ -19,7 +19,7 @@
type="text"
placeholder="{% trans 'Search opportunities...' %}"
name="search"
hx-get="{% url 'opportunity_list' %}"
hx-get="{% url 'opportunity_list' request.dealer.slug %}"
hx-trigger="keyup changed delay:500ms"
hx-target="#opportunities-grid"
hx-select="#opportunities-grid"
@ -36,7 +36,7 @@
<select
class="form-select"
name="stage"
hx-get="{% url 'opportunity_list' %}"
hx-get="{% url 'opportunity_list' request.dealer.slug %}"
hx-trigger="change"
hx-target="#opportunities-grid"
hx-select="#opportunities-grid"
@ -57,7 +57,7 @@
<select
class="form-select"
name="sort"
hx-get="{% url 'opportunity_list' %}"
hx-get="{% url 'opportunity_list' request.dealer.slug %}"
hx-trigger="change"
hx-target="#opportunities-grid"
hx-select="#opportunities-grid"
@ -79,7 +79,7 @@
</div>
<div class="d-flex justify-content-end">
<a class="btn btn-phoenix-primary btn-sm" href="{% url 'opportunity_create' %}">
<a class="btn btn-phoenix-primary btn-sm" href="{% url 'opportunity_create' request.dealer.slug %}">
<span class="fas fa-plus me-2"></span>{{ _("Add Opportunity") }}
</a>
</div>

View File

@ -119,10 +119,10 @@
</div>
<div class="d-flex gap-2">
<a class="btn btn-sm btn-phoenix-primary" href="{% url 'opportunity_detail' opportunity.slug %}">
<a class="btn btn-sm btn-phoenix-primary" href="{% url 'opportunity_detail' request.dealer.slug opportunity.slug %}">
{{ _("View Details") }} <i class="fa-solid fa-eye ms-2"></i>
</a>
<a class="btn btn-sm btn-phoenix-success" href="{% url 'update_opportunity' opportunity.slug %}">
<a class="btn btn-sm btn-phoenix-success" href="{% url 'update_opportunity' request.dealer.slug opportunity.slug %}">
{{ _("Update") }} <i class="fa-solid fa-pen ms-2"></i>
</a>
</div>

View File

@ -12,7 +12,7 @@
<div class="d-md-flex justify-content-between">
{% if perms.django_ledger.add_customermodel %}
<div>
<a href="{% url 'customer_create' %}" class="btn btn-sm btn-phoenix-primary me-4"><span class="fas fa-plus me-2"></span>{{ _("Add Customer") }}</a>
<a href="{% url 'customer_create' request.dealer.slug %}" class="btn btn-sm btn-phoenix-primary me-4"><span class="fas fa-plus me-2"></span>{{ _("Add Customer") }}</a>
</div>
{% endif %}
</div>
@ -70,7 +70,7 @@
</td>
<td class="name align-middle white-space-nowrap ps-0">
<div class="d-flex align-items-center">
<div><a class="fs-8 fw-bold" href="{% url 'customer_detail' customer.slug %}">{{ customer.full_name }}</a>
<div><a class="fs-8 fw-bold" href="{% url 'customer_detail' request.dealer.slug customer.slug %}">{{ customer.full_name }}</a>
<div class="d-flex align-items-center">
</div>
</div>
@ -91,13 +91,13 @@
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ customer.created|date }}</td>
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
{% if perms.django_ledger.change_customermodel %}
<a href="{% url 'customer_update' customer.slug %}" class="btn btn-sm btn-phoenix-primary me-2" data-url="{% url 'customer_update' customer.slug %}">
<a href="{% url 'customer_update' request.dealer.slug customer.slug %}" class="btn btn-sm btn-phoenix-primary me-2" data-url="{% url 'customer_update' request.dealer.slug customer.slug %}">
<i class="fas fa-pen"></i>
</a>
{% endif %}
{% if perms.django_ledger.delete_customermodel %}
<button class="btn btn-phoenix-danger btn-sm delete-btn"
data-url="{% url 'customer_delete' customer.slug %}"
data-url="{% url 'customer_delete' request.dealer.slug customer.slug %}"
data-message="{{ _("Are you sure you want to delete this customer")}}"
data-bs-toggle="modal" data-bs-target="#deleteModal">
<i class="fas fa-trash"></i>

View File

@ -1,6 +1,6 @@
{% load i18n static crispy_forms_filters %}
<form method="post" action="{% url 'add_note_to_customer' customer.slug %}" enctype="multipart/form-data">
<form method="post" action="{% url 'add_note_to_customer' request.dealer.slug customer.slug %}" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-sm btn-phoenix-success w-100">{{ _("Add") }}</button>

View File

@ -17,7 +17,7 @@
<div class="col-auto">
{% if perms.django_ledger.delete_customermodel %}
<button class="btn btn-phoenix-danger btn-sm delete-btn"
data-url="{% url 'customer_delete' customer.slug %}"
data-url="{% url 'customer_delete' request.dealer.slug customer.slug %}"
data-message="Are you sure you want to delete this customer?"
data-bs-toggle="modal" data-bs-target="#deleteModal">
<i class="fas fa-trash me-1"> </i>{{ _("Delete") }}
@ -27,7 +27,7 @@
<div class="col-auto">
{% if perms.django_ledger.change_customermodel %}
<a href="{% url 'customer_update' customer.slug %}" class="btn btn-sm btn-phoenix-primary"><span class="fa-solid fa-pen-to-square me-2"></span>{{_("Update")}}</a>
<a href="{% url 'customer_update' request.dealer.slug customer.slug %}" class="btn btn-sm btn-phoenix-primary"><span class="fa-solid fa-pen-to-square me-2"></span>{{_("Update")}}</a>
{% endif %}
</div>
</div>
@ -85,10 +85,14 @@
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center justify-content-end">
<a id="addBtn" href="#" class="btn btn-sm btn-phoenix-primary mb-3" data-url="{% url 'add_note_to_customer' customer.slug %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Add") }}<i class='fa fa-plus-circle text-success ms-2'></i>">
<a id="addBtn" href="#" class="btn btn-sm btn-phoenix-primary mb-3" data-url="{% url 'add_note_to_customer' request.dealer.slug customer.slug %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Add") }}<i class='fa fa-plus-circle text-success ms-2'></i>">
<span class="fas fa-plus me-1"></span>
{% trans 'Add Note' %}
</a>
{% comment %} <a id="addBtn" class="btn btn-sm btn-phoenix-primary mb-3" hx-get="{% url 'add_note_to_customer' request.dealer.slug customer.slug %}" hx-target=".modal-body" hx-swap="innerHTML" hx-push-url="false" data-bs-toggle="modal" data-bs-target="#noteModal">
<span class="fas fa-plus me-1"></span>
{% trans 'Add Note' %}
</a> {% endcomment %}
</div>
<table class="table fs-9 mb-0 table-responsive">
<tr>
@ -208,7 +212,7 @@
const modalBody = noteModal.querySelector(".modal-body");
noteModal.addEventListener("show.bs.modal", function (event) {
noteModal.addEventListener("", function (event) {
const button = event.relatedTarget;
const url = button.getAttribute("data-url");
const title = button.getAttribute("data-note-title");

View File

@ -17,12 +17,12 @@
</div>
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 ">
<span class="uil fs-5 lh-1 uil-bolt-alt text-primary"></span>
<a href="{% url 'lead_list' %}"><h4 class="fs-6 pt-3">{{ total_leads }}</h4></a>
<a href="{% url 'lead_list' request.dealer.slug %}"><h4 class="fs-6 pt-3">{{ total_leads }}</h4></a>
<p class="fs-9 mb-0">{{ _("Leads")}}</p>
</div>
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 ">
<span class="uil fs-5 lh-1 uil-user-plus text-warning"></span>
<a href="{% url 'customer_list' %}"><h4 class="fs-6 pt-3">{{ customers }}</h4></a>
<a href="{% url 'customer_list' request.dealer.slug %}"><h4 class="fs-6 pt-3">{{ customers }}</h4></a>
<p class="fs-9 mb-0">{{ _("Customers")}}</p>
</div>
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 ">

View File

@ -138,21 +138,21 @@
<li class="collapsed-nav-item-title d-none">{% trans 'crm'|upper %}</li>
{% if perms.inventory.view_lead %}
<li class="nav-item">
<a class="nav-link" href="{% url 'lead_list' %}">
<a class="nav-link" href="{% url 'lead_list' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span data-feather="users"></span></span><span class="nav-link-text">{% trans 'leads'|capfirst %}</span>
</div>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'lead_tracking' %}">
<a class="nav-link" href="{% url 'lead_tracking' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span data-feather="users"></span></span><span class="nav-link-text">{% trans 'leads Tracking'|capfirst %}</span>
</div>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'opportunity_list' %}">
<a class="nav-link" href="{% url 'opportunity_list' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span data-feather="users"></span></span><span class="nav-link-text">{% trans 'Opportunity'|capfirst %}</span>
</div>
@ -161,7 +161,7 @@
{% endif %}
{% if perms.django_ledger.view_customermodel %}
<li class="nav-item">
<a class="nav-link" href="{% url 'customer_list' %}">
<a class="nav-link" href="{% url 'customer_list' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span data-feather="users"></span></span><span class="nav-link-text">{% trans 'customers'|capfirst %}</span>
</div>
@ -169,7 +169,7 @@
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{% url 'organization_list' %}">
<a class="nav-link" href="{% url 'organization_list' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-city"></span></span><span class="nav-link-text">{% trans "Organizations"|capfirst %}</span>
</div>

View File

@ -18,7 +18,7 @@
<div class="d-md-flex justify-content-between">
{% if perms.django_ledger.add_customermodel %}
<div>
<a href="{% url 'organization_create' %}" class="btn btn-sm btn-phoenix-primary"><span class="fas fa-plus me-2"></span>{% trans 'add organization'|capfirst %}</a>
<a href="{% url 'organization_create' request.dealer.slug %}" class="btn btn-sm btn-phoenix-primary"><span class="fas fa-plus me-2"></span>{% trans 'add organization'|capfirst %}</a>
</div>
{% endif %}
</div>
@ -85,7 +85,7 @@
{% trans 'Are you sure you want to delete this Organization?' %}
</p>
<button type="button" class="btn btn-phoenix-secondary btn-sm" data-bs-dismiss="modal">{% trans 'No' %}</button>
<a type="button" class="btn btn-phoenix-danger btn-sm" href="{% url 'organization_delete' org.slug %}">{% trans 'Yes' %}</a>
<a type="button" class="btn btn-phoenix-danger btn-sm" href="{% url 'organization_delete' request.dealer.slug org.slug %}">{% trans 'Yes' %}</a>
</div>
</div>
</div>
@ -94,7 +94,7 @@
<td class="name align-middle white-space-nowrap ps-0 px-1">
<div class="d-flex align-items-center">
<div>
<a class="fs-8 fw-bold" href="{% url 'organization_detail' org.slug %}">{{ org.name }}</a>
<a class="fs-8 fw-bold" href="{% url 'organization_detail' request.dealer.slug org.slug %}">{{ org.name }}</a>
</div>
</div>
</td>
@ -110,7 +110,7 @@
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2">
<a href="{% url 'organization_update' org.slug %}" class="dropdown-item text-success-dark">{% trans 'Edit' %}</a>
<a href="{% url 'organization_update' request.dealer.slug org.slug %}" class="dropdown-item text-success-dark">{% trans 'Edit' %}</a>
{% if perms.django_ledger.delete_customermodel %}
<div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans 'Delete' %}</button>
{% endif %}

View File

@ -2,7 +2,7 @@
<tr id="task-{{task.pk}}" class="hover-actions-trigger btn-reveal-trigger position-static {% if task.completed %}completed-task{% endif %}">
<td class="fs-9 align-middle px-0 py-3">
<div class="form-check mb-0 fs-8">
<input class="form-check-input" {% if task.completed %}checked{% endif %} type="checkbox" hx-post="{% url 'update_task' task.pk %}" hx-trigger="change" hx-swap="outerHTML" hx-target="#task-{{task.pk}}" />
<input class="form-check-input" {% if task.completed %}checked{% endif %} type="checkbox" hx-post="{% url 'update_task' request.dealer.slug task.pk %}" hx-trigger="change" hx-swap="outerHTML" hx-target="#task-{{task.pk}}" />
</div>
</td>
<td class="subject order align-middle white-space-nowrap py-2 ps-0"><a class="fw-semibold text-primary" href="">{{task.title}}</a>