fix installation step and utm standarded.
This commit is contained in:
		
							parent
							
								
									204b0aa48e
								
							
						
					
					
						commit
						93db2fe7f5
					
				
					 10 changed files with 191 additions and 108 deletions
				
			
		| 
						 | 
				
			
			@ -19,7 +19,7 @@ def installation_assign_step(request, instance_id, step_id):
 | 
			
		|||
    next_step = instance.process.steps.filter(order__gt=step.order).first()
 | 
			
		||||
 | 
			
		||||
    # Installers list (profiles that have installer role)
 | 
			
		||||
    installers = Profile.objects.filter(roles__slug=UserRoles.INSTALLER.value).select_related('user').all()
 | 
			
		||||
    installers = Profile.objects.filter(roles__slug=UserRoles.INSTALLER.value, county=instance.well.county).select_related('user').all()
 | 
			
		||||
    assignment, _ = InstallationAssignment.objects.get_or_create(process_instance=instance)
 | 
			
		||||
 | 
			
		||||
    # Role flags
 | 
			
		||||
| 
						 | 
				
			
			@ -72,17 +72,56 @@ def installation_assign_step(request, instance_id, step_id):
 | 
			
		|||
    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_item_changes_for_report(report, remove_map, add_map, quote_price_map):
 | 
			
		||||
    """Helper function to create item changes for a report"""
 | 
			
		||||
    # Create remove changes
 | 
			
		||||
    for item_id, qty in remove_map.items():
 | 
			
		||||
        up = quote_price_map.get(item_id)
 | 
			
		||||
        total = (up * qty) if up is not None else None
 | 
			
		||||
        InstallationItemChange.objects.create(
 | 
			
		||||
            report=report,
 | 
			
		||||
            item_id=item_id,
 | 
			
		||||
            change_type='remove',
 | 
			
		||||
            quantity=qty,
 | 
			
		||||
            unit_price=up,
 | 
			
		||||
            total_price=total,
 | 
			
		||||
        )
 | 
			
		||||
    
 | 
			
		||||
    # Create add changes
 | 
			
		||||
    for item_id, data in add_map.items():
 | 
			
		||||
        unit_price = data.get('price')
 | 
			
		||||
        qty = data.get('qty') or 1
 | 
			
		||||
        total = (unit_price * qty) if (unit_price is not None) else None
 | 
			
		||||
        InstallationItemChange.objects.create(
 | 
			
		||||
            report=report,
 | 
			
		||||
            item_id=item_id,
 | 
			
		||||
            change_type='add',
 | 
			
		||||
            quantity=qty,
 | 
			
		||||
            unit_price=unit_price,
 | 
			
		||||
            total_price=total,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def installation_report_step(request, instance_id, step_id):
 | 
			
		||||
    instance = get_object_or_404(ProcessInstance, id=instance_id)
 | 
			
		||||
    step = get_object_or_404(instance.process.steps, id=step_id)
 | 
			
		||||
 | 
			
		||||
    previous_step = instance.process.steps.filter(order__lt=step.order).last()
 | 
			
		||||
    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()
 | 
			
		||||
    # Only installers can enter edit mode
 | 
			
		||||
    user_is_installer = hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.INSTALLER)
 | 
			
		||||
 | 
			
		||||
    # Only the assigned installer can create/edit the report
 | 
			
		||||
    try:
 | 
			
		||||
        has_installer_role = bool(getattr(request.user, 'profile', None) and request.user.profile.has_role(UserRoles.INSTALLER))
 | 
			
		||||
    except Exception:
 | 
			
		||||
        has_installer_role = False
 | 
			
		||||
    is_assigned_installer = bool(assignment and assignment.installer_id == request.user.id)
 | 
			
		||||
    user_is_installer = bool(has_installer_role and is_assigned_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 []
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +139,14 @@ def installation_report_step(request, instance_id, step_id):
 | 
			
		|||
    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)
 | 
			
		||||
    # Align permission check with invoices flow (role id intersection)
 | 
			
		||||
    try:
 | 
			
		||||
        req_role_ids = {r.role_id for r in reqs}
 | 
			
		||||
        user_role_ids = {ur.id for ur in user_roles}
 | 
			
		||||
        can_approve_reject = len(req_role_ids.intersection(user_role_ids)) > 0
 | 
			
		||||
    except Exception:
 | 
			
		||||
        can_approve_reject = False
 | 
			
		||||
    user_can_approve = can_approve_reject
 | 
			
		||||
    approvals_list = list(step_instance.approvals.select_related('role').all())
 | 
			
		||||
    approvals_by_role = {a.role_id: a for a in approvals_list}
 | 
			
		||||
    approver_statuses = [
 | 
			
		||||
| 
						 | 
				
			
			@ -160,6 +206,13 @@ def installation_report_step(request, instance_id, step_id):
 | 
			
		|||
            StepRejection.objects.create(step_instance=step_instance, rejected_by=request.user, reason=reason)
 | 
			
		||||
            existing_report.approved = False
 | 
			
		||||
            existing_report.save()
 | 
			
		||||
            # If current step moved ahead of this step, reset it back for correction (align with invoices)
 | 
			
		||||
            try:
 | 
			
		||||
                if instance.current_step and instance.current_step.order > step.order:
 | 
			
		||||
                    instance.current_step = step
 | 
			
		||||
                    instance.save(update_fields=['current_step'])
 | 
			
		||||
            except Exception:
 | 
			
		||||
                pass
 | 
			
		||||
            messages.success(request, 'گزارش رد شد و برای اصلاح به نصاب بازگشت.')
 | 
			
		||||
            return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -177,6 +230,21 @@ def installation_report_step(request, instance_id, step_id):
 | 
			
		|||
        is_suspicious = True if request.POST.get('is_meter_suspicious') == 'on' else False
 | 
			
		||||
        utm_x = request.POST.get('utm_x') or None
 | 
			
		||||
        utm_y = request.POST.get('utm_y') or None
 | 
			
		||||
        # Normalize UTM to integer meters
 | 
			
		||||
        if utm_x is not None and utm_x != '':
 | 
			
		||||
            try:
 | 
			
		||||
                utm_x = int(Decimal(str(utm_x)))
 | 
			
		||||
            except InvalidOperation:
 | 
			
		||||
                utm_x = None
 | 
			
		||||
        else:
 | 
			
		||||
            utm_x = None
 | 
			
		||||
        if utm_y is not None and utm_y != '':
 | 
			
		||||
            try:
 | 
			
		||||
                utm_y = int(Decimal(str(utm_y)))
 | 
			
		||||
            except InvalidOperation:
 | 
			
		||||
                utm_y = None
 | 
			
		||||
        else:
 | 
			
		||||
            utm_y = None
 | 
			
		||||
 | 
			
		||||
        # Build maps from form fields: remove and add
 | 
			
		||||
        remove_map = {}
 | 
			
		||||
| 
						 | 
				
			
			@ -221,8 +289,6 @@ def installation_report_step(request, instance_id, step_id):
 | 
			
		|||
                    unit_price = item_obj.unit_price if item_obj else None
 | 
			
		||||
                add_map[item_id] = {'qty': qty, 'price': unit_price}
 | 
			
		||||
 | 
			
		||||
        # اجازهٔ ثبت همزمان حذف و افزودن برای یک قلم (بدون محدودیت و ادغام)
 | 
			
		||||
 | 
			
		||||
        if existing_report and edit_mode:
 | 
			
		||||
            report = existing_report
 | 
			
		||||
            report.description = description
 | 
			
		||||
| 
						 | 
				
			
			@ -247,29 +313,7 @@ def installation_report_step(request, instance_id, step_id):
 | 
			
		|||
                InstallationPhoto.objects.create(report=report, image=f)
 | 
			
		||||
            # replace item changes with new submission
 | 
			
		||||
            report.item_changes.all().delete()
 | 
			
		||||
            for item_id, qty in remove_map.items():
 | 
			
		||||
                up = quote_price_map.get(item_id)
 | 
			
		||||
                total = (up * qty) if up is not None else None
 | 
			
		||||
                InstallationItemChange.objects.create(
 | 
			
		||||
                    report=report,
 | 
			
		||||
                    item_id=item_id,
 | 
			
		||||
                    change_type='remove',
 | 
			
		||||
                    quantity=qty,
 | 
			
		||||
                    unit_price=up,
 | 
			
		||||
                    total_price=total,
 | 
			
		||||
                )
 | 
			
		||||
            for item_id, data in add_map.items():
 | 
			
		||||
                unit_price = data.get('price')
 | 
			
		||||
                qty = data.get('qty') or 1
 | 
			
		||||
                total = (unit_price * qty) if (unit_price is not None) else None
 | 
			
		||||
                InstallationItemChange.objects.create(
 | 
			
		||||
                    report=report,
 | 
			
		||||
                    item_id=item_id,
 | 
			
		||||
                    change_type='add',
 | 
			
		||||
                    quantity=qty,
 | 
			
		||||
                    unit_price=unit_price,
 | 
			
		||||
                    total_price=total,
 | 
			
		||||
                )
 | 
			
		||||
            create_item_changes_for_report(report, remove_map, add_map, quote_price_map)
 | 
			
		||||
        else:
 | 
			
		||||
            report = InstallationReport.objects.create(
 | 
			
		||||
                assignment=assignment,
 | 
			
		||||
| 
						 | 
				
			
			@ -286,29 +330,7 @@ def installation_report_step(request, instance_id, step_id):
 | 
			
		|||
            for f in request.FILES.getlist('photos'):
 | 
			
		||||
                InstallationPhoto.objects.create(report=report, image=f)
 | 
			
		||||
            # item changes
 | 
			
		||||
            for item_id, qty in remove_map.items():
 | 
			
		||||
                up = quote_price_map.get(item_id)
 | 
			
		||||
                total = (up * qty) if up is not None else None
 | 
			
		||||
                InstallationItemChange.objects.create(
 | 
			
		||||
                    report=report,
 | 
			
		||||
                    item_id=item_id,
 | 
			
		||||
                    change_type='remove',
 | 
			
		||||
                    quantity=qty,
 | 
			
		||||
                    unit_price=up,
 | 
			
		||||
                    total_price=total,
 | 
			
		||||
                )
 | 
			
		||||
            for item_id, data in add_map.items():
 | 
			
		||||
                unit_price = data.get('price')
 | 
			
		||||
                qty = data.get('qty') or 1
 | 
			
		||||
                total = (unit_price * qty) if (unit_price is not None) else None
 | 
			
		||||
                InstallationItemChange.objects.create(
 | 
			
		||||
                    report=report,
 | 
			
		||||
                    item_id=item_id,
 | 
			
		||||
                    change_type='add',
 | 
			
		||||
                    quantity=qty,
 | 
			
		||||
                    unit_price=unit_price,
 | 
			
		||||
                    total_price=total,
 | 
			
		||||
                )
 | 
			
		||||
            create_item_changes_for_report(report, remove_map, add_map, quote_price_map)
 | 
			
		||||
 | 
			
		||||
        # After installer submits/edits, set step back to in_progress and clear approvals
 | 
			
		||||
        step_instance.status = 'in_progress'
 | 
			
		||||
| 
						 | 
				
			
			@ -319,6 +341,33 @@ def installation_report_step(request, instance_id, step_id):
 | 
			
		|||
        except Exception:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        # If the report was edited, ensure downstream steps reopen like invoices flow
 | 
			
		||||
        try:
 | 
			
		||||
            subsequent_steps = instance.process.steps.filter(order__gt=step.order)
 | 
			
		||||
            for subsequent_step in subsequent_steps:
 | 
			
		||||
                subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first()
 | 
			
		||||
                if subsequent_step_instance and subsequent_step_instance.status == 'completed':
 | 
			
		||||
                    # Reopen the step
 | 
			
		||||
                    instance.step_instances.filter(step=subsequent_step).update(
 | 
			
		||||
                        status='in_progress',
 | 
			
		||||
                        completed_at=None
 | 
			
		||||
                    )
 | 
			
		||||
                    # Clear previous approvals if any
 | 
			
		||||
                    try:
 | 
			
		||||
                        subsequent_step_instance.approvals.all().delete()
 | 
			
		||||
                    except Exception:
 | 
			
		||||
                        pass
 | 
			
		||||
        except Exception:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        # If current step is ahead of this step, reset it back to this step
 | 
			
		||||
        try:
 | 
			
		||||
            if instance.current_step and instance.current_step.order > step.order:
 | 
			
		||||
                instance.current_step = step
 | 
			
		||||
                instance.save(update_fields=['current_step'])
 | 
			
		||||
        except Exception:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        messages.success(request, 'گزارش ثبت شد و در انتظار تایید است.')
 | 
			
		||||
        return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -340,6 +389,7 @@ def installation_report_step(request, instance_id, step_id):
 | 
			
		|||
        'assignment': assignment,
 | 
			
		||||
        'report': existing_report,
 | 
			
		||||
        'edit_mode': edit_mode,
 | 
			
		||||
        'user_is_installer': user_is_installer,
 | 
			
		||||
        'quote': quote,
 | 
			
		||||
        'quote_items': quote_items,
 | 
			
		||||
        'all_items': items,
 | 
			
		||||
| 
						 | 
				
			
			@ -351,6 +401,7 @@ def installation_report_step(request, instance_id, step_id):
 | 
			
		|||
        'step_instance': step_instance,
 | 
			
		||||
        'approver_statuses': approver_statuses,
 | 
			
		||||
        'user_can_approve': user_can_approve,
 | 
			
		||||
        'can_approve_reject': can_approve_reject,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue