diff --git a/core/apps/management/migrations/0002_alter_expense_expense_type.py b/core/apps/management/migrations/0002_alter_expense_expense_type.py
new file mode 100644
index 0000000..be14f13
--- /dev/null
+++ b/core/apps/management/migrations/0002_alter_expense_expense_type.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.2.7 on 2026-02-19 07:05
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('management', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='expense',
+ name='expense_type',
+ field=models.CharField(choices=[('salary', 'Maosh'), ('utilities', 'Kommunal toβlovlar'), ('maintenance', 'Texnik xizmat'), ('food', 'Oziq-ovqat'), ('transport', "Yo'lkira"), ('other', 'Boshqa')], default='other', max_length=20),
+ ),
+ ]
diff --git a/core/apps/management/models/expense.py b/core/apps/management/models/expense.py
index e63fee8..3c06a4a 100644
--- a/core/apps/management/models/expense.py
+++ b/core/apps/management/models/expense.py
@@ -8,7 +8,6 @@ class Expense(models.Model):
MAINTENANCE = "maintenance", "Texnik xizmat"
FOOD = "food", "Oziq-ovqat"
TRANSPORT = "transport", "Yo'lkira"
- BUY_TOYS = "buy_toys", "OΚ»yinchoqlar sotib olish"
OTHER = "other", "Boshqa"
amount = models.DecimalField(max_digits=12, decimal_places=2)
diff --git a/core/apps/management/templates/common/lists/report_list.html b/core/apps/management/templates/common/lists/report_list.html
index 29852f3..4668796 100644
--- a/core/apps/management/templates/common/lists/report_list.html
+++ b/core/apps/management/templates/common/lists/report_list.html
@@ -1,164 +1,237 @@
{% extends "base.html" %}
-{% block title %}Hisobotlar{% endblock %}
+{% block title %}Qurilma Statistikasi{% endblock %}
{% block content %}
-
-
{{ title|default:"Hisobotlar" }}
-
-
- {% if reports %}
- {% for report in reports %}
-
-
+
-
-
- Miqdor:
- {{ report.quantity }} dona
-
-
- Sana:
- {{ report.created_at|date:"d.m.Y H:i" }}
-
-
- Yaratgan:
- {{ report.created_by.get_full_name }}
-
-
-
- {% endfor %}
- {% else %}
-
-
Hech qanday hisobot topilmadi
+
+
+
+
+
+
+
+
+
+
π°
+
+
Jami Kirim
+
{{ total_kirim|floatformat:0 }} so'm
+
+
+
+
π
+
+
Jami Xarajat
+
{{ total_xarajat|floatformat:0 }} so'm
+
+
+
+
{% if total_foyda >= 0 %}π{% else %}π{% endif %}
+
+
Jami Foyda
+
{{ total_foyda|floatformat:0 }} so'm
+
+
+
+
+
+
+
+
+
+ | # |
+ Qurilma |
+ Miqdor |
+ Kirim |
+ Xarajat |
+ Foyda |
+ Sana |
+
+
+
+ {% if rows %}
+ {% for row in rows %}
+
+ | {{ forloop.counter }} |
+ {{ row.device_name }} |
+ {{ row.quantity }} |
+ {{ row.kirim|floatformat:0 }} so'm |
+ {{ row.xarajat|floatformat:0 }} so'm |
+
+
+ {% if row.foyda >= 0 %}+{% endif %}{{ row.foyda|floatformat:0 }}
+
+ |
+ {{ row.date }} |
+
+ {% endfor %}
+ {% else %}
+
+ | Hech qanday ma'lumot topilmadi |
+
+ {% endif %}
+
+
+
+
+
+
+
+
{% endblock %}
\ No newline at end of file
diff --git a/core/apps/management/urls.py b/core/apps/management/urls.py
index 7fd76bc..f00e344 100644
--- a/core/apps/management/urls.py
+++ b/core/apps/management/urls.py
@@ -25,7 +25,7 @@ urlpatterns = [
path("list/warehouse/", views.warehouse_list, name="warehouse_list"),
path("list/user/", views.user_list, name="user_list"),
path("list/toy-movement/", views.toy_movement_list, name="toy_movement_list"),
- path("list/reports/", views.report_list, name="report_list"),
+ path("list/reports/", views.device_statistics, name="report_list"),
path("list/toy-movement-statistics/", views.toy_movement_statistics, name="toy_movement_statistics"),
# Edit
path("edit/device/
/", views.edit_device, name="edit_device"),
diff --git a/core/apps/management/views/common/list.py b/core/apps/management/views/common/list.py
index 7761f45..f611dc2 100644
--- a/core/apps/management/views/common/list.py
+++ b/core/apps/management/views/common/list.py
@@ -1,8 +1,11 @@
-from django.shortcuts import render
-from django.contrib.auth.decorators import login_required
-from core.apps.management.models import Device, Income, Expense, Warehouse, ToyMovement, Report
+from core.apps.management.models import Device, Income, Expense, Warehouse, ToyMovement
from core.apps.accounts.models import User
+from django.contrib.auth.decorators import login_required
from core.apps.management.decorators import role_required
+from decimal import Decimal
+from django.db.models import Sum
+from django.shortcuts import render
+
@login_required
@role_required(["manager", "businessman"])
@@ -150,36 +153,114 @@ def device_payment_list(request):
}
)
-from django.contrib.auth.decorators import login_required
-from django.shortcuts import render
-from django.db.models import Q
-
-from core.apps.management.models import Report
-from core.apps.management.decorators import role_required
@login_required
-@role_required(['manager', 'businessman'])
-def report_list(request):
- reports = (
- Report.objects
- .select_related("device", "device__district", "created_by")
- .order_by("-created_at")
- )
+@role_required(["businessman", "manager"])
+def device_statistics(request):
+ """
+ Har bir ToyMovement uchun kirim / xarajat / foyda hisoblaydi.
- if request.user.role == "manager":
- reports = reports.filter(
- device__district__region=request.user.region
- )
+ Formula:
+ narx1 = oylik maosh / 30
+ narx2 = narx1 / aparat soni (maosh ulushi, qurilma/kun)
+ narx3 = kunlik umumiy xarajat / aparat soni (boshqa umumiy / kun)
+ narx4 = arenda / 30 (shu qurilma arenda / kun)
+ xarajat = narx2 + narx3 + narx4
+ kirim = harakat miqdori * o'yinchoq narxi
+ foyda = kirim - xarajat
+ """
- return render(
- request,
- "common/lists/report_list.html",
- {
- "reports": reports,
- "title": "Hisobotlar"
- }
+ # ββ FILTERS ββββββββββββββββββββββββββββββββββββββββββββββββββ
+ start_date = request.GET.get("start_date")
+ end_date = request.GET.get("end_date")
+ device_id = request.GET.get("device")
+
+ qs = ToyMovement.objects.select_related("device", "created_by").filter(
+ device__isnull=False
)
+ if start_date:
+ qs = qs.filter(created_at__date__gte=start_date)
+ if end_date:
+ qs = qs.filter(created_at__date__lte=end_date)
+ if device_id:
+ qs = qs.filter(device_id=device_id)
+
+ qs = qs.order_by("-created_at")
+
+ # ββ SHARED CONSTANTS βββββββββββββββββββββββββββββββββββββββββ
+ total_devices = Device.objects.count() or 1
+
+ # Latest toy price
+ latest_income = Income.objects.order_by("-created_at").first()
+ price_per_toy = latest_income.price_per_toy if latest_income else Decimal("0")
+
+ # Total confirmed salary β narx1 β narx2
+ total_salary = Expense.objects.filter(
+ expense_type=Expense.ExpenseType.SALARY,
+ is_confirmed=True,
+ ).aggregate(s=Sum("amount"))["s"] or Decimal("0")
+
+ narx1 = total_salary / 30
+ narx2 = narx1 / total_devices
+
+ # General (non-device-specific, non-salary) confirmed expenses β narx3
+ total_general = Expense.objects.filter(
+ is_confirmed=True,
+ device__isnull=True,
+ ).exclude(
+ expense_type=Expense.ExpenseType.SALARY
+ ).aggregate(s=Sum("amount"))["s"] or Decimal("0")
+
+ narx3 = (total_general / 30) / total_devices
+
+ # ββ BUILD ROWS ββββββββββββββββββββββββββββββββββββββββββββββββ
+ rows = []
+ total_kirim = Decimal("0")
+ total_xarajat = Decimal("0")
+ total_foyda = Decimal("0")
+
+ for mv in qs:
+ device = mv.device
+
+ # KIRIM
+ kirim = Decimal(mv.quantity) * price_per_toy
+
+ # narx4 β rent for this device per day
+ narx4 = Decimal(device.amount) / 30 if device.amount else Decimal("0")
+
+ # Direct device expenses (maintenance etc.) / 30
+ direct = Expense.objects.filter(
+ device=device, is_confirmed=True
+ ).aggregate(s=Sum("amount"))["s"] or Decimal("0")
+ narx_direct = direct / 30
+
+ xarajat = narx2 + narx3 + narx4 + narx_direct
+ foyda = kirim - xarajat
+
+ total_kirim += kirim
+ total_xarajat += xarajat
+ total_foyda += foyda
+
+ rows.append({
+ "device_name": device.address,
+ "quantity": mv.quantity,
+ "kirim": kirim,
+ "xarajat": xarajat,
+ "foyda": foyda,
+ "date": mv.created_at.strftime("%d.%m.%Y %H:%M"),
+ })
+
+ return render(request, "common/lists/report_list.html", {
+ "rows": rows,
+ "devices": Device.objects.order_by("address"),
+ "total_kirim": total_kirim,
+ "total_xarajat": total_xarajat,
+ "total_foyda": total_foyda,
+ "price_per_toy": price_per_toy,
+ })
+
+
@login_required
@role_required(["employee"])