Add confirmation and summary
This commit is contained in:
parent
9b3973805e
commit
35799b7754
25 changed files with 1419 additions and 265 deletions
|
@ -5,7 +5,8 @@ from django.urls import reverse
|
|||
from django.utils import timezone
|
||||
from accounts.models import Profile
|
||||
from common.consts import UserRoles
|
||||
from processes.models import ProcessInstance, StepInstance
|
||||
from processes.models import ProcessInstance, StepInstance, StepRejection, StepApproval
|
||||
from accounts.models import Role
|
||||
from invoices.models import Item, Quote, QuoteItem
|
||||
from .models import InstallationAssignment, InstallationReport, InstallationPhoto, InstallationItemChange
|
||||
from decimal import Decimal, InvalidOperation
|
||||
|
@ -21,7 +22,18 @@ def installation_assign_step(request, instance_id, step_id):
|
|||
installers = Profile.objects.filter(roles__slug=UserRoles.INSTALLER.value).select_related('user').all()
|
||||
assignment, _ = InstallationAssignment.objects.get_or_create(process_instance=instance)
|
||||
|
||||
# Role flags
|
||||
profile = getattr(request.user, 'profile', None)
|
||||
is_manager = False
|
||||
try:
|
||||
is_manager = bool(profile and profile.has_role(UserRoles.MANAGER))
|
||||
except Exception:
|
||||
is_manager = False
|
||||
|
||||
if request.method == 'POST':
|
||||
if not is_manager:
|
||||
messages.error(request, 'شما اجازه تعیین نصاب را ندارید')
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
||||
installer_id = request.POST.get('installer_id')
|
||||
scheduled_date = (request.POST.get('scheduled_date') or '').strip()
|
||||
assignment.installer_id = installer_id or None
|
||||
|
@ -43,6 +55,10 @@ def installation_assign_step(request, instance_id, step_id):
|
|||
return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id)
|
||||
return redirect('processes:request_list')
|
||||
|
||||
# Read-only logic for non-managers
|
||||
read_only = not is_manager
|
||||
show_denied_msg = (not is_manager) and (assignment.installer_id is None)
|
||||
|
||||
return render(request, 'installations/installation_assign_step.html', {
|
||||
'instance': instance,
|
||||
'step': step,
|
||||
|
@ -50,6 +66,9 @@ def installation_assign_step(request, instance_id, step_id):
|
|||
'installers': installers,
|
||||
'previous_step': previous_step,
|
||||
'next_step': next_step,
|
||||
'is_manager': is_manager,
|
||||
'read_only': read_only,
|
||||
'show_denied_msg': show_denied_msg,
|
||||
})
|
||||
|
||||
|
||||
|
@ -61,15 +80,94 @@ def installation_report_step(request, instance_id, step_id):
|
|||
next_step = instance.process.steps.filter(order__gt=step.order).first()
|
||||
assignment = InstallationAssignment.objects.filter(process_instance=instance).first()
|
||||
existing_report = InstallationReport.objects.filter(assignment=assignment).order_by('-created').first()
|
||||
edit_mode = True if request.GET.get('edit') == '1' else False
|
||||
print("edit_mode", edit_mode)
|
||||
# Only installers can enter edit mode
|
||||
user_is_installer = hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.INSTALLER)
|
||||
edit_mode = True if (request.GET.get('edit') == '1' and user_is_installer) else False
|
||||
# current quote items baseline
|
||||
quote = Quote.objects.filter(process_instance=instance).first()
|
||||
quote_items = list(quote.items.select_related('item').all()) if quote else []
|
||||
quote_price_map = {qi.item_id: qi.unit_price for qi in quote_items}
|
||||
items = Item.objects.all().order_by('name')
|
||||
items = Item.objects.filter(is_active=True, is_special=False, is_deleted=False).order_by('name')
|
||||
|
||||
# Ensure a StepInstance exists for this step
|
||||
step_instance, _ = StepInstance.objects.get_or_create(
|
||||
process_instance=instance,
|
||||
step=step,
|
||||
defaults={'status': 'in_progress'}
|
||||
)
|
||||
|
||||
# Build approver requirements/status for UI
|
||||
reqs = list(step.approver_requirements.select_related('role').all())
|
||||
user_roles_qs = getattr(getattr(request.user, 'profile', None), 'roles', None)
|
||||
user_roles = list(user_roles_qs.all()) if user_roles_qs is not None else []
|
||||
user_can_approve = any(r.role in user_roles for r in reqs)
|
||||
approvals_list = list(step_instance.approvals.select_related('role').all())
|
||||
approvals_by_role = {a.role_id: a for a in approvals_list}
|
||||
approver_statuses = [
|
||||
{
|
||||
'role': r.role,
|
||||
'status': (approvals_by_role.get(r.role_id).decision if approvals_by_role.get(r.role_id) else None),
|
||||
'reason': (approvals_by_role.get(r.role_id).reason if approvals_by_role.get(r.role_id) else ''),
|
||||
}
|
||||
for r in reqs
|
||||
]
|
||||
|
||||
# Manager approval/rejection actions
|
||||
if request.method == 'POST' and request.POST.get('action') in ['approve', 'reject']:
|
||||
action = request.POST.get('action')
|
||||
# find a matching approver role based on step requirements
|
||||
req_roles = [req.role for req in step.approver_requirements.select_related('role').all()]
|
||||
user_roles = list(getattr(getattr(request.user, 'profile', None), 'roles', Role.objects.none()).all())
|
||||
matching_role = next((r for r in user_roles if r in req_roles), None)
|
||||
if matching_role is None:
|
||||
messages.error(request, 'شما دسترسی لازم برای این عملیات را ندارید.')
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
||||
|
||||
if not existing_report:
|
||||
messages.error(request, 'گزارش برای تایید/رد وجود ندارد.')
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
||||
|
||||
if action == 'approve':
|
||||
existing_report.approved = True
|
||||
existing_report.save()
|
||||
StepApproval.objects.update_or_create(
|
||||
step_instance=step_instance,
|
||||
role=matching_role,
|
||||
defaults={'approved_by': request.user, 'decision': 'approved', 'reason': ''}
|
||||
)
|
||||
if step_instance.is_fully_approved():
|
||||
step_instance.status = 'completed'
|
||||
step_instance.completed_at = timezone.now()
|
||||
step_instance.save()
|
||||
if next_step:
|
||||
instance.current_step = next_step
|
||||
instance.save()
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id)
|
||||
return redirect('processes:request_list')
|
||||
messages.success(request, 'تایید شما ثبت شد. منتظر تایید سایر نقشها.')
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
||||
|
||||
if action == 'reject':
|
||||
reason = (request.POST.get('reject_reason') or '').strip()
|
||||
if not reason:
|
||||
messages.error(request, 'لطفاً علت رد شدن را وارد کنید.')
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
||||
StepApproval.objects.update_or_create(
|
||||
step_instance=step_instance,
|
||||
role=matching_role,
|
||||
defaults={'approved_by': request.user, 'decision': 'rejected', 'reason': reason}
|
||||
)
|
||||
StepRejection.objects.create(step_instance=step_instance, rejected_by=request.user, reason=reason)
|
||||
existing_report.approved = False
|
||||
existing_report.save()
|
||||
messages.success(request, 'گزارش رد شد و برای اصلاح به نصاب بازگشت.')
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
||||
|
||||
if request.method == 'POST':
|
||||
# Only installers can submit or edit reports (non-approval actions)
|
||||
if request.POST.get('action') not in ['approve', 'reject'] and not user_is_installer:
|
||||
messages.error(request, 'شما مجوز ثبت/ویرایش گزارش نصب را ندارید')
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
||||
description = (request.POST.get('description') or '').strip()
|
||||
visited_date = (request.POST.get('visited_date') or '').strip()
|
||||
if '/' in visited_date:
|
||||
|
@ -134,6 +232,7 @@ def installation_report_step(request, instance_id, step_id):
|
|||
report.is_meter_suspicious = is_suspicious
|
||||
report.utm_x = utm_x
|
||||
report.utm_y = utm_y
|
||||
report.approved = False # back to awaiting approval after edits
|
||||
report.save()
|
||||
# delete selected existing photos
|
||||
for key, val in request.POST.items():
|
||||
|
@ -211,18 +310,17 @@ def installation_report_step(request, instance_id, step_id):
|
|||
total_price=total,
|
||||
)
|
||||
|
||||
# complete step
|
||||
StepInstance.objects.update_or_create(
|
||||
process_instance=instance,
|
||||
step=step,
|
||||
defaults={'status': 'completed', 'completed_at': timezone.now()}
|
||||
)
|
||||
# After installer submits/edits, set step back to in_progress and clear approvals
|
||||
step_instance.status = 'in_progress'
|
||||
step_instance.completed_at = None
|
||||
step_instance.save()
|
||||
try:
|
||||
step_instance.approvals.all().delete()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if next_step:
|
||||
instance.current_step = next_step
|
||||
instance.save()
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id)
|
||||
return redirect('processes:request_list')
|
||||
messages.success(request, 'گزارش ثبت شد و در انتظار تایید است.')
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
||||
|
||||
# Build prefill maps from existing report changes
|
||||
removed_ids = set()
|
||||
|
@ -250,6 +348,9 @@ def installation_report_step(request, instance_id, step_id):
|
|||
'added_map': added_map,
|
||||
'previous_step': previous_step,
|
||||
'next_step': next_step,
|
||||
'step_instance': step_instance,
|
||||
'approver_statuses': approver_statuses,
|
||||
'user_can_approve': user_can_approve,
|
||||
})
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue