Merge pull request 'Search functionality inside po list' (#91) from frontend into main
Reviewed-on: #91
This commit is contained in:
commit
d772557135
@ -937,7 +937,7 @@ def po_fullfilled_notification(sender, instance, created, **kwargs):
|
|||||||
@receiver(post_save, sender=models.Vendor)
|
@receiver(post_save, sender=models.Vendor)
|
||||||
def vendor_created_notification(sender, instance, created, **kwargs):
|
def vendor_created_notification(sender, instance, created, **kwargs):
|
||||||
if created:
|
if created:
|
||||||
recipients = models.CustomGroup.objects.filter(dealer=instance.dealer,name="Inventory").first().group.user_set.excludeall()
|
recipients = models.CustomGroup.objects.filter(dealer=instance.dealer,name="Inventory").first().group.user_set.all()
|
||||||
|
|
||||||
for recipient in recipients:
|
for recipient in recipients:
|
||||||
models.Notification.objects.create(
|
models.Notification.objects.create(
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import logging
|
|||||||
import tempfile
|
import tempfile
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
# from rich import print
|
# from rich import print
|
||||||
from random import randint
|
from random import randint
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
@ -2496,7 +2495,7 @@ class VendorCreateView(
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
messages.error(self.request, _("Vendor with this email already exists"))
|
messages.error(self.request, _("Vendor with this email already exists"))
|
||||||
return redirect("vendor_create")
|
return redirect("vendor_create",dealer_slug=self.kwargs["dealer_slug"])
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
form.instance.dealer = dealer
|
form.instance.dealer = dealer
|
||||||
form.instance.save()
|
form.instance.save()
|
||||||
@ -8851,8 +8850,7 @@ def payment_callback(request,dealer_slug):
|
|||||||
return render(request, "payment_failed.html", {"message": message})
|
return render(request, "payment_failed.html", {"message": message})
|
||||||
|
|
||||||
|
|
||||||
def sse_stream(request):
|
def sse_stream(request):
|
||||||
print("hi")
|
|
||||||
def event_stream():
|
def event_stream():
|
||||||
last_id = request.GET.get("last_id", 0)
|
last_id = request.GET.get("last_id", 0)
|
||||||
while True:
|
while True:
|
||||||
@ -9386,6 +9384,74 @@ class PurchaseOrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListVie
|
|||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
entity = dealer.entity
|
entity = dealer.entity
|
||||||
return self.model.objects.filter(entity=entity)
|
return self.model.objects.filter(entity=entity)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
dealer = get_user_type(self.request)
|
||||||
|
entity = dealer.entity
|
||||||
|
queryset = self.model.objects.filter(entity=entity)
|
||||||
|
|
||||||
|
query = self.request.GET.get('q') # This is generic: looks for 'q' from GET
|
||||||
|
|
||||||
|
if query:
|
||||||
|
# Start with an empty Q object for the search filters
|
||||||
|
search_filters = Q()
|
||||||
|
|
||||||
|
# 1. Try to parse the query as a date
|
||||||
|
parsed_date = None
|
||||||
|
date_formats = [
|
||||||
|
'%Y-%m-%d', # 2023-10-26
|
||||||
|
'%m/%d/%Y', # 10/26/2023
|
||||||
|
'%d-%m-%Y', # 26-10-2023
|
||||||
|
'%B %d, %Y', # October 26, 2023
|
||||||
|
'%b %d, %Y', # Oct 26, 2023
|
||||||
|
'%Y/%m/%d', # 2023/10/26
|
||||||
|
'%Y-%m', # 2023-10 (for year-month search)
|
||||||
|
'%Y',
|
||||||
|
'%b %d',
|
||||||
|
'%B %d' # 2023 (for year search)
|
||||||
|
]
|
||||||
|
|
||||||
|
for fmt in date_formats:
|
||||||
|
try:
|
||||||
|
# For '%Y-%m' and '%Y', we only care about year/month, not exact day
|
||||||
|
if fmt == '%Y-%m':
|
||||||
|
parsed_date = datetime.strptime(query, fmt)
|
||||||
|
search_filters |= Q(created__year=parsed_date.year, created__month=parsed_date.month)
|
||||||
|
break
|
||||||
|
elif fmt == '%Y':
|
||||||
|
parsed_date = datetime.strptime(query, fmt)
|
||||||
|
search_filters |= Q(created__year=parsed_date.year)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
parsed_date = datetime.strptime(query, fmt).date()
|
||||||
|
search_filters |= Q(created__date=parsed_date) # Matches exact date part of datetime field
|
||||||
|
break # Found a match, no need to try other formats
|
||||||
|
except ValueError:
|
||||||
|
continue # Try next format
|
||||||
|
|
||||||
|
# 2. Add text-based search filters (always apply these)
|
||||||
|
# Combine them with OR operator
|
||||||
|
text_filters = (
|
||||||
|
Q(po_number__icontains=query) |
|
||||||
|
Q(po_title__icontains=query) |
|
||||||
|
Q(po_status__icontains=query) |
|
||||||
|
Q(created__icontains=query)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
# If a date was successfully parsed, combine with text filters
|
||||||
|
if parsed_date:
|
||||||
|
# Use a combined Q object. This means it will search for
|
||||||
|
# (date_match OR po_number_match OR po_title_match)
|
||||||
|
queryset = queryset.filter(search_filters | text_filters).distinct()
|
||||||
|
else:
|
||||||
|
# If no date was parsed, only apply text filters
|
||||||
|
queryset = queryset.filter(text_filters).distinct()
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 964 KiB |
@ -3,7 +3,7 @@
|
|||||||
{% block title %}{{ _('Leads')|capfirst }}{% endblock title %}
|
{% block title %}{{ _('Leads')|capfirst }}{% endblock title %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row g-3 mt-4">
|
<div class="row g-3 mt-4 mb-4">
|
||||||
<h2 class="mb-2">{{ _("Leads")|capfirst }}</h2>
|
<h2 class="mb-2">{{ _("Leads")|capfirst }}</h2>
|
||||||
<!-- Action Tracking Modal -->
|
<!-- Action Tracking Modal -->
|
||||||
{% include "crm/leads/partials/update_action.html" %}
|
{% include "crm/leads/partials/update_action.html" %}
|
||||||
|
|||||||
@ -87,7 +87,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="opportunities-grid" class="row g-4 px-2 px-lg-4 mt-1">
|
<div id="opportunities-grid" class="row g-4 px-2 px-lg-4 mt-1 mb-4">
|
||||||
{% include 'crm/opportunities/partials/opportunity_grid.html' %}
|
{% include 'crm/opportunities/partials/opportunity_grid.html' %}
|
||||||
</div>
|
</div>
|
||||||
{% if page_obj.paginator.num_pages > 1 %}
|
{% if page_obj.paginator.num_pages > 1 %}
|
||||||
|
|||||||
@ -120,10 +120,10 @@
|
|||||||
|
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<a class="btn btn-sm btn-phoenix-primary" href="{% url 'opportunity_detail' request.dealer.slug 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>
|
<i class="fa-solid fa-eye ms-2"></i>{{ _("View") }}
|
||||||
</a>
|
</a>
|
||||||
<a class="btn btn-sm btn-phoenix-success" href="{% url 'update_opportunity' request.dealer.slug 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>
|
<i class="fa-solid fa-pen ms-2"></i> {{ _("Update") }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,7 +11,8 @@
|
|||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="d-md-flex justify-content-between">
|
<div class="d-md-flex justify-content-between">
|
||||||
<div>
|
<div>
|
||||||
<a href="{% url 'group_create' request.dealer.slug %}" class="btn btn-sm btn-phoenix-primary"><span class="fas fa-plus me-2"></span>{% trans "Add Group" %}</a>
|
<a href="{% url 'group_create' request.dealer.slug %}" class="btn btn-sm btn-phoenix-primary me-5"><span class="fas fa-plus me-2"></span>{% trans "Add Group" %}</a>
|
||||||
|
<a href="{% url 'user_list' request.dealer.slug %}" class="btn btn-sm btn-phoenix-secondary"><span class="fas fas fa-arrow-left me-2"></span>{% trans "Back to Staffs" %}</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -111,6 +111,8 @@
|
|||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
<i class="fas fa-save me-2"></i>{% trans "Save Changes" %}
|
<i class="fas fa-save me-2"></i>{% trans "Save Changes" %}
|
||||||
</button>
|
</button>
|
||||||
|
<a href="{% url 'group_detail' request.dealer.slug group.pk %}" class="btn btn-phoenix-secondary "><i class="fa-solid fa-ban"></i> {% trans "Cancel"|capfirst %}</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,14 +11,13 @@
|
|||||||
<li class="list-group-item"><strong>{% trans "Address" %}:</strong> {{ organization.address }}</li>
|
<li class="list-group-item"><strong>{% trans "Address" %}:</strong> {{ organization.address }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<a href="{% url 'organization_update' organization.pk %}" class="btn btn-sm btn-phoenix-warning me-2">{% trans "Edit" %}</a>
|
<a href="{% url 'organization_update' request.dealer.slug organization.slug %}" class="btn btn-sm btn-phoenix-primary me-2"><span class="fas fa-edit me-1"></span>{% trans "Edit" %}</a>
|
||||||
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
||||||
data-url="{% url 'organization_delete' organization.slug %}"
|
data-url="{% url 'organization_delete' request.dealer.slug organization.slug %}"
|
||||||
data-message="Are you sure you want to delete this organization?"
|
data-message="Are you sure you want to delete this organization?"
|
||||||
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||||
{% trans 'Delete' %}<i class="fas fa-trash ms-1"></i>
|
<i class="fas fa-trash me-1"></i> {% trans 'Delete' %}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% include 'modal/delete_modal.html' %}
|
{% include 'modal/delete_modal.html' %}
|
||||||
|
|||||||
@ -11,9 +11,9 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid mt-4">
|
||||||
<!--Heading-->
|
<!--Heading-->
|
||||||
<h3>
|
<h3 class="mb-3">
|
||||||
{% if object %}
|
{% if object %}
|
||||||
{% trans 'Update Organization'%}
|
{% trans 'Update Organization'%}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{% load i18n static %}
|
{% load i18n static %}
|
||||||
<div class="d-flex justify-content-between align-items-center mt-4">
|
<div class="d-flex justify-content-between align-items-center mt-4 mb-3">
|
||||||
<div class="text-body-secondary">
|
<div class="text-body-secondary">
|
||||||
{{ _("Showing") }} {{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }}
|
{{ _("Showing") }} {{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }}
|
||||||
{{ _("of") }} {{ page_obj.paginator.count }} {{ _("results") }}
|
{{ _("of") }} {{ page_obj.paginator.count }} {{ _("results") }}
|
||||||
|
|||||||
@ -65,9 +65,9 @@
|
|||||||
<div class="btn-reveal-trigger position-static">
|
<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>
|
<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">
|
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||||
<a href="{% url 'purchase_order_detail' request.dealer.slug po.pk %}" class="dropdown-item text-success-dark">{% trans 'Detail' %}</a>
|
<a href="{% url 'purchase_order_detail' request.dealer.slug po.pk %}" class="dropdown-item text-success-dark">{% trans 'Purchase Order Detail' %}</a>
|
||||||
{% if po.po_status == 'fulfilled' %}
|
{% if po.po_status == 'fulfilled' %}
|
||||||
<a href="{% url 'view_items_inventory' dealer_slug=request.dealer.slug entity_slug=entity_slug po_pk=po.pk %}" class="dropdown-item text-success-dark">{% trans 'View Inventory Items' %}</a>
|
<a href="{% url 'view_items_inventory' dealer_slug=request.dealer.slug entity_slug=entity_slug po_pk=po.pk %}" class="dropdown-item text-success-dark">{% trans 'Add Inventory Items' %}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="dropdown-item text-warning-dark" disabled><span class="fas fa-exclamation-triangle me-1"></span> Fulfill the PO Before Viewing Inventory</button>
|
<button class="dropdown-item text-warning-dark" disabled><span class="fas fa-exclamation-triangle me-1"></span> Fulfill the PO Before Viewing Inventory</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@ -72,7 +72,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center mb-4">
|
||||||
<div class="col-lg-10">
|
<div class="col-lg-10">
|
||||||
<div class="card shadow">
|
<div class="card shadow">
|
||||||
<div class="card-header bg-primary text-white">
|
<div class="card-header bg-primary text-white">
|
||||||
|
|||||||
@ -18,7 +18,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<p><strong>{{ _("Phone Number") }}:</strong> {{ user_.phone_number }}</p>
|
<p><strong>{{ _("Phone Number") }}:</strong> {{ user_.phone_number }}</p>
|
||||||
<p><strong>{{ _("Role") }}:</strong> {{ user_.staff_type }}</p>
|
<div>
|
||||||
|
<span><strong>{{ _("Roles") }}:</strong></span>
|
||||||
|
{% for group in user_.groups %}
|
||||||
|
<span><strong>{{ group.name }}</strong></span>
|
||||||
|
{% if not forloop.last %}
|
||||||
|
<span>&</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="">
|
<section class="">
|
||||||
|
|
||||||
<div class="row mt-4">
|
<div class="row mt-4">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="d-md-flex justify-content-between">
|
<div class="d-md-flex justify-content-between">
|
||||||
@ -47,6 +46,7 @@
|
|||||||
<td class="align-middle white-space-nowrap align-items-center">{{ user.email }}</td>
|
<td class="align-middle white-space-nowrap align-items-center">{{ user.email }}</td>
|
||||||
<td class="align-middle white-space-nowrap align-items-center justify-content-center">{{ user.phone_number }}</td>
|
<td class="align-middle white-space-nowrap align-items-center justify-content-center">{{ user.phone_number }}</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
{% for group in user.groups %}
|
{% for group in user.groups %}
|
||||||
<span class="badge badge-sm bg-primary text-center"><i class="fa-solid fa-scroll"></i> {% trans group.name|title %}</span>
|
<span class="badge badge-sm bg-primary text-center"><i class="fa-solid fa-scroll"></i> {% trans group.name|title %}</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user