shafafiyat/processes/views.py
2025-08-21 09:18:51 +03:30

412 lines
19 KiB
Python

from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse
from django.utils import timezone
import json
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.http import JsonResponse
from django.views.decorators.http import require_POST, require_GET
from django.db import transaction
from django.contrib.auth import get_user_model
from .models import Process, ProcessInstance, StepInstance
from wells.models import Well
from accounts.models import Profile
from .forms import ProcessInstanceForm
from accounts.forms import CustomerForm
from wells.forms import WellForm
from wells.models import WaterMeterManufacturer
@login_required
def process_list(request):
"""نمایش لیست فرآیندهای فعال"""
processes = Process.objects.filter(is_active=True)
return render(request, 'processes/process_list.html', {
'processes': processes
})
@login_required
def process_detail(request, process_id):
"""نمایش جزئیات فرآیند"""
process = get_object_or_404(Process, id=process_id, is_active=True)
return render(request, 'processes/process_detail.html', {
'process': process
})
@login_required
def request_list(request):
"""نمایش لیست درخواست‌ها با جدول و مدال ایجاد"""
instances = ProcessInstance.objects.select_related('well', 'representative', 'requester').filter(is_deleted=False).order_by('-created')
processes = Process.objects.filter(is_active=True)
manufacturers = WaterMeterManufacturer.objects.all().order_by('name')
return render(request, 'processes/request_list.html', {
'instances': instances,
'customer_form': CustomerForm(),
'well_form': WellForm(),
'processes': processes,
'manufacturers': manufacturers
})
@require_GET
@login_required
def lookup_well_by_subscription(request):
sub = request.GET.get('water_subscription_number', '').strip()
if not sub:
return JsonResponse({'ok': False, 'error': 'شماره اشتراک الزامی است'}, status=400)
try:
well = Well.objects.select_related('representative', 'water_meter_manufacturer').get(water_subscription_number=sub)
data = {
'id': well.id,
'water_subscription_number': well.water_subscription_number,
'electricity_subscription_number': well.electricity_subscription_number,
'water_meter_serial_number': well.water_meter_serial_number,
'water_meter_old_serial_number': well.water_meter_old_serial_number,
'water_meter_manufacturer': well.water_meter_manufacturer.id if well.water_meter_manufacturer else None,
'utm_x': str(well.utm_x) if well.utm_x is not None else None,
'utm_y': str(well.utm_y) if well.utm_y is not None else None,
'utm_zone': well.utm_zone,
'utm_hemisphere': well.utm_hemisphere,
'well_power': well.well_power,
'reference_letter_number': well.reference_letter_number,
'reference_letter_date': well.reference_letter_date.isoformat() if well.reference_letter_date else None,
'representative_letter_file_url': well.representative_letter_file.url if well.representative_letter_file else '',
'representative_letter_file_name': well.representative_letter_file.name.split('/')[-1] if well.representative_letter_file else '',
'representative_id': well.representative.id if well.representative else None,
'representative_full_name': well.representative.get_full_name() if well.representative else None,
}
return JsonResponse({'ok': True, 'exists': True, 'well': data})
except Well.DoesNotExist:
return JsonResponse({'ok': True, 'exists': False})
@require_GET
@login_required
def lookup_representative_by_national_code(request):
national_code = request.GET.get('national_code', '').strip()
if not national_code:
return JsonResponse({'ok': False, 'error': 'کد ملی الزامی است'}, status=400)
profile = Profile.objects.select_related('user').filter(national_code=national_code).first()
if not profile:
return JsonResponse({'ok': True, 'exists': False})
user = profile.user
return JsonResponse({
'ok': True,
'exists': True,
'user': {
'id': user.id,
'username': user.username,
'first_name': user.first_name,
'last_name': user.last_name,
'full_name': user.get_full_name(),
'profile': {
'national_code': profile.national_code,
'phone_number_1': profile.phone_number_1,
'phone_number_2': profile.phone_number_2,
'card_number': profile.card_number,
'account_number': profile.account_number,
'address': profile.address,
}
}
})
@require_POST
@login_required
@transaction.atomic
def create_request_with_entities(request):
"""ایجاد/به‌روزرسانی چاه و نماینده و سپس ایجاد درخواست"""
User = get_user_model()
process_id = request.POST.get('process')
process = Process.objects.get(id=process_id)
description = request.POST.get('description', '')
# Well fields
water_subscription_number = request.POST.get('water_subscription_number')
well_id = request.POST.get('well_id') # optional if existing
# Representative fields
representative_id = request.POST.get('representative_id')
# Prefer plain CustomerForm keys; fallback to representative_* keys
representative_national_code = request.POST.get('national_code') or request.POST.get('representative_national_code')
representative_first_name = request.POST.get('first_name') or request.POST.get('representative_first_name')
representative_last_name = request.POST.get('last_name') or request.POST.get('representative_last_name')
representative_username = request.POST.get('username') or request.POST.get('representative_username')
representative_phone_number_1 = request.POST.get('phone_number_1') or request.POST.get('representative_phone_number_1')
representative_phone_number_2 = request.POST.get('phone_number_2') or request.POST.get('representative_phone_number_2')
representative_card_number = request.POST.get('card_number') or request.POST.get('representative_card_number')
representative_account_number = request.POST.get('account_number') or request.POST.get('representative_account_number')
representative_address = request.POST.get('address') or request.POST.get('representative_address')
if not process_id:
return JsonResponse({'ok': False, 'errors': {'request': {'process': ['فرآیند الزامی است']}}}, status=400)
if not water_subscription_number:
return JsonResponse({'ok': False, 'errors': {'well': {'water_subscription_number': ['شماره اشتراک آب الزامی است']}}}, status=400)
if not representative_id and not representative_national_code:
return JsonResponse({'ok': False, 'errors': {'customer': {'national_code': ['کد ملی نماینده را وارد کنید یا دکمه بررسی/افزودن نماینده را بزنید']}}}, status=400)
representative_user = None
representative_profile = None
if representative_id:
representative_profile = Profile.objects.select_related('user').filter(user_id=representative_id).first()
if not representative_profile:
return JsonResponse({'ok': False, 'errors': {'customer': {'__all__': ['نماینده انتخاب‌شده یافت نشد']}}}, status=400)
representative_user = representative_profile.user
# Optionally update if fields provided
changed = False
if representative_first_name:
representative_user.first_name = representative_first_name
changed = True
if representative_last_name:
representative_user.last_name = representative_last_name
changed = True
if representative_username:
representative_user.username = representative_username
changed = True
if changed:
representative_user.save()
if representative_national_code:
representative_profile.national_code = representative_national_code
if representative_phone_number_1 is not None:
representative_profile.phone_number_1 = representative_phone_number_1
if representative_phone_number_2 is not None:
representative_profile.phone_number_2 = representative_phone_number_2
if representative_card_number is not None:
representative_profile.card_number = representative_card_number
if representative_account_number is not None:
representative_profile.account_number = representative_account_number
if representative_address is not None:
representative_profile.address = representative_address
representative_profile.save()
else:
# Use CustomerForm to validate/create/update representative profile by national code
profile_instance = None
if representative_national_code:
profile_instance = Profile.objects.filter(national_code=representative_national_code).first()
customer_data = {
'first_name': representative_first_name or '',
'last_name': representative_last_name or '',
'phone_number_1': representative_phone_number_1 or '',
'phone_number_2': representative_phone_number_2 or '',
'national_code': representative_national_code or '',
'address': representative_address or '',
'card_number': representative_card_number or '',
'account_number': representative_account_number or '',
}
customer_form = CustomerForm(customer_data, instance=profile_instance)
customer_form.request = request
if not customer_form.is_valid():
return JsonResponse({'ok': False, 'errors': {'customer': customer_form.errors}}, status=400)
representative_profile = customer_form.save()
representative_user = representative_profile.user
# Resolve/create/update well
# Build WellForm data from POST
well = None
if well_id:
well = Well.objects.filter(id=well_id).first()
if not well:
return JsonResponse({'ok': False, 'error': 'شناسه چاه نامعتبر است'}, status=400)
else:
existing = Well.objects.filter(water_subscription_number=water_subscription_number).first()
if existing:
well = existing
well_data = request.POST.copy()
# Ensure representative set from created/selected user if not provided
if representative_user and not well_data.get('representative'):
well_data['representative'] = str(representative_user.id)
if not well_data.get('water_subscription_number'):
well_data['water_subscription_number'] = water_subscription_number
# Preserve existing values on partial updates
if well:
for field_name in WellForm.Meta.fields:
if field_name in ('representative_letter_file',):
# File field handled via request.FILES; skip if not provided
continue
incoming = well_data.get(field_name, None)
if incoming is None or incoming == '':
current_value = getattr(well, field_name, None)
if current_value is None:
continue
# Convert FK to id
if hasattr(current_value, 'pk'):
well_data[field_name] = str(current_value.pk)
else:
# Convert dates/decimals/others to string
try:
well_data[field_name] = current_value.isoformat() # dates
except AttributeError:
well_data[field_name] = str(current_value)
well_form = WellForm(well_data, request.FILES, instance=well)
if not well_form.is_valid():
return JsonResponse({'ok': False, 'errors': {'well': well_form.errors}}, status=400)
# Save with ability to remove existing file
well = well_form.save(commit=False)
try:
if request.POST.get('remove_file') == 'true' and getattr(well, 'representative_letter_file', None):
well.representative_letter_file.delete(save=False)
well.representative_letter_file = None
except Exception:
pass
well.save()
# Auto fill geo ownership from current user profile if available
current_profile = getattr(request.user, 'profile', None)
if current_profile:
if hasattr(well, 'affairs'):
well.affairs = current_profile.affairs
if hasattr(well, 'county'):
well.county = current_profile.county
if hasattr(well, 'broker'):
well.broker = current_profile.broker
well.save()
# Create request instance
instance = ProcessInstance.objects.create(
process=process,
description=description,
well=well,
representative=representative_user,
requester=request.user,
status='pending',
priority='medium',
)
# ایجاد نمونه‌های مرحله بر اساس مراحل فرآیند و تنظیم مرحله فعلی
for step in process.steps.all().order_by('order'):
StepInstance.objects.create(
process_instance=instance,
step=step
)
first_step = process.steps.all().order_by('order').first()
if first_step:
instance.current_step = first_step
instance.status = 'in_progress'
instance.save()
redirect_url = reverse('processes:instance_steps', args=[instance.id])
return JsonResponse({'ok': True, 'instance_id': instance.id, 'redirect': redirect_url})
@require_POST
@login_required
def delete_request(request, instance_id):
"""حذف درخواست"""
instance = get_object_or_404(ProcessInstance, id=instance_id)
code = instance.code
instance.delete()
return JsonResponse({
'success': True,
'message': f'درخواست {code} با موفقیت حذف شد'
})
@login_required
def start_process(request, process_id):
"""شروع فرآیند جدید"""
process = get_object_or_404(Process, id=process_id, is_active=True)
if request.method == 'POST':
form = ProcessInstanceForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.process = process
instance.requester = request.user
instance.save()
# ایجاد نمونه‌های مرحله
for step in process.steps.all():
StepInstance.objects.create(
process_instance=instance,
step=step
)
# تنظیم مرحله اول به عنوان مرحله فعلی
first_step = process.steps.first()
if first_step:
instance.current_step = first_step
instance.status = 'in_progress'
instance.save()
messages.success(request, f'فرآیند {process.name} با موفقیت شروع شد.')
return redirect('processes:instance_detail', instance_id=instance.id)
else:
form = ProcessInstanceForm()
return render(request, 'processes/start_process.html', {
'process': process,
'form': form
})
@login_required
def instance_detail(request, instance_id):
"""نمایش جزئیات نمونه فرآیند"""
instance = get_object_or_404(ProcessInstance, id=instance_id)
return render(request, 'processes/instance_detail.html', {
'instance': instance
})
@login_required
def step_detail(request, instance_id, step_id):
"""نمایش جزئیات مرحله خاص"""
instance = get_object_or_404(
ProcessInstance.objects.select_related('process', 'well', 'requester', 'representative', 'representative__profile'),
id=instance_id
)
step = get_object_or_404(instance.process.steps, id=step_id)
# بررسی دسترسی به مرحله
if not instance.can_access_step(step):
messages.error(request, 'شما به این مرحله دسترسی ندارید. ابتدا مراحل قبلی را تکمیل کنید.')
return redirect('processes:request_list')
# هدایت به view مناسب بر اساس نوع مرحله
if step.order == 1: # مرحله اول - انتخاب اقلام
return redirect('invoices:quote_step', instance_id=instance.id, step_id=step.id)
elif step.order == 2: # مرحله دوم - صدور پیش‌فاکتور
return redirect('invoices:quote_preview_step', instance_id=instance.id, step_id=step.id)
elif step.order == 3: # مرحله سوم - ثبت فیش‌های واریزی
return redirect('invoices:quote_payment_step', instance_id=instance.id, step_id=step.id)
# برای سایر مراحل، template عمومی نمایش داده می‌شود
step_instance = instance.step_instances.filter(step=step).first()
# Navigation logic
previous_step = instance.process.steps.filter(order__lt=step.order).last()
next_step = instance.process.steps.filter(order__gt=step.order).first()
return render(request, 'processes/step_detail.html', {
'instance': instance,
'step': step,
'step_instance': step_instance,
'previous_step': previous_step,
'next_step': next_step,
})
@login_required
def instance_steps(request, instance_id):
"""هدایت به مرحله فعلی instance"""
instance = get_object_or_404(ProcessInstance, id=instance_id)
if not instance.current_step:
# اگر مرحله فعلی تعریف نشده، به اولین مرحله برو
first_step = instance.process.steps.first()
if first_step:
instance.current_step = first_step
instance.save()
return redirect('processes:step_detail', instance_id=instance.id, step_id=first_step.id)
else:
messages.error(request, 'هیچ مرحله‌ای برای این فرآیند تعریف نشده است.')
return redirect('processes:request_list')
return redirect('processes:step_detail', instance_id=instance.id, step_id=instance.current_step.id)
@login_required
def my_processes(request):
"""نمایش فرآیندهای کاربر"""
my_instances = ProcessInstance.objects.filter(requester=request.user)
assigned_steps = StepInstance.objects.filter(assigned_to=request.user, status='in_progress')
return render(request, 'processes/my_processes.html', {
'my_instances': my_instances,
'assigned_steps': assigned_steps
})