generic contact form

This commit is contained in:
Tobias Brunner 2025-02-27 15:44:55 +01:00
parent eece05af74
commit 2cbcc4ba98
No known key found for this signature in database
9 changed files with 280 additions and 48 deletions

View file

@ -0,0 +1,39 @@
# Generated by Django 5.1.5 on 2025-02-27 14:33
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("services", "0007_service_is_coming_soon"),
]
operations = [
migrations.AlterField(
model_name="lead",
name="company",
field=models.CharField(blank=True, max_length=200, null=True),
),
migrations.AlterField(
model_name="lead",
name="message",
field=models.TextField(blank=True, max_length=1000, null=True),
),
migrations.AlterField(
model_name="lead",
name="phone",
field=models.CharField(blank=True, max_length=50, null=True),
),
migrations.AlterField(
model_name="lead",
name="service",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="services.service",
),
),
]

View file

@ -276,18 +276,22 @@ class ExternalLink(models.Model):
class Lead(models.Model): class Lead(models.Model):
service = models.ForeignKey(Service, on_delete=models.CASCADE) name = models.CharField(max_length=200)
email = models.EmailField()
company = models.CharField(max_length=200, null=True, blank=True)
phone = models.CharField(max_length=50, null=True, blank=True)
message = models.TextField(blank=True, null=True, max_length=1000)
odoo_lead_id = models.IntegerField(null=True, blank=True)
service = models.ForeignKey(
Service, on_delete=models.SET_NULL, null=True, blank=True
)
offering = models.ForeignKey( offering = models.ForeignKey(
ServiceOffering, on_delete=models.SET_NULL, null=True, blank=True ServiceOffering, on_delete=models.SET_NULL, null=True, blank=True
) )
plan = models.ForeignKey(Plan, on_delete=models.SET_NULL, null=True, blank=True) plan = models.ForeignKey(Plan, on_delete=models.SET_NULL, null=True, blank=True)
name = models.CharField(max_length=200)
company = models.CharField(max_length=200)
email = models.EmailField()
phone = models.CharField(max_length=50)
message = models.TextField(blank=True, max_length=1000)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
odoo_lead_id = models.IntegerField(null=True, blank=True)
def __str__(self): def __str__(self):
return f"{self.name} - {self.company} ({self.service})" return f"{self.name} - {self.company} ({self.service})"

View file

