diff --git a/inventory/forms.py b/inventory/forms.py index 60c57542..6ddb9c00 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -1606,6 +1606,7 @@ class PermissionForm(forms.ModelForm): "django_ledger.invoicemodel", "django_ledger.vendormodel", "django_ledger.journalentrymodel" + # "django_ledger.purchaseordermodel",#TODO add purchase order ] permissions = cache.get( diff --git a/inventory/middleware.py b/inventory/middleware.py index 3990f4e2..033bb5c7 100644 --- a/inventory/middleware.py +++ b/inventory/middleware.py @@ -143,7 +143,6 @@ class DealerSlugMiddleware: return None if dealer_slug.lower() != request.dealer.slug.lower(): - print(dealer_slug) logger.warning(f"Dealer slug mismatch: {dealer_slug} != {request.dealer.slug}") raise Http404("Dealer slug mismatch") diff --git a/inventory/signals.py b/inventory/signals.py index cfa64cc6..01a46e49 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -904,15 +904,28 @@ def create_po_item_upload(sender,instance,created,**kwargs): models.PoItemsUploaded.objects.create(dealer=dealer,po=instance, item=item, status="fulfilled") - ########################################################## ######################Notification######################## ########################################################## +@receiver(post_save, sender=PurchaseOrderModel) +def create_po_fulfilled_notification(sender,instance,created,**kwargs): + if instance.po_status == "fulfilled": + dealer = models.Dealer.objects.get(entity=instance.entity) + accountants = models.CustomGroup.objects.filter(dealer=dealer,name="Inventory").first().group.user_set.exclude(email=dealer.user.email) + for accountant in accountants: + models.Notification.objects.create( + user=accountant, + message=f""" + New Purchase Order {instance.po_number} has been added to dealer {instance.dealer.name}. + View + """, + ) + @receiver(post_save, sender=models.Car) def car_created_notification(sender, instance, created, **kwargs): if created: - accountants = models.CustomGroup.objects.filter(dealer=instance.dealer,name="Accountant").first().group.user_set.exclude(email=instance.dealer.user.email) + accountants = models.CustomGroup.objects.filter(dealer=instance.dealer,name__in=["Manager","Accountant"]).first().group.user_set.all() for accountant in accountants: models.Notification.objects.create( user=accountant, @@ -971,13 +984,18 @@ def lead_created_notification(sender, instance, created, **kwargs): View """, ) -@receiver(post_save, sender=models.Lead) -def lead_created_notification(sender, instance, created, **kwargs): - if created: - models.Notification.objects.create( - user=instance.staff.user, - message=f""" - New Lead has been added. - View - """, - ) +# @receiver(post_save, sender=models.Lead) +# def lead_created_notification(sender, instance, created, **kwargs): +# if created: +# models.Notification.objects.create( +# user=instance.staff.user, +# message=f""" +# New Lead has been added. +# View +# """, +# ) + + +# send notification after car is sold {manager,dealer} +# after po review send notification to {manager} to approve po +# after estimate review send notification to {manager} to approve estimate \ No newline at end of file diff --git a/inventory/urls.py b/inventory/urls.py index e711597d..73b3902e 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -207,11 +207,7 @@ urlpatterns = [ path("notifications/stream/", views.sse_stream, name="sse_stream"), path("notifications/fetch/", views.fetch_notifications, name="fetch_notifications"), - path( - "notifications/", - views.NotificationListView.as_view(), - name="notifications_history", - ), + path("notifications/list/", views.NotificationListView.as_view(), name="notifications_history"), path( "notifications//mark_as_read/", @@ -1086,17 +1082,17 @@ urlpatterns = [ ######### # Purchase Order path( - "/purchase_orders/", + "//purchase_orders/", views.PurchaseOrderListView.as_view(), name="purchase_order_list", ), path( - "/purchase_orders/new/", + "/purchase_orders//new/", views.PurchaseOrderCreateView, name="purchase_order_create", ), path( - "/purchase_orders//detail/", + "/purchase_orders///detail/", views.PurchaseOrderDetailView.as_view(), name="purchase_order_detail", ), diff --git a/inventory/views.py b/inventory/views.py index 31132ebe..cc1c20d7 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -3499,6 +3499,20 @@ class BankAccountCreateView( kwargs["entity_slug"] = entity.slug kwargs["user_model"] = entity.admin return kwargs + + def get_form(self, form_class=None): + dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) + form = super().get_form(form_class) + account_qs = dealer.entity.get_all_accounts().filter( + role__in=[ + roles.ASSET_CA_CASH, + roles.LIABILITY_CL_ACC_PAYABLE, + roles.LIABILITY_LTL_MORTGAGE_PAYABLE + ]) + form.fields['account_model'].queryset = account_qs + + return form + def get_success_url(self): return reverse_lazy("bank_account_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]}) @@ -3567,7 +3581,18 @@ class BankAccountUpdateView( kwargs["entity_slug"] = entity.slug # Get entity_slug from URL kwargs["user_model"] = entity.admin # Get user_model from the request return kwargs + def get_form(self, form_class=None): + dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) + form = super().get_form(form_class) + account_qs = dealer.entity.get_all_accounts().filter( + role__in=[ + roles.ASSET_CA_CASH, + roles.LIABILITY_CL_ACC_PAYABLE, + roles.LIABILITY_LTL_MORTGAGE_PAYABLE + ]) + form.fields['account_model'].queryset = account_qs + return form def get_success_url(self): return reverse_lazy( "bank_account_detail", @@ -6320,14 +6345,19 @@ class ItemExpenseCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateV permission_required = ["django_ledger.add_itemmodel"] def get_form_kwargs(self): - dealer = get_user_type(self.request) + dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) kwargs = super().get_form_kwargs() kwargs["entity_slug"] = dealer.entity.slug kwargs["user_model"] = dealer.entity.admin return kwargs + # def get_form(self,form_class=None): + # dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) + # form = super().get_form(form_class) + # form.fields["uom"].queryset = dealer.entity.get_uom_all() + # return form def form_valid(self, form): - dealer = get_user_type(self.request) + dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) form.instance.entity = dealer.entity return super().form_valid(form) def get_success_url(self): @@ -6880,15 +6910,15 @@ class BillModelCreateView(CreateView): return {"date_draft": get_localdate()} def get_form(self, form_class=None): - dealer = get_user_type(self.request) + dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) return BillModelCreateForm(entity_model=dealer.entity, **self.get_form_kwargs()) def form_valid(self, form): - dealer = get_user_type(self.request) + dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) bill_model: BillModel = form.save(commit=False) ledger_model, bill_model = bill_model.configure( - entity_slug=dealer.entity.slug, - user_model=self.request.user, + entity_slug=dealer.entity, + user_model=dealer.entity.admin, commit_ledger=True, ) @@ -8836,7 +8866,7 @@ def payment_callback(request,dealer_slug): return render(request, "payment_failed.html", {"message": message}) -def sse_stream(request): +def sse_stream(request): def event_stream(): last_id = request.GET.get("last_id", 0) while True: @@ -8895,7 +8925,7 @@ def mark_all_notifications_as_read(request): @login_required def notifications_history(request): models.Notification.objects.filter(user=request.user, is_read=False).update( - read=True + is_read=True ) return JsonResponse({"status": "success"}) @@ -9216,18 +9246,23 @@ def permenant_delete_account(request,dealer_slug, content_type, slug): ##################################################################### -def PurchaseOrderCreateView(request, dealer_slug): +def PurchaseOrderCreateView(request, dealer_slug,entity_slug): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) entity = dealer.entity if request.method == "POST": - po = entity.create_purchase_order(po_title=request.POST.get("po_title")) - po.entity = entity - po.save() + try: + po = entity.create_purchase_order(po_title=request.POST.get("po_title")) + po.entity = entity + po.save() + except ValidationError as e: + messages.error(request, str(e)) + return redirect("purchase_order_create", dealer_slug=dealer.slug, entity_slug=entity.slug) messages.success(request, _("Purchase order created successfully")) - return redirect("purchase_order_detail", dealer_slug=dealer.slug, pk=po.pk) + return redirect("purchase_order_detail", dealer_slug=dealer.slug, entity_slug=entity.slug, pk=po.pk) form = PurchaseOrderModelCreateForm( - entity_slug=entity.slug, user_model=entity.admin + entity_slug=entity.slug, + user_model=entity.admin, ) return render(request, "purchase_orders/po_form.html", {"form": form}) @@ -9346,14 +9381,12 @@ class PurchaseOrderDetailView(PurchaseOrderModelDetailViewBase): context_object_name = "po_model" def get_queryset(self): - dealer = get_user_type(self.request) - self.queryset = PurchaseOrderModel.objects.for_entity( - entity_slug=dealer.entity.slug, user_model=dealer.entity.admin - ).select_related("entity", "ce_model") + dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) + self.queryset = dealer.entity.get_purchase_orders().select_related("entity", "ce_model") return super().get_queryset() def get_context_data(self, **kwargs): - dealer = get_user_type(self.request) + dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) context = super().get_context_data(**kwargs) context["entity_slug"] = dealer.entity.slug return context @@ -9367,77 +9400,76 @@ class PurchaseOrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListVie permission_required = ["inventory.view_carfinance"] def get_queryset(self): - dealer = get_user_type(self.request) - entity = dealer.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) + dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) + return self.model.objects.filter(entity=dealer.entity) - query = self.request.GET.get('q') # This is generic: looks for 'q' from GET + # def get_queryset(self): + # dealer = get_user_type(self.request) + # entity = dealer.entity + # queryset = self.model.objects.filter(entity=entity) - if query: - # Start with an empty Q object for the search filters - search_filters = Q() + # query = self.request.GET.get('q') # This is generic: looks for 'q' from GET - # 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) - ] + # if query: + # # Start with an empty Q object for the search filters + # search_filters = Q() - 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 + # # 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) + # ] - # 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) + # 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 - # 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): dealer = get_user_type(self.request) @@ -9451,7 +9483,7 @@ class PurchaseOrderUpdateView(PurchaseOrderModelUpdateViewBase): context_object_name = "po_model" def get_context_data(self, itemtxs_formset=None, **kwargs): - dealer = self.request.dealer + dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) context = super().get_context_data(**kwargs) context["entity_slug"] = dealer.entity.slug po_model: PurchaseOrderModel = self.object diff --git a/templates/bill/bill_detail.html b/templates/bill/bill_detail.html index a0d3ecbe..56e78a9e 100644 --- a/templates/bill/bill_detail.html +++ b/templates/bill/bill_detail.html @@ -185,7 +185,7 @@ {% if bill_item.po_model_id %} + href="{% url 'purchase_order_detail' request.dealer.slug request.dealer.entity.slug bill_item.po_model_id %}"> {% trans 'View PO' %} {% endif %} diff --git a/templates/bill/tags/bill_item_formset.html b/templates/bill/tags/bill_item_formset.html index 0dbff626..d7978ae6 100644 --- a/templates/bill/tags/bill_item_formset.html +++ b/templates/bill/tags/bill_item_formset.html @@ -72,7 +72,7 @@ {% currency_symbol %}{{ f.instance.po_total_amount | currency_format }} + href="{% url 'purchase_order_detail' dealer_slug entity_slug f.instance.po_model_id %}"> {% trans 'View PO' %} diff --git a/templates/crm/notifications_history.html b/templates/crm/notifications_history.html index 72dbb8df..50423916 100644 --- a/templates/crm/notifications_history.html +++ b/templates/crm/notifications_history.html @@ -14,7 +14,7 @@

{{ _("System")}}:

- {% if not notification.is_read %} + {% if notification.is_read %}

{{ notification.message|safe }} {{ notification.created|timesince }}

{% else %}

{{ notification.message|safe }} {{ notification.created|timesince }}

@@ -48,7 +48,5 @@

No notifications found.

{% endif %} - - {% endblock %} diff --git a/templates/dashboards/manager.html b/templates/dashboards/manager.html index 6a259ec8..9540f0d9 100644 --- a/templates/dashboards/manager.html +++ b/templates/dashboards/manager.html @@ -37,7 +37,7 @@
-

{{ purchase_orders }}

+

{{ purchase_orders }}

{{ _("Purchase Orders")}}

diff --git a/templates/groups/group_permission_form.html b/templates/groups/group_permission_form.html index 3a19188c..55863d8b 100644 --- a/templates/groups/group_permission_form.html +++ b/templates/groups/group_permission_form.html @@ -108,8 +108,6 @@ - {% trans "Cancel"|capfirst %} - diff --git a/templates/header.html b/templates/header.html index 98b2baa6..e8f5db64 100644 --- a/templates/header.html +++ b/templates/header.html @@ -45,13 +45,15 @@ - + {% endif %} @@ -262,7 +264,7 @@ {% endif %}