From d9364ae269c1e68f5d549f3a68b51957b6354b84 Mon Sep 17 00:00:00 2001 From: Nyasha Chizampeni Date: Sun, 11 Aug 2024 04:21:11 +0200 Subject: [PATCH] save --- accounts/admin.py | 2 +- accounts/views.py | 5 +- dashboard/helpers/dashboard_data.py | 157 +++++++++++++++++++++++++--- dashboard/views.py | 23 ++-- requisition/views.py | 4 +- stock/views.py | 2 +- templates/dashboard/index.html | 8 +- templates/reciept/index.html | 4 +- templates/requisition/index.html | 2 + templates/transactions/details.html | 2 +- templates/transactions/index.html | 2 + transactions/views.py | 44 ++++++-- 12 files changed, 206 insertions(+), 49 deletions(-) diff --git a/accounts/admin.py b/accounts/admin.py index 3a96a93..81bbfa1 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -10,7 +10,7 @@ class CompanyAdmin(admin.ModelAdmin): @admin.register(User) class UserAdmin(admin.ModelAdmin): - list_display = ('username', 'email', 'gender', 'role', 'status', 'is_logged_in') + list_display = ('username', 'company','email', 'gender', 'role', 'status', 'is_logged_in') list_filter = ('role', 'status', 'is_logged_in') search_fields = ('username', 'email', 'phone_number') diff --git a/accounts/views.py b/accounts/views.py index 1e4203e..1da53cd 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -29,8 +29,9 @@ def create_staff(request, pk:int): if request.method == 'POST': form = UserForm(request.POST, request.FILES) if form.is_valid(): - staff_user = form.save(commit=False) site = Site.objects.get(pk=pk) # Assuming the site is selected in the form + staff_user = form.save(commit=False) + staff_user.company = request.user.company # Assign the user as the site's manager or operator based on their role if staff_user.role == 'Manager': @@ -142,5 +143,5 @@ class SiteDetailView(TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['site'] = Site.objects.filter(uuid=kwargs.get('uuid')).first() - context["remaining_stock"] = DashboardData(self.request.user, datetime.now()).get_stock_data().get('current_available_Stock_quantity') + context["remaining_stock"] = DashboardData(self.request.user).get_stock_data().get('current_available_Stock_quantity') return context \ No newline at end of file diff --git a/dashboard/helpers/dashboard_data.py b/dashboard/helpers/dashboard_data.py index 2ac1ce5..5da6f92 100644 --- a/dashboard/helpers/dashboard_data.py +++ b/dashboard/helpers/dashboard_data.py @@ -1,28 +1,155 @@ from transactions.models import Transaction from stock.models import Stock from accounts.models import Site, User -from datetime import datetime +from requisition.models import Requisition +from datetime import datetime, timedelta +from django.db.models import Sum, F class DashboardData: - def __init__(self, user:User, date: datetime): - self.date = date + def __init__(self, user: User): self.user = user - + self.site = self.get_site_data() + def get_site_data(self): - return self.user.site + """Returns the site associated with the user based on their role.""" + if self.user.role == "Operator": + return self.user.operation_site + elif self.user.role == "Manager": + return self.user.managed_site + return None def get_sales_data(self): + """Returns a dictionary with sales data.""" + return { + "current_month_total_sales": self.calculate_current_month_sales(), + "total_sales": self.calculate_total_sales(), + "last_month_total_sales": self.calculate_last_month_sales(), + "week_total_sales": self.calculate_week_sales(), + "total_sales_today": self.calculate_today_sales() + } + + def calculate_current_month_sales(self): + """Calculate total sales for the current month.""" + start_of_month = datetime.now().replace(day=1) + # Calculate the total sales for the last month by summing quantity * unit_cost + return Transaction.objects.filter( + site=self.site, + created__gte=start_of_month, + ).aggregate( + total_sales=Sum(F('quantity') * F('unit_cost')) + )['total_sales'] * self.site.stock.first().price or 0.00 + + def calculate_total_sales(self): + """Calculate total sales for today.""" + return Transaction.objects.filter(site=self.site).aggregate( + total_sales=Sum(F('quantity') * F('unit_cost')) + )['total_sales'] * self.site.stock.first().price or 0.00 + + def calculate_last_month_sales(self): + """Calculate total sales for the last month.""" + start_of_last_month = (datetime.now().replace(day=1) - timedelta(days=1)).replace(day=1) + end_of_last_month = datetime.now().replace(day=1) - timedelta(days=1) + + # Calculate the total sales for the last month by summing quantity * unit_cost + return Transaction.objects.filter( + site=self.site, + created__gte=start_of_last_month, + created__lte=end_of_last_month + ).aggregate( + total_sales=Sum(F('quantity') * F('unit_cost')) + )['total_sales'] or 0.00 + + def calculate_week_sales(self): + """Calculate total sales for the current week.""" + start_of_week = datetime.now() - timedelta(days=datetime.now().weekday()) + + # Calculate the total sales for the current week by summing quantity * unit_cost + return Transaction.objects.filter( + site=self.site, + created__gte=start_of_week + ).aggregate( + total_sales=Sum(F('quantity') * F('unit_cost')) + )['total_sales'] * self.site.stock.first().price or 0.00 - return {"current_month_total_sales": 678, "total_sales": 1000, "last_month_total_sales": 679, "week_total_sales": 67.45 , "total_sales_today":14.7} - - def get_stock_data(self): - return {"current_month_total_sales_quantity": 340.3,"week_total_sales_quantity":29.4, "total_sales_quantity": 340.3,"last_month_total_sales_quantity": 340.3, "current_available_Stock_quantity":65, "total_Stock_quantity_sold":1220, "total_requisitions":68 } - + def calculate_today_sales(self): + """Calculate total sales for today.""" + today = datetime.now().date() + return Transaction.objects.filter(site=self.site, created__date=today).aggregate( + total_sales=Sum(F('quantity') * F('unit_cost')) + )['total_sales'] * self.site.stock.first().price or 0.00 + def get_stock_data(self): + """Returns a dictionary with stock data.""" + return { + "current_month_total_sales_quantity": self.calculate_current_month_sales_quantity(), + "week_total_sales_quantity": self.calculate_week_sales_quantity(), + "total_sales_quantity": self.calculate_total_sales_quantity(), + "last_month_total_sales_quantity": self.calculate_last_month_sales_quantity(), + "current_available_stock_quantity": self.calculate_current_available_stock_quantity(), + "total_stock_quantity_sold": self.calculate_total_stock_quantity_sold(), + "total_requisitions": self.calculate_total_requisitions() + } + + def calculate_current_month_sales_quantity(self): + """Calculate total quantity of items sold in the current month.""" + start_of_month = datetime.now().replace(day=1) + return Transaction.objects.filter(site=self.site, created__gte=start_of_month).aggregate(Sum('quantity'))['quantity__sum'] or 0 + + def calculate_week_sales_quantity(self): + """Calculate total quantity of items sold in the current week.""" + start_of_week = datetime.now() - timedelta(days=datetime.now().weekday()) + return Transaction.objects.filter(site=self.site, created__gte=start_of_week).aggregate(Sum('quantity'))['quantity__sum'] or 0 + def calculate_total_sales_quantity(self): + """Calculate total quantity of items sold for the site.""" + return Transaction.objects.filter(site=self.site).aggregate(Sum('quantity'))['quantity__sum'] or 0 + + def calculate_last_month_sales_quantity(self): + """Calculate total quantity of items sold in the last month.""" + start_of_last_month = (datetime.now().replace(day=1) - timedelta(days=1)).replace(day=1) + end_of_last_month = datetime.now().replace(day=1) - timedelta(days=1) + return Transaction.objects.filter(site=self.site, created__gte=start_of_last_month, created__lte=end_of_last_month).aggregate(Sum('quantity'))['quantity__sum'] or 0 + + def calculate_current_available_stock_quantity(self): + """Calculate current available stock quantity.""" + return Stock.objects.filter(site=self.site).aggregate(Sum('quantity'))['quantity__sum'] or 0 + + def calculate_total_stock_quantity_sold(self): + """Calculate total quantity of stock sold.""" + return self.calculate_total_sales_quantity() + + def calculate_total_requisitions(self): + """Calculate total number of requisitions.""" + # Assuming `requisitions` is a related field in `Transaction` or a similar model + return Requisition.objects.filter(site=self.site).count() + def get_sales_plot_data(self): - - return {"monthly_average":345, "weekly_average":89.3, "daily_average":23.4, "plot_data":[]} - + """Returns a dictionary with data for plotting sales.""" + return { + "monthly_average": self.calculate_monthly_average(), + "weekly_average": self.calculate_weekly_average(), + "daily_average": self.calculate_daily_average(), + "plot_data": self.calculate_plot_data() + } + + def calculate_monthly_average(self): + """Calculate the average monthly sales.""" + return self.calculate_total_sales() / 12 # Simplified; adjust as needed + + def calculate_weekly_average(self): + """Calculate the average weekly sales.""" + return self.calculate_total_sales() / 52 # Simplified; adjust as needed + + def calculate_daily_average(self): + """Calculate the average daily sales.""" + return self.calculate_total_sales() / 365 # Simplified; adjust as needed + + def calculate_plot_data(self): + """Generate plot data.""" + # Implement the actual logic to generate plot data based on your requirements + return [] + def get_stock_sales_table_data(self): - - return Transaction.objects.filter().all() \ No newline at end of file + """Returns a queryset of all transactions for the user's site.""" + if self.site: + return Transaction.objects.filter(site=self.site) + return Transaction.objects.none() # Return an empty queryset if no site is assigned diff --git a/dashboard/views.py b/dashboard/views.py index c3515fa..3319521 100644 --- a/dashboard/views.py +++ b/dashboard/views.py @@ -11,23 +11,22 @@ class DashboardView(TemplateView): template_name = 'dashboard/index.html' def dispatch(self, request, *args, **kwargs): - # Check if the user is an Admin - if request.user.role == 'Admin': - # Check if the admin has a company set up - if not request.user.company: + user = request.user + + if user.role == 'Admin': + # Admin must have a company set up + if not user.company: return redirect('create_company') - - # Check if the user is a Manager or Operator - if request.user.role in ['Manager', 'Operator']: - # Check if the manager/operator has a company and site assigned - if not request.user.company or not request.user.site: - # Show a banner/message + + elif user.role in ['Manager', 'Operator']: + # Manager or Operator must have a company and a site assigned + if not user.company or (user.role == 'Manager' and not user.managed_site) or (user.role == 'Operator' and not user.operation_site): messages.info(request, "Please contact your admin to set up your company and site.") return redirect('account_login') - + return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['dashboard_data'] = DashboardData(self.request.user, datetime.now()) + context['dashboard_data'] = DashboardData(self.request.user) return context diff --git a/requisition/views.py b/requisition/views.py index f450bc2..5ad1adc 100644 --- a/requisition/views.py +++ b/requisition/views.py @@ -51,7 +51,7 @@ class RequisitionDetailView(DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["remaining_stock"] = DashboardData(self.request.user, datetime.now()).get_stock_data().get('current_available_Stock_quantity') + context["remaining_stock"] = DashboardData(self.request.user).get_stock_data().get('current_available_Stock_quantity') return context class RequisitionCreateView(View): @@ -89,7 +89,7 @@ class RequisitionUpdateView(UpdateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["remaining_stock"] = DashboardData(datetime.now()).get_stock_data().get('current_available_Stock_quantity') + context["remaining_stock"] = DashboardData(self.request.user).get_stock_data().get('current_available_Stock_quantity') return context class RequisitionDeleteView(View): diff --git a/stock/views.py b/stock/views.py index 7551c12..7b1ca0a 100644 --- a/stock/views.py +++ b/stock/views.py @@ -68,7 +68,7 @@ class RecieptDetailView(DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["remaining_stock"] = DashboardData(datetime.now()).get_stock_data().get('current_available_Stock_quantity') + context["remaining_stock"] = DashboardData(self.request.user).get_stock_data().get('current_available_Stock_quantity') return context class RecieptDeleteView(View): diff --git a/templates/dashboard/index.html b/templates/dashboard/index.html index 37dc7f9..0c25c53 100644 --- a/templates/dashboard/index.html +++ b/templates/dashboard/index.html @@ -107,7 +107,7 @@
Available Stock Stock
-
{{ dashboard_data.get_stock_data.current_available_Stock_quantity }}kg
+
{{ dashboard_data.site.stock.first.quantity}}kg
@@ -187,7 +187,7 @@
Site Statistics
  • Quantity Moved
    -
    {{ dashboard_data.get_stock_data.total_Stock_quantity_sold }}kg
    +
    {{ dashboard_data.get_stock_data.total_stock_quantity_sold }}kg
  • @@ -208,8 +208,8 @@
    Site Statistics
  • -
    Address
    -
    {{ dashboard_data.get_site_data.address }}
    +
    Site
    +
    {{ dashboard_data.get_site_data.name }}
  • diff --git a/templates/reciept/index.html b/templates/reciept/index.html index a2a3110..26d5e8d 100644 --- a/templates/reciept/index.html +++ b/templates/reciept/index.html @@ -22,8 +22,10 @@

    Stock Reception

    diff --git a/templates/requisition/index.html b/templates/requisition/index.html index 5065b16..0ce5dd1 100644 --- a/templates/requisition/index.html +++ b/templates/requisition/index.html @@ -44,8 +44,10 @@

    Requisitions

  • + {% if request.user.role == "Manager" or request.user.role == "Operator" %} Add Requisition + {% endif %}
  • diff --git a/templates/transactions/details.html b/templates/transactions/details.html index e90c11a..a4e8074 100644 --- a/templates/transactions/details.html +++ b/templates/transactions/details.html @@ -25,7 +25,7 @@
    Sale: {{ sale.order_number.upper }}
  • Product
    -
    ${{ sale.product }}
    +
    {{ sale.product }}
  • diff --git a/templates/transactions/index.html b/templates/transactions/index.html index c40eda8..984fd1e 100644 --- a/templates/transactions/index.html +++ b/templates/transactions/index.html @@ -46,9 +46,11 @@

    Stock Sales (Remaining Quantity: {{ remain Out Of Stock {% else %} + {% if request.user.role == "Manager" or request.user.role == "Operator" %} Add Sale {% endif %} + {% endif %} diff --git a/transactions/views.py b/transactions/views.py index 242da9e..ae5e5e7 100644 --- a/transactions/views.py +++ b/transactions/views.py @@ -53,22 +53,46 @@ class TransactionDetailView(DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["remaining_stock"] = DashboardData(self.request.user.site, datetime.now()).get_stock_data().get('current_available_Stock_quantity') + context["remaining_stock"] = DashboardData(self.request.user).get_stock_data().get('current_available_Stock_quantity') return context class TransactionCreateView(View): - def post(self, request): + def post(self, request): form = TransactionForm(request.POST) + if form.is_valid(): - stock = Stock.objects.first() - stock.quantity = stock.quantity - form.cleaned_data['quantity'] - stock.save() - form.save() - messages.success(request, "Sale Saved Successfully") # type: ignore + site = self.get_user_site(request.user) + if not site: + messages.warning(request, "Invalid user role or site association.") + return redirect(reverse('transaction_list')) + + # Decrement the stock quantity + stock = site.stock.first() # Assuming the site has a related stock object + if stock and stock.quantity >= form.cleaned_data['quantity']: + stock.quantity -= form.cleaned_data['quantity'] + stock.save() + + # Save the transaction + transaction = form.save(commit=False) + transaction.site = site + transaction.save() + + messages.success(request, "Sale Saved Successfully") + else: + messages.error(request, "Insufficient stock quantity or stock not found.") else: - messages.warning(request, form.errors) # type: ignore + messages.warning(request, "Failed to save the transaction. Please correct the errors below.") + messages.warning(request, form.errors) + return redirect(reverse('transaction_list')) - + + def get_user_site(self, user): + """Returns the site associated with the user based on their role.""" + if user.role == "Operator": + return user.operation_site + elif user.role == "Manager": + return user.managed_site + return None class TransactionUpdateStatusView(View): def get(self, request, pk): transaction = Transaction.objects.filter(pk=pk).first() @@ -93,7 +117,7 @@ class TransactionUpdateView(UpdateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["remaining_stock"] = DashboardData(datetime.now()).get_stock_data().get('current_available_Stock_quantity') + context["remaining_stock"] = DashboardData(self.request.user).get_stock_data().get('current_available_Stock_quantity') return context class TransactionDeleteView(View):