@ -56,36 +56,48 @@ class OdooAPI:
def create_lead(self, lead, source="form"): def create_lead(self, lead, source="form"):
"""Create a lead in Odoo""" """Create a lead in Odoo"""
try: try:
logger.info( logger.info(f"Attempting to create lead for {lead.name}")
f"Attempting to create lead for {lead.name} from {lead.company}"
)
# Get provider name from offering if it exists
provider_name = ""
if hasattr(lead, "offering") and lead.offering:
provider_name = lead.offering.cloud_provider.name
# Prepare service description # Prepare service description
service_details = [] service_details = []
if lead.service:
if hasattr(lead, "service") and lead.service:
service_details.append(f"Service: {lead.service.name}") service_details.append(f"Service: {lead.service.name}")
if provider_name:
service_details.append(f"Provider: {provider_name}") # Get provider name from offering if it exists
if lead.plan: if (
service_details.append(f"Plan: {lead.plan.name}") hasattr(lead, "offering")
and lead.offering
and hasattr(lead.offering, "cloud_provider")
):
provider_name = lead.offering.cloud_provider.name
service_details.append(f"Provider: {provider_name}")
if hasattr(lead, "plan") and lead.plan:
service_details.append(f"Plan: {lead.plan.name}")
if source == "form": if source == "form":
service_details.append(f"Source: Servala Website") service_details.append(f"Source: Servala Website")
elif source == "osbapi": elif source == "osbapi":
service_details.append(f"Source: OSB API") service_details.append(f"Source: OSB API")
elif source == "contact_form":
service_details.append(f"Source: Contact Form")
# Prepare lead name based on whether it's a service lead or generic contact
if hasattr(lead, "service") and lead.service:
lead_name = f"Servala - Interest in {lead.service.name}"
else:
lead_name = "Servala - Website Contact"
# Prepare lead data # Prepare lead data
lead_data = { lead_data = {
"name": f"Servala - Interest in {lead.service.name}", "name": lead_name,
"contact_name": lead.name, "contact_name": lead.name,
"partner_name": lead.company, "partner_name": (
lead.company if hasattr(lead, "company") and lead.company else ""
),
"email_from": lead.email, "email_from": lead.email,
"phone": lead.phone, "phone": lead.phone if hasattr(lead, "phone") and lead.phone else "",
"description": ( "description": (
f"{lead.message}<br/><br/>" + "<br/>".join(service_details) f"{lead.message}<br/><br/>" + "<br/>".join(service_details)
if lead.message if lead.message
@ -111,29 +123,50 @@ class OdooAPI:
logger.info(f"Successfully created lead in Odoo with ID: {odoo_lead_id}") logger.info(f"Successfully created lead in Odoo with ID: {odoo_lead_id}")
# Post message in chatter # Post message in chatter
message_data = { if hasattr(lead, "service") and lead.service:
"body": f""" # For service-related leads
<p>Thank you for your interest in the service <strong>{lead.service.name}</strong>. message_data = {
We recorded the following information and will get back to you soon.</p><br/> "body": f"""
<p>Thank you for your interest in the service <strong>{lead.service.name}</strong>.
<p>Contact Details:</p> We recorded the following information and will get back to you soon:</p><br/>
<ul>
<li><strong>Name:</strong> {lead.name}</li> <p>Contact Details:</p>
<li><strong>Company:</strong> {lead.company}</li> <ul>
<li><strong>Email:</strong> {lead.email}</li> <li><strong>Name:</strong> {lead.name}</li>
<li><strong>Phone:</strong> {lead.phone}</li> <li><strong>Company:</strong> {lead.company if hasattr(lead, "company") and lead.company else "N/A"}</li>
</ul> <li><strong>Email:</strong> {lead.email}</li>
<li><strong>Phone:</strong> {lead.phone if hasattr(lead, "phone") and lead.phone else "N/A"}</li>
</ul>
<p>Service Details:<p> <p>Service Details:<p>
<ul> <ul>
<li><strong>Service:</strong> {lead.service.name}</li> <li><strong>Service:</strong> {lead.service.name}</li>
{f'<li><strong>Provider:</strong> {provider_name}</li>' if provider_name else ''} {f'<li><strong>Provider:</strong> {provider_name}</li>' if provider_name else ''}
{f'<li><strong>Plan:</strong> {lead.plan.name}</li>' if lead.plan else ''} {f'<li><strong>Plan:</strong> {lead.plan.name}</li>' if lead.plan else ''}
</ul> </ul>
""", """,
"message_type": "comment", "message_type": "comment",
"subtype_xmlid": "mail.mt_comment", "subtype_xmlid": "mail.mt_comment",
} }
else:
# For contact form submissions
message_data = {
"body": f"""
<p>Thank you for contacting Servala by VSHN.<br/>
We recorded the following information and will get back to you soon:</p><br/>
<p>Contact Details:</p>
<ul>
<li><strong>Name:</strong> {lead.name}</li>
<li><strong>Email:</strong> {lead.email}</li>
{f'<li><strong>Phone:</strong> {lead.phone}</li>' if lead.phone else ''}
{f'<li><strong>Company:</strong> {lead.company}</li>' if lead.company else ''}
{f'<li><strong>Message:</strong> {lead.message}</li>' if lead.message else ''}
</ul>
""",
"message_type": "comment",
"subtype_xmlid": "mail.mt_comment",
}
# Post the message # Post the message
self.odoo.env["crm.lead"].browse(odoo_lead_id).message_post( self.odoo.env["crm.lead"].browse(odoo_lead_id).message_post(

View file

@ -37,6 +37,9 @@
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:provider_list' %}">Cloud Providers</a></li> <li class="menu__item"><a class="menu__item-link" href="{% url 'services:provider_list' %}">Cloud Providers</a></li>
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:partner_list' %}">Consulting Partners</a></li> <li class="menu__item"><a class="menu__item-link" href="{% url 'services:partner_list' %}">Consulting Partners</a></li>
</ul> </ul>
<ul class="menu-cta mb-0">
<li class="mr-17"><a class="btn btn-outline-light btn-outline-primary" href="{% url 'services:contact' %}" role="button">Contact</a></li>
</ul>
</nav> </nav>
</div> </div>
<div class="nav__toggle"> <div class="nav__toggle">

View file

@ -0,0 +1,82 @@
{% extends 'services/base.html' %}
{% load form_tags %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow-sm">
<div class="card-body p-4">
<h2 class="mb-4">Contact Us</h2>
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label for="{{ form.name.id_for_label }}" class="form-label">Your Name</label>
{{ form.name }}
{% if form.name.errors %}
<div class="invalid-feedback d-block">
{{ form.name.errors }}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.email.id_for_label }}" class="form-label">Your Email Address</label>
{{ form.email }}
{% if form.email.errors %}
<div class="invalid-feedback d-block">
{{ form.email.errors }}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.phone.id_for_label }}" class="form-label">Your Phone Number (Optional)</label>
{{ form.phone }}
{% if form.phone.errors %}
<div class="invalid-feedback d-block">
{{ form.phone.errors }}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.company.id_for_label }}" class="form-label">Your Company (Optional)</label>
{{ form.company }}
{% if form.company.errors %}
<div class="invalid-feedback d-block">
{{ form.company.errors }}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.message.id_for_label }}" class="form-label">Your Message (Optional)</label>
{{ form.message }}
{% if form.message.errors %}
<div class="invalid-feedback d-block">
{{ form.message.errors }}
</div>
{% endif %}
</div>
<div class="mt-4">
<button type="submit" class="btn btn-primary btn-lg">Send Message</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,18 @@
{% extends 'services/base.html' %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card shadow-sm">
<div class="card-body p-5">
<h2 class="mb-4">Thank You!</h2>
<p class="lead">Your message has been sent successfully.</p>
<p>We'll get back to you as soon as possible.</p>
<a href="{% url 'services:homepage' %}" class="btn btn-primary mt-3">Back to Home</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -35,7 +35,7 @@
</div> </div>
<div class="mb-40"> <div class="mb-40">
<label for="{{ form.phone.id_for_label }}" class="form-label text-purple">Your Phone Number</label> <label for="{{ form.phone.id_for_label }}" class="form-label text-purple">Your Phone Number (Optional)</label>
{{ form.phone|addclass:"form-control" }} {{ form.phone|addclass:"form-control" }}
{% if form.phone.errors %} {% if form.phone.errors %}
<div class="invalid-feedback d-block"> <div class="invalid-feedback d-block">
@ -45,7 +45,7 @@
</div> </div>
<div class="mb-40"> <div class="mb-40">
<label for="{{ form.company.id_for_label }}" class="form-label text-purple">Your Company</label> <label for="{{ form.company.id_for_label }}" class="form-label text-purple">Your Company (Optional)</label>
{{ form.company|addclass:"form-control" }} {{ form.company|addclass:"form-control" }}
{% if form.company.errors %} {% if form.company.errors %}
<div class="invalid-feedback d-block"> <div class="invalid-feedback d-block">
@ -55,7 +55,7 @@
</div> </div>
<div class="mb-40"> <div class="mb-40">
<label for="{{ form.message.id_for_label }}" class="form-label text-purple">Message (Optional)</label> <label for="{{ form.message.id_for_label }}" class="form-label text-purple">Your Message (Optional)</label>
{{ form.message|addclass:"form-control" }} {{ form.message|addclass:"form-control" }}
{% if form.message.errors %} {% if form.message.errors %}
<div class="invalid-feedback d-block"> <div class="invalid-feedback d-block">

View file

@ -15,4 +15,6 @@ urlpatterns = [
path("partner/<slug:slug>/", views.partner_detail, name="partner_detail"), path("partner/<slug:slug>/", views.partner_detail, name="partner_detail"),
path("service/<slug:slug>/interest/", views.create_lead, name="create_lead"), path("service/<slug:slug>/interest/", views.create_lead, name="create_lead"),
path("service/<slug:slug>/thank-you/", views.thank_you, name="thank_you"), path("service/<slug:slug>/thank-you/", views.thank_you, name="thank_you"),
path("contact/", views.leads.contact, name="contact"),
path("contact/thank-you/", views.leads.contact_thank_you, name="contact_thank_you"),
] ]

View file

@ -91,3 +91,54 @@ def create_lead(request, slug):
def thank_you(request, slug): def thank_you(request, slug):
service = get_object_or_404(Service, slug=slug) service = get_object_or_404(Service, slug=slug)
return render(request, "services/thank_you.html", {"service": service}) return render(request, "services/thank_you.html", {"service": service})
def contact(request):
if request.method == "POST":
form = LeadForm(request.POST)
if form.is_valid():
# Create a minimal Lead object
from hub.services.models import Lead
lead = Lead(
name=form.cleaned_data["name"],
email=form.cleaned_data["email"],
message=form.cleaned_data["message"],
company=form.cleaned_data["company"],
phone=form.cleaned_data["phone"],
)
try:
logger.info(f"Attempting to create contact lead from {lead.name}")
odoo = OdooAPI()
odoo_lead_id = odoo.create_lead(lead, source="contact_form")
lead.odoo_lead_id = odoo_lead_id
lead.save()
logger.info(
f"Successfully created contact lead with Odoo ID: {odoo_lead_id}"
)
messages.success(
request, "Thank you for your message. We'll get back to you soon!"
)
return redirect("services:contact_thank_you")
except Exception as e:
logger.error(f"Failed to create contact lead: {str(e)}", exc_info=True)
error_message = "Sorry, there was an error processing your request. Please try again later."
if settings.DEBUG:
error_message += f" Error: {str(e)}"
messages.error(request, error_message)
else:
form = LeadForm()
context = {
"form": form,
}
return render(request, "services/contact_form.html", context)
def contact_thank_you(request):
return render(request, "services/contact_thank_you.html")