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