opportunity stage update logic

This commit is contained in:
Faheedkhan 2025-08-05 13:51:05 +03:00
parent 699b2dfdbd
commit a6461f34e0
9 changed files with 132 additions and 16 deletions

View File

@ -1286,6 +1286,32 @@ class OpportunityForm(forms.ModelForm):
if self.instance and self.instance.pk:
self.fields["probability"].initial = self.instance.probability
class OpportunityStageForm(forms.ModelForm):
"""
Represents a form for creating or editing Opportunity instances.
This class is a Django ModelForm designed to simplify the process of
validating and persisting data for Opportunity model instances. It
maps fields from the Opportunity model to form fields, making it
convenient to handle user input for operations such as creating and
updating opportunities.
:ivar Meta.model: The model associated with the form.
:type Meta.model: type
:ivar Meta.fields: List of fields from the model included in the form.
:type Meta.fields: list
"""
class Meta:
model = Opportunity
fields = [
"stage",
]
class InvoiceModelCreateForm(InvoiceModelCreateFormBase):
"""

View File

@ -228,6 +228,11 @@ urlpatterns = [
views.OpportunityUpdateView.as_view(),
name="update_opportunity",
),
path(
"<slug:dealer_slug>/crm/opportunities/<slug:slug>/stage/edit",
views.OpportunityStageUpdateView.as_view(),
name="update_opportunity_stage",
),
path(
"<slug:dealer_slug>/crm/opportunities/",
views.OpportunityListView.as_view(),

View File

@ -6832,7 +6832,7 @@ class OpportunityCreateView(
template_name = "crm/opportunities/opportunity_form.html"
success_message = _("Opportunity created successfully.")
permission_required = ["inventory.add_opportunity"]
def get_initial(self):
initial = super().get_initial()
dealer = get_object_or_404(models.Dealer, slug=self.kwargs.get("dealer_slug"))
@ -6908,6 +6908,7 @@ class OpportunityUpdateView(
template_name = "crm/opportunities/opportunity_form.html"
success_message = _("Opportunity updated successfully.")
permission_required = ["inventory.change_opportunity"]
def get_form(self, form_class=None):
form = super().get_form(form_class)
@ -6930,6 +6931,46 @@ class OpportunityUpdateView(
},
)
class OpportunityStageUpdateView(
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
"""
Handles the update functionality for Opportunity objects.
This class-based view is responsible for handling the update of existing
Opportunity instances. It uses a Django form that is specified by the
`form_class` attribute and renders a template to display and process the
update form. Access to this view is restricted to authenticated users, as
it inherits from `LoginRequiredMixin`.
It defines the model to be updated and the form template to be used. Upon
successful update, it redirects the user to the detail page of the updated
opportunity instance.
:ivar model: The model associated with this view. Represents the Opportunity model.
:type model: django.db.models.Model
:ivar form_class: The form class used to manage the Opportunity update process.
:type form_class: django.forms.ModelForm
:ivar template_name: The path to the template used to render the opportunity
update form.
:type template_name: str
"""
model = models.Opportunity
form_class = forms.OpportunityStageForm
success_message = _("Opportunity Stage updated successfully.")
permission_required = ["inventory.change_opportunity"]
def get_success_url(self):
return reverse_lazy(
"opportunity_detail",
kwargs={
"dealer_slug": self.kwargs.get("dealer_slug"),
"slug": self.object.slug,
},
)
class OpportunityDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
"""
@ -6952,7 +6993,7 @@ class OpportunityDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailV
template_name = "crm/opportunities/opportunity_detail.html"
context_object_name = "opportunity"
permission_required = ["inventory.view_opportunity"]
def get_context_data(self, **kwargs):
dealer = get_object_or_404(models.Dealer, slug=self.kwargs.get("dealer_slug"))
context = super().get_context_data(**kwargs)
@ -7024,6 +7065,7 @@ class OpportunityDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailV
"schedules": qs.filter(scheduled_at__gt=timezone.now())[:5],
}
context["schedule_form"] = forms.ScheduleForm()
context["stage_form"] = forms.OpportunityStageForm()
return context
@ -10272,8 +10314,11 @@ class PurchaseOrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListVie
def get_context_data(self, **kwargs):
dealer = get_user_type(self.request)
vendors=models.Vendor.objects.filter(dealer=dealer)
context = super().get_context_data(**kwargs)
context["entity_slug"] = dealer.entity.slug
context["vendors"] = vendors
return context

View File

@ -226,7 +226,7 @@
{% endif %}
<!-- Mark as Approved -->
{% endif %}
{% if bill.can_approve and not request.is_manager or not request.is_dealer %}
{% if bill.can_approve and request.is_accountant %}
<button class="btn btn-phoenix-warning" disabled>
<i class="fas fa-hourglass-start me-2"></i><span class="text-warning">{% trans 'Waiting for Manager Approval' %}</span>
</button>

View File

@ -1,5 +1,6 @@
{% extends 'base.html' %}
{% load i18n static humanize %}
{% load crispy_forms_tags %}
{% block title %}
{{ _("Opportunity Detail") }}
{% endblock title %}
@ -39,8 +40,9 @@
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>
<a class="dropdown-item" type="button"
data-bs-toggle="modal"
data-bs-target="#updateStageModal">Update Stage</a>
</li>
{% endif %}
{% if perms.inventory.delete_opportunity %}
@ -1095,6 +1097,36 @@
</div>
</div>
</div>
<div class="modal fade" id="updateStageModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="post"
action="{% url 'update_opportunity_stage' request.dealer.slug opportunity.slug %}"
hx-post="{% url 'update_opportunity_stage' request.dealer.slug opportunity.slug %}"
hx-swap="none"
hx-on::after-request="location.reload()">
{% csrf_token %}
<div class="modal-header">
<h5 class="modal-title" id="updateStageModalLabel">{{ _("Update Opportunity Stage") }}</h5>
<button class="btn btn-close p-1"
type="button"
data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body">
{{ stage_form|crispy }}
</div>
<div class="modal-footer">
<button class="btn btn-phoenix-primary" type="submit">{{ _("Save") }}</button>
<button class="btn btn-phoenix-secondary"
type="button"
data-bs-dismiss="modal">{{ _("Cancel") }}</button>
</div>
</form>
</div>
</div>
</div>
{% include 'modal/delete_modal.html' %}
<!-- email Modal -->

View File

@ -352,7 +352,7 @@
<a class="nav-link" href="#">
{% endif %}
<div class="d-flex align-items-center">
<i class="fas fa-shopping-cart"></i><span class="nav-link-text">{% trans 'Car purchase Report'|capfirst %}</span>
<i class="fas fa-chart-pie"></i><span class="nav-link-text">{% trans 'Car purchase Report'|capfirst %}</span>
</div>
</a>
@ -363,9 +363,9 @@
<a class="nav-link" href="#">
{% endif %}
<div class="d-flex align-items-center">
<i class="fas fa-car"></i><span class="nav-link-text">{% trans 'Car Sale Report'|capfirst %}</span>
<i class="fas fa-chart-pie"></i><span class="nav-link-text">{% trans 'Car Sale Report'|capfirst %}</span>
</div>
</a>
</a>
</li>
</ul>

View File

@ -107,7 +107,7 @@
<div class="card summary-card">
<div class="card-body">
<h5 class="card-title">{% trans 'Total VAT Amount' %}<i class="fas fa-percent ms-2"></i></h5>
<p class="card-text">{{ total_vat|floatformat:2 }}</p>
<p class="card-text">{{ 10000|floatformat:2 }}</p>
</div>
</div>
</div>

View File

@ -1,7 +1,7 @@
<!-- po_list.html -->
{% extends "base.html" %}
{% load i18n static %}
{% block title %}Purchase Orders - {{ block.super }}{% endblock %}
{% block title %}Purchase Orders{% endblock %}
{% block content %}
@ -123,7 +123,15 @@
{% include 'modal/delete_modal.html' %}
{% else %}
{% url "purchase_order_create" request.dealer.slug request.dealer.entity.slug as create_purchase_url %}
{% include "empty-illustration-page.html" with value="purchase order" url=create_purchase_url %}
{% if vendors %}
{% url "purchase_order_create" request.dealer.slug request.dealer.entity.slug as create_purchase_url %}
{% include "empty-illustration-page.html" with value="purchase order" url=create_purchase_url %}
{% else %}
{% url "vendor_create" request.dealer.slug as vendor_create_url %}
{% include "message-illustration.html" with value1=_("You don't have a Vendor, Please add a Vendor before creating a Purchase Order.") value2=_("Create New Vendor") url=vendor_create_url %}
{% endif %}
{% endif %}
{% endblock %}

View File

@ -79,12 +79,12 @@
</table>
</div>
<div class="card-footer d-flex ">
<a class="btn btn-sm btn-phoenix-primary me-1"
<a class="btn btn-sm btn-phoenix-primary me-3"
href="{% url 'user_update' request.dealer.slug user_.slug %}">
{{ _("Edit") }}
<i class="fa-solid fa-pen-to-square"></i>
</a>
<button class="btn btn-phoenix-danger btn-sm delete-btn me-1"
<button class="btn btn-phoenix-danger btn-sm delete-btn me-3"
data-url="{% url 'user_delete' request.dealer.slug user_.slug %}"
data-message='{{ _("Are you sure you want to delete this user?") }}'
data-bs-toggle="modal"
@ -92,9 +92,9 @@
{{ _("Delete") }}
<i class="fas fa-trash"></i>
</button>
<a class="btn btn-sm btn-phoenix-secondary me-1"
<a class="btn btn-sm btn-phoenix-secondary me-3"
href="{% url 'user_list' request.dealer.slug %}">
{{ _("Back to List") }}
{{ _("Back to Staffs List") }}
<i class="fa-regular fa-circle-left"></i>
</a>
<a class="btn btn-sm btn-phoenix-secondary"