Initial commit
This commit is contained in:
@@ -1,27 +1,42 @@
|
||||
from django.db.models import F, Sum
|
||||
|
||||
from core.apps.management.forms import DeviceForm, IncomeForm, WarehouseForm, UserCreateForm, ExpenseFormEmployee, \
|
||||
ExpenseFormManager, ExpenseFormBusinessman
|
||||
ExpenseFormManager, ExpenseFormBusinessman, ReportForm
|
||||
from django.db import transaction
|
||||
from django.shortcuts import render, redirect
|
||||
from core.apps.management.forms import ToyMovementForm, ToyMovementFormEmployee
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from core.apps.management.decorators import role_required
|
||||
from core.apps.management.forms import UserCreateFormManagerToEmployee, UserCreateFormBusinessman
|
||||
from core.apps.management.models import ToyMovement
|
||||
from core.apps.management.forms.RentForm import RentForm
|
||||
from core.apps.management.models import ToyMovement, Warehouse, Report
|
||||
|
||||
|
||||
@login_required
|
||||
@role_required(["manager", "businessman"])
|
||||
def create_device(request):
|
||||
form = DeviceForm(request.POST or None, user=request.user)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return redirect("dashboard")
|
||||
return render(request, "common/create/device_create.html", {
|
||||
"form": form,
|
||||
"title": "Aparat Yaratish"
|
||||
})
|
||||
user = request.user
|
||||
|
||||
if request.method == "POST":
|
||||
form = DeviceForm(request.POST, user=user)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return redirect("dashboard")
|
||||
else:
|
||||
form = DeviceForm(user=user)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"common/create/device_create.html",
|
||||
{
|
||||
"form": form,
|
||||
"title": "Aparat Yaratish"
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@role_required(["employee"])
|
||||
def create_income(request):
|
||||
if request.method == "POST":
|
||||
form = IncomeForm(request.POST, user=request.user)
|
||||
@@ -35,6 +50,24 @@ def create_income(request):
|
||||
form = IncomeForm(user=request.user)
|
||||
return render(request, "common/create/income_create.html", {"form": form})
|
||||
|
||||
@login_required
|
||||
@role_required(['manager', 'businessman'])
|
||||
def create_income_manager_and_businessman(request):
|
||||
if request.method == "POST":
|
||||
form = IncomeForm(request.POST, user=request.user)
|
||||
if form.is_valid():
|
||||
with transaction.atomic():
|
||||
income = form.save(commit=False)
|
||||
income.created_by = request.user
|
||||
income.save()
|
||||
|
||||
warehouse = income.warehouse
|
||||
warehouse.toys_count += income.amount
|
||||
warehouse.save()
|
||||
return redirect("common/create/income_create.html")
|
||||
else:
|
||||
form = IncomeForm(user=request.user)
|
||||
return render(request=request, template_name="common/create/income_create.html", context={"form": form})
|
||||
|
||||
@login_required
|
||||
def create_expense(request):
|
||||
@@ -111,110 +144,239 @@ def create_user(request):
|
||||
"title": "Foydalanuvchi yaratish",
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def create_toy_movement(request):
|
||||
user = request.user
|
||||
|
||||
# Choose form based on role
|
||||
form_class = ToyMovementFormEmployee if user.role == "employee" else ToyMovementForm
|
||||
if user.role == "employee":
|
||||
form_class = ToyMovementFormEmployee
|
||||
else:
|
||||
form_class = ToyMovementForm
|
||||
|
||||
if request.method == "POST":
|
||||
form = form_class(request.POST, user=user)
|
||||
|
||||
if form.is_valid():
|
||||
with transaction.atomic():
|
||||
movement = form.save(commit=False)
|
||||
|
||||
# Stock validation
|
||||
from_wh = movement.from_warehouse
|
||||
if from_wh.toys_count < movement.quantity:
|
||||
form.add_error("quantity", "Not enough toys in warehouse.")
|
||||
return render(
|
||||
request,
|
||||
"common/create/toy_movement_create.html",
|
||||
{"form": form, "user_role": user.role}
|
||||
quantity = form.cleaned_data["quantity"]
|
||||
|
||||
if user.role == "employee":
|
||||
# Auto determine warehouse by region
|
||||
from_wh = Warehouse.objects.select_for_update().filter(
|
||||
region=user.region
|
||||
).first()
|
||||
|
||||
if not from_wh:
|
||||
form.add_error(None, "No warehouse assigned to your region.")
|
||||
return render(request,
|
||||
"common/create/toy_movement_create.html",
|
||||
{"form": form})
|
||||
|
||||
if from_wh.toys_count < quantity:
|
||||
form.add_error("quantity", "Not enough toys in warehouse.")
|
||||
return render(request,
|
||||
"common/create/toy_movement_create.html",
|
||||
{"form": form})
|
||||
|
||||
# Deduct stock
|
||||
Warehouse.objects.filter(pk=from_wh.pk).update(
|
||||
toys_count=F("toys_count") - quantity
|
||||
)
|
||||
|
||||
# Deduct from source warehouse
|
||||
from_wh.toys_count -= movement.quantity
|
||||
from_wh.save()
|
||||
movement = form.save(commit=False)
|
||||
movement.movement_type = "from_warehouse"
|
||||
movement.from_warehouse = from_wh
|
||||
movement.to_warehouse = None
|
||||
movement.created_by = user
|
||||
movement.save()
|
||||
|
||||
# Add to destination warehouse if moving between warehouses
|
||||
if movement.movement_type == "between_warehouses" and movement.to_warehouse:
|
||||
to_wh = movement.to_warehouse
|
||||
to_wh.toys_count += movement.quantity
|
||||
to_wh.save()
|
||||
else:
|
||||
# Manager / Businessman normal logic
|
||||
from_wh = form.cleaned_data["from_warehouse"]
|
||||
movement_type = form.cleaned_data["movement_type"]
|
||||
to_wh = form.cleaned_data.get("to_warehouse")
|
||||
|
||||
# Set creator
|
||||
movement.created_by = user
|
||||
movement.save()
|
||||
from_wh = Warehouse.objects.select_for_update().get(pk=from_wh.pk)
|
||||
|
||||
if from_wh.toys_count < quantity:
|
||||
form.add_error("quantity", "Not enough toys in warehouse.")
|
||||
return render(request,
|
||||
"common/create/toy_movement_create.html",
|
||||
{"form": form})
|
||||
|
||||
Warehouse.objects.filter(pk=from_wh.pk).update(
|
||||
toys_count=F("toys_count") - quantity
|
||||
)
|
||||
|
||||
if movement_type == "between_warehouses" and to_wh:
|
||||
to_wh = Warehouse.objects.select_for_update().get(pk=to_wh.pk)
|
||||
Warehouse.objects.filter(pk=to_wh.pk).update(
|
||||
toys_count=F("toys_count") + quantity
|
||||
)
|
||||
|
||||
movement = form.save(commit=False)
|
||||
movement.created_by = user
|
||||
movement.save()
|
||||
|
||||
return redirect("dashboard")
|
||||
|
||||
else:
|
||||
form = form_class(user=user)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"common/create/toy_movement_create.html",
|
||||
{"form": form, "user_role": user.role}
|
||||
{"form": form}
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
@login_required
|
||||
@role_required(["manager", "businessman"])
|
||||
def create_toy_movement_auto(request):
|
||||
user = request.user
|
||||
|
||||
# Only employees can use this auto-creation
|
||||
|
||||
if request.method == "POST":
|
||||
# We force movement_type to "between_warehouses"
|
||||
movement = ToyMovement(
|
||||
movement_type="between_warehouses",
|
||||
from_warehouse=user.warehouse,
|
||||
to_warehouse_id=request.POST.get("to_warehouse"),
|
||||
device=None, # not used for between_warehouses
|
||||
quantity=int(request.POST.get("quantity", 0)),
|
||||
created_by=user
|
||||
)
|
||||
form = ToyMovementForm(request.POST, user=user)
|
||||
|
||||
# Stock validation
|
||||
from_wh = movement.from_warehouse
|
||||
if from_wh.toys_count < movement.quantity:
|
||||
form = ToyMovementForm(user=user) # just render empty form with message
|
||||
return render(
|
||||
request,
|
||||
"common/create/toy_movement_create.html",
|
||||
{
|
||||
"form": form,
|
||||
"user_role": user.role,
|
||||
"error": "Not enough toys in warehouse."
|
||||
}
|
||||
)
|
||||
if form.is_valid():
|
||||
with transaction.atomic():
|
||||
# Get cleaned data
|
||||
from_wh = form.cleaned_data.get("from_warehouse")
|
||||
to_wh = form.cleaned_data.get("to_warehouse")
|
||||
movement_type = form.cleaned_data.get("movement_type")
|
||||
quantity = form.cleaned_data.get("quantity")
|
||||
|
||||
with transaction.atomic():
|
||||
# Update warehouse stock
|
||||
from_wh.toys_count -= movement.quantity
|
||||
from_wh.save()
|
||||
# Validate warehouse exists
|
||||
if not from_wh:
|
||||
form.add_error("from_warehouse", "Source warehouse is required.")
|
||||
return render(request, "common/create/toy_movement_create.html", {"form": form})
|
||||
|
||||
# Save movement
|
||||
movement.save()
|
||||
# Lock and get fresh warehouse data
|
||||
from_wh = Warehouse.objects.select_for_update().get(pk=from_wh.pk)
|
||||
|
||||
return redirect("dashboard")
|
||||
# Check stock
|
||||
if from_wh.toys_count < quantity:
|
||||
form.add_error("quantity", "Not enough toys in warehouse.")
|
||||
return render(request, "common/create/toy_movement_create.html", {"form": form})
|
||||
|
||||
# GET request → render the create form
|
||||
form = ToyMovementForm(user=user)
|
||||
# Pre-fill movement_type and from_warehouse
|
||||
form.fields["movement_type"].initial = "between_warehouses"
|
||||
form.fields["from_warehouse"].initial = user.warehouse
|
||||
# Optionally, disable editing these fields
|
||||
form.fields["movement_type"].widget.attrs["readonly"] = True
|
||||
form.fields["from_warehouse"].widget.attrs["readonly"] = True
|
||||
# Deduct from source warehouse
|
||||
Warehouse.objects.filter(pk=from_wh.pk).update(
|
||||
toys_count=F("toys_count") - quantity
|
||||
)
|
||||
|
||||
# Add to destination warehouse if between_warehouses
|
||||
if movement_type == "between_warehouses" and to_wh:
|
||||
to_wh = Warehouse.objects.select_for_update().get(pk=to_wh.pk)
|
||||
Warehouse.objects.filter(pk=to_wh.pk).update(
|
||||
toys_count=F("toys_count") + quantity
|
||||
)
|
||||
|
||||
# Save movement
|
||||
movement = form.save(commit=False)
|
||||
movement.created_by = user
|
||||
movement.save()
|
||||
|
||||
return redirect("toy_movement_list")
|
||||
else:
|
||||
# Form has errors, display them
|
||||
return render(request, "common/create/toy_movement_create.html", {"form": form})
|
||||
|
||||
else:
|
||||
# GET request → render the create form
|
||||
form = ToyMovementForm(user=user)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"common/create/toy_movement_create.html",
|
||||
{"form": form, "user_role": user.role}
|
||||
{"form": form}
|
||||
)
|
||||
|
||||
@login_required
|
||||
@role_required(["manager", "businessman"])
|
||||
def create_rent(request):
|
||||
|
||||
if request.method == "POST":
|
||||
form = RentForm(request.POST)
|
||||
if form.is_valid():
|
||||
rent = form.save(commit=False)
|
||||
rent.created_by = request.user
|
||||
rent.save()
|
||||
return redirect("dashboard")
|
||||
else:
|
||||
form = RentForm()
|
||||
|
||||
return render(request, "common/create/rent_create.html", {"form":form, "title":"Create Rent"})
|
||||
|
||||
@login_required
|
||||
@role_required(["employee"])
|
||||
def create_report(request):
|
||||
|
||||
if request.method == "POST":
|
||||
form = ReportForm(request.POST, user=request.user)
|
||||
|
||||
if form.is_valid():
|
||||
|
||||
# 🔥 Employee MUST have warehouse
|
||||
if not request.user.warehouse:
|
||||
form.add_error(None, "Sizga ombor biriktirilmagan.")
|
||||
return render(
|
||||
request,
|
||||
"common/create/report_create.html",
|
||||
{"form": form, "title": "Yakuniy Hisobot"}
|
||||
)
|
||||
|
||||
device = form.cleaned_data["device"]
|
||||
entered_quantity = form.cleaned_data["quantity"]
|
||||
|
||||
with transaction.atomic():
|
||||
|
||||
# ✅ 1. Save last entered quantity as ToyMovement
|
||||
ToyMovement.objects.create(
|
||||
movement_type="from_warehouse",
|
||||
from_warehouse=request.user.warehouse,
|
||||
device=device,
|
||||
quantity=entered_quantity,
|
||||
created_by=request.user,
|
||||
)
|
||||
|
||||
# ✅ 2. Find last report for this device
|
||||
last_report = Report.objects.filter(
|
||||
device=device
|
||||
).order_by("-created_at").first()
|
||||
|
||||
if last_report:
|
||||
last_time = last_report.created_at
|
||||
|
||||
total_since_last = ToyMovement.objects.filter(
|
||||
device=device,
|
||||
created_at__gt=last_time
|
||||
).aggregate(
|
||||
total=Sum("quantity")
|
||||
)["total"] or 0
|
||||
else:
|
||||
total_since_last = ToyMovement.objects.filter(
|
||||
device=device
|
||||
).aggregate(
|
||||
total=Sum("quantity")
|
||||
)["total"] or 0
|
||||
|
||||
# ✅ 3. Create Report
|
||||
Report.objects.create(
|
||||
device=device,
|
||||
quantity=total_since_last,
|
||||
created_by=request.user
|
||||
)
|
||||
|
||||
return redirect("employee_dashboard")
|
||||
|
||||
else:
|
||||
form = ReportForm(user=request.user)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"common/create/report_create.html",
|
||||
{
|
||||
"form": form,
|
||||
"title": "Yakuniy Hisobot"
|
||||
}
|
||||
)
|
||||
@@ -2,7 +2,8 @@ from django.contrib.auth.decorators import login_required
|
||||
from core.apps.accounts.models import User
|
||||
|
||||
from core.apps.management.forms import DeviceForm, IncomeForm, ExpenseForm, WarehouseForm, UserCreateForm, \
|
||||
ToyMovementEmployeeForm, ToyMovementForm, ExpenseFormEmployee, ExpenseFormManager, ExpenseFormBusinessman
|
||||
ToyMovementEmployeeForm, ToyMovementForm, ExpenseFormEmployee, ExpenseFormManager, ExpenseFormBusinessman, \
|
||||
DevicePaymentForm
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from core.apps.management.models import Device, Income, Expense, Warehouse, ToyMovement
|
||||
from django.db import transaction
|
||||
@@ -30,11 +31,11 @@ def edit_income(request, pk):
|
||||
form = IncomeForm(request.POST, instance=income)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return redirect("income_list")
|
||||
return redirect("common/create/income_create.html")
|
||||
else:
|
||||
form = IncomeForm(instance=income)
|
||||
|
||||
return render(request, "common/edit/income_edit.html", {
|
||||
return render(request, "common/create/income_create.html", {
|
||||
"form": form,
|
||||
"title": "Kirimni tahrirlash"
|
||||
})
|
||||
@@ -125,6 +126,22 @@ def edit_user(request, pk):
|
||||
"title": "Foydalanuvchini tahrirlash",
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
@role_required(["employee"])
|
||||
def mark_device_paid(request, pk):
|
||||
device = get_object_or_404(Device, pk=pk)
|
||||
|
||||
# Security check
|
||||
if device.district.region != request.user.region:
|
||||
return redirect("device_payment_list")
|
||||
|
||||
if request.method == "POST":
|
||||
device.is_paid = True
|
||||
device.save()
|
||||
|
||||
return redirect("device_payment_list")
|
||||
|
||||
# @role_required(["businessman"])
|
||||
# @login_required
|
||||
# def edit_toy_movement(request, pk):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from django.shortcuts import render
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from core.apps.management.models import Device, Income, Expense, Warehouse, ToyMovement
|
||||
from core.apps.management.models import Device, Income, Expense, Warehouse, ToyMovement, Report
|
||||
from core.apps.accounts.models import User
|
||||
from core.apps.management.decorators import role_required
|
||||
|
||||
@@ -39,14 +39,34 @@ def device_list(request):
|
||||
|
||||
@login_required
|
||||
def expense_list(request):
|
||||
expenses = Expense.objects.select_related("created_by", "confirmed_by").order_by("-created_at")
|
||||
user = request.user
|
||||
|
||||
expenses = Expense.objects.select_related(
|
||||
"created_by",
|
||||
"confirmed_by"
|
||||
).order_by("-created_at")
|
||||
|
||||
# 🔐 ROLE-BASED FILTERING
|
||||
if user.role == "employee":
|
||||
expenses = expenses.filter(created_by=user)
|
||||
|
||||
elif user.role == "manager":
|
||||
# Optional: show only region expenses
|
||||
expenses = expenses.filter(
|
||||
created_by__region=user.region
|
||||
)
|
||||
|
||||
# businessman sees everything (no filter)
|
||||
|
||||
context = {
|
||||
"expenses": expenses,
|
||||
"role": request.user.role,
|
||||
"role": user.role,
|
||||
}
|
||||
|
||||
return render(request, "common/lists/expense_list.html", context)
|
||||
|
||||
|
||||
|
||||
@login_required
|
||||
def income_list(request):
|
||||
incomes = Income.objects.select_related("device", "created_by", "created_by__region").order_by("-created_at")
|
||||
@@ -87,4 +107,63 @@ def toy_movement_list(request):
|
||||
"toy_movements": toy_movements,
|
||||
"role": request.user.role,
|
||||
}
|
||||
return render(request, "common/lists/toy_movement_list.html", context)
|
||||
return render(request, "common/lists/toy_movement_list.html", context)
|
||||
|
||||
@login_required
|
||||
@role_required(["manager", "businessman"])
|
||||
def income_list(request):
|
||||
|
||||
if request.user.role == "businessman":
|
||||
incomes = Income.objects.all()
|
||||
elif request.user.role == "manager":
|
||||
incomes = Income.objects.filter(warehouse__region=request.user.region)
|
||||
|
||||
return render(request, "common/create/income_create.html", {"incomes": incomes})
|
||||
|
||||
@login_required
|
||||
@role_required(["employee"])
|
||||
def device_payment_list(request):
|
||||
# Employee only sees devices in his region
|
||||
devices = Device.objects.filter(
|
||||
district__region=request.user.region
|
||||
).order_by("due_date")
|
||||
|
||||
return render(
|
||||
request,
|
||||
"common/lists/device_payment_list.html",
|
||||
{
|
||||
"devices": devices,
|
||||
"title": "Arendalar"
|
||||
}
|
||||
)
|
||||
|
||||
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")
|
||||
)
|
||||
|
||||
if request.user.role == "manager":
|
||||
reports = reports.filter(
|
||||
device__district__region=request.user.region
|
||||
)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"common/lists/report_list.html",
|
||||
{
|
||||
"reports": reports,
|
||||
"title": "Hisobotlar"
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user