|
1 | 1 | import calendar
|
| 2 | +import json |
2 | 3 | import logging
|
3 | 4 | from calendar import monthrange
|
4 | 5 | from collections import defaultdict
|
|
11 | 12 | from django.db.models.functions import Coalesce
|
12 | 13 | from django.http import JsonResponse
|
13 | 14 | from django.urls import reverse
|
| 15 | +from django.utils.safestring import mark_safe |
14 | 16 |
|
15 | 17 | from register.models import Customer, Payment, Tenant
|
16 | 18 | from register.models import Expense
|
|
21 | 23 | IncomeSerializer, PaidCustomerSerializer, CustomerProfileSerializer, TenantSerializer
|
22 | 24 | from register.utils import get_tenant_perf, is_last_day_of_month, get_milk_current_price, \
|
23 | 25 | is_transaction_revertible, customer_register_last_updated, get_active_month, \
|
24 |
| - get_customer_balance_amount, get_bill_summary |
| 26 | + get_customer_balance_amount, get_bill_summary, get_customer_due_amount_by_month |
25 | 27 |
|
26 | 28 | logger = logging.getLogger()
|
27 | 29 |
|
@@ -298,3 +300,155 @@ def get_profile_api(request, customer_id):
|
298 | 300 | 'month_year': date.today().strftime("%B, %Y"),
|
299 | 301 | 'print_bill_url': reverse('print_bill', args=[customer.id]),
|
300 | 302 | })
|
| 303 | + |
| 304 | + @login_required() |
| 305 | + def get_report_data_api(request, poll_id): |
| 306 | + """ |
| 307 | + This function returns JSON data for the Report page. |
| 308 | + """ |
| 309 | + chart_data = [] |
| 310 | + d1 = date.today() |
| 311 | + percent = 0 |
| 312 | + milk_delivered = ['morning-yes', 'evening-yes'] |
| 313 | + twelve_month_ago = d1.replace(day=1).replace(year=d1.year - 1) |
| 314 | + # Fetch all Expenses and Incomes |
| 315 | + tenant = Q(tenant_id=request.user.id) |
| 316 | + expense_data = Expense.objects.filter(tenant).values('log_date__month', |
| 317 | + 'log_date__year').annotate( |
| 318 | + expense=Sum('cost')).values('log_date__month', 'log_date__year', 'expense') |
| 319 | + |
| 320 | + income_data = Income.objects.filter(tenant).values('log_date__month', |
| 321 | + 'log_date__year').annotate( |
| 322 | + income=Sum('amount')).values('log_date__month', 'log_date__year', 'income') |
| 323 | + |
| 324 | + # Fetch all Registers |
| 325 | + register_query = Q(tenant, schedule__in=milk_delivered, log_date__gte=twelve_month_ago) |
| 326 | + all_register_entry = Register.objects.filter(register_query) |
| 327 | + |
| 328 | + for i in range(-12, 1): |
| 329 | + percent += 3.75 |
| 330 | + graph_month = d1 + relativedelta(months=i) |
| 331 | + month_str = graph_month.strftime("%B-%Y") |
| 332 | + month_abbr = graph_month.strftime("%b-%y") |
| 333 | + request.session[poll_id] = f'Income and Expense ({graph_month.strftime("%B-%Y")})' |
| 334 | + request.session[f'{poll_id}_percent'] = percent |
| 335 | + request.session.save() |
| 336 | + |
| 337 | + # Retrieve Expense for the current month |
| 338 | + month_expense = next((item['expense'] for item in expense_data if |
| 339 | + item['log_date__month'] == graph_month.month and item[ |
| 340 | + 'log_date__year'] == graph_month.year), 0) |
| 341 | + |
| 342 | + # Retrieve Income for the current month |
| 343 | + month_extra_income = next((item['income'] for item in income_data if |
| 344 | + item['log_date__month'] == graph_month.month and item[ |
| 345 | + 'log_date__year'] == graph_month.year), 0) |
| 346 | + |
| 347 | + month_register_sale = sum( |
| 348 | + [entry.quantity * entry.current_price for entry in all_register_entry if |
| 349 | + entry.log_date.month == graph_month.month and entry.log_date.year == graph_month.year]) / 1000 |
| 350 | + |
| 351 | + month_register_sale += month_extra_income |
| 352 | + |
| 353 | + month_paid = sum( |
| 354 | + [entry.quantity * entry.current_price for entry in all_register_entry if |
| 355 | + entry.log_date.month == graph_month.month and entry.log_date.year == graph_month.year and entry.paid]) / 1000 |
| 356 | + |
| 357 | + month_paid += month_extra_income |
| 358 | + month_due = month_register_sale - month_paid |
| 359 | + |
| 360 | + profit = float( |
| 361 | + max(month_paid - month_expense, 0)) if month_paid > month_expense else False |
| 362 | + loss = float( |
| 363 | + max(month_expense - month_paid, 0)) if month_paid <= month_expense else False |
| 364 | + |
| 365 | + current_month = { |
| 366 | + "monthName": month_str, |
| 367 | + "month": month_abbr, |
| 368 | + "income": float(month_register_sale), |
| 369 | + "paid": float(month_paid), |
| 370 | + "due": float(month_due), |
| 371 | + "expense": float(month_expense), |
| 372 | + "profit": profit, |
| 373 | + "loss": loss, |
| 374 | + } |
| 375 | + chart_data.append(current_month) |
| 376 | + |
| 377 | + # Get milk production over past 365 days |
| 378 | + chart_data_milk = [] |
| 379 | + all_milk_production = defaultdict( |
| 380 | + int) # Using defaultdict to automatically initialize values to 0 |
| 381 | + for entry in all_register_entry: |
| 382 | + all_milk_production[(entry.schedule, entry.log_date.date())] += entry.quantity |
| 383 | + |
| 384 | + for i in range(-365, 1): |
| 385 | + percent += 0.123 |
| 386 | + d1 = date.today() |
| 387 | + graph_day = d1 + relativedelta(days=i) |
| 388 | + request.session[poll_id] = f'Milk Production ({graph_day.strftime("%d-%B-%Y")})' |
| 389 | + request.session[f'{poll_id}_percent'] = percent |
| 390 | + request.session.save() |
| 391 | + milk_production_morning = all_milk_production[('morning-yes', graph_day)] |
| 392 | + milk_production_evening = all_milk_production[('evening-yes', graph_day)] |
| 393 | + |
| 394 | + current_day = { |
| 395 | + "dayName": graph_day.strftime('%d-%B-%Y'), |
| 396 | + 'milkMorning': round(float(milk_production_morning / 1000), 2), |
| 397 | + 'milkEvening': round(float(milk_production_evening / 1000), 2), |
| 398 | + "milkQuantity": round(float(milk_production_morning / 1000), 2) + round( |
| 399 | + float(milk_production_evening / 1000), 2), |
| 400 | + } |
| 401 | + chart_data_milk.append(current_day) |
| 402 | + |
| 403 | + percent += 5 |
| 404 | + request.session[f'{poll_id}_percent'] = percent |
| 405 | + request.session.save() |
| 406 | + # Calculate all time Expenses |
| 407 | + all_time_expense = \ |
| 408 | + Expense.objects.filter(tenant_id=request.user.id).aggregate(Sum('cost'))[ |
| 409 | + 'cost__sum'] or 0 |
| 410 | + |
| 411 | + # Calculate all time Income |
| 412 | + all_time_milk_income = \ |
| 413 | + Payment.objects.filter(tenant_id=request.user.id).aggregate(Sum('amount'))[ |
| 414 | + 'amount__sum'] or 0 |
| 415 | + all_time_extra_income = \ |
| 416 | + Income.objects.filter(tenant_id=request.user.id).aggregate(Sum('amount'))[ |
| 417 | + 'amount__sum'] or 0 |
| 418 | + all_time_income = all_time_milk_income + all_time_extra_income |
| 419 | + |
| 420 | + # Calculate all time profit or loss |
| 421 | + is_profit = True if all_time_expense < all_time_income else False |
| 422 | + all_time_profit_or_loss = abs(all_time_income - all_time_expense) |
| 423 | + percent += 5 |
| 424 | + request.session[f'{poll_id}_percent'] = percent |
| 425 | + request.session.save() |
| 426 | + due_list, due_month = get_customer_due_amount_by_month(request) |
| 427 | + context = { |
| 428 | + 'graph_data': mark_safe(json.dumps(chart_data)), |
| 429 | + 'table_data': chart_data, |
| 430 | + 'chart_data_milk': mark_safe(json.dumps(chart_data_milk)), |
| 431 | + 'all_time_expense': all_time_expense, |
| 432 | + 'all_time_income': all_time_income, |
| 433 | + 'is_profit': is_profit, |
| 434 | + 'all_time_profit_or_loss': all_time_profit_or_loss, |
| 435 | + 'due_customers': mark_safe(json.dumps(due_list)), |
| 436 | + 'due_month': mark_safe(json.dumps(due_month)), |
| 437 | + } |
| 438 | + request.session[poll_id] = 'Done' |
| 439 | + request.session.save() |
| 440 | + return JsonResponse(context) |
| 441 | + |
| 442 | + @login_required() |
| 443 | + def get_report_data_status_api(request, poll_id): |
| 444 | + retry = 30 |
| 445 | + status = None |
| 446 | + while retry: |
| 447 | + status = {'status': request.session.get(poll_id, None), |
| 448 | + 'percent': request.session.get(f'{poll_id}_percent') |
| 449 | + } |
| 450 | + if status: |
| 451 | + return JsonResponse(status) |
| 452 | + else: |
| 453 | + retry -= 1 |
| 454 | + return JsonResponse(status) |
0 commit comments