fix final payment step.
This commit is contained in:
		
							parent
							
								
									93db2fe7f5
								
							
						
					
					
						commit
						9592c00565
					
				
					 6 changed files with 305 additions and 80 deletions
				
			
		| 
						 | 
				
			
			@ -12,7 +12,7 @@ import json
 | 
			
		|||
from processes.models import ProcessInstance, ProcessStep, StepInstance, StepRejection, StepApproval
 | 
			
		||||
from accounts.models import Role
 | 
			
		||||
from common.consts import UserRoles
 | 
			
		||||
from .models import Item, Quote, QuoteItem, Payment, Invoice
 | 
			
		||||
from .models import Item, Quote, QuoteItem, Payment, Invoice, InvoiceItem
 | 
			
		||||
from installations.models import InstallationReport, InstallationItemChange
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -792,14 +792,7 @@ def approve_final_invoice(request, instance_id, step_id):
 | 
			
		|||
            return JsonResponse({'success': False, 'message': 'شما مجوز تایید این مرحله را ندارید'}, status=403)
 | 
			
		||||
    except Exception:
 | 
			
		||||
        return JsonResponse({'success': False, 'message': 'شما مجوز تایید این مرحله را ندارید'}, status=403)
 | 
			
		||||
    # Block approval when there is any remaining (positive or negative)
 | 
			
		||||
    invoice.calculate_totals()
 | 
			
		||||
    # if invoice.remaining_amount != 0:
 | 
			
		||||
    #     return JsonResponse({
 | 
			
		||||
    #         'success': False,
 | 
			
		||||
    #         'message': f"تا زمانی که مانده فاکتور صفر نشده امکان تایید نیست (مانده فعلی: {invoice.remaining_amount})"
 | 
			
		||||
    #     })
 | 
			
		||||
    # mark step completed
 | 
			
		||||
 | 
			
		||||
    step_instance, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step)
 | 
			
		||||
    step_instance.status = 'completed'
 | 
			
		||||
    step_instance.completed_at = timezone.now()
 | 
			
		||||
| 
						 | 
				
			
			@ -826,7 +819,7 @@ def add_special_charge(request, instance_id, step_id):
 | 
			
		|||
            return JsonResponse({'success': False, 'message': 'شما مجوز افزودن هزینه ویژه را ندارید'}, status=403)
 | 
			
		||||
    except Exception:
 | 
			
		||||
        return JsonResponse({'success': False, 'message': 'شما مجوز افزودن هزینه ویژه را ندارید'}, status=403)
 | 
			
		||||
    # charge_type was removed from UI; we no longer require it
 | 
			
		||||
 | 
			
		||||
    item_id = request.POST.get('item_id')
 | 
			
		||||
    amount = (request.POST.get('amount') or '').strip()
 | 
			
		||||
    if not item_id:
 | 
			
		||||
| 
						 | 
				
			
			@ -841,7 +834,7 @@ def add_special_charge(request, instance_id, step_id):
 | 
			
		|||
    # Fetch existing special item from DB
 | 
			
		||||
    special_item = get_object_or_404(Item, id=item_id, is_special=True)
 | 
			
		||||
 | 
			
		||||
    from .models import InvoiceItem
 | 
			
		||||
    
 | 
			
		||||
    InvoiceItem.objects.create(
 | 
			
		||||
        invoice=invoice,
 | 
			
		||||
        item=special_item,
 | 
			
		||||
| 
						 | 
				
			
			@ -863,7 +856,6 @@ def delete_special_charge(request, instance_id, step_id, item_id):
 | 
			
		|||
            return JsonResponse({'success': False, 'message': 'شما مجوز حذف هزینه ویژه را ندارید'}, status=403)
 | 
			
		||||
    except Exception:
 | 
			
		||||
        return JsonResponse({'success': False, 'message': 'شما مجوز حذف هزینه ویژه را ندارید'}, status=403)
 | 
			
		||||
    from .models import InvoiceItem
 | 
			
		||||
    inv_item = get_object_or_404(InvoiceItem, id=item_id, invoice=invoice)
 | 
			
		||||
    # allow deletion only for special items
 | 
			
		||||
    try:
 | 
			
		||||
| 
						 | 
				
			
			@ -880,6 +872,7 @@ def delete_special_charge(request, instance_id, step_id, item_id):
 | 
			
		|||
def final_settlement_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)
 | 
			
		||||
 | 
			
		||||
    if not instance.can_access_step(step):
 | 
			
		||||
        messages.error(request, 'شما به این مرحله دسترسی ندارید. ابتدا مراحل قبلی را تکمیل کنید.')
 | 
			
		||||
        return redirect('processes:request_list')
 | 
			
		||||
| 
						 | 
				
			
			@ -890,6 +883,7 @@ def final_settlement_step(request, instance_id, step_id):
 | 
			
		|||
 | 
			
		||||
    # Ensure step instance exists
 | 
			
		||||
    step_instance, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step, defaults={'status': 'in_progress'})
 | 
			
		||||
    
 | 
			
		||||
    # Build approver statuses for template
 | 
			
		||||
    reqs = list(step.approver_requirements.select_related('role').all())
 | 
			
		||||
    approvals_map = {a.role_id: a.decision for a in step_instance.approvals.select_related('role').all()}
 | 
			
		||||
| 
						 | 
				
			
			@ -947,6 +941,13 @@ def final_settlement_step(request, instance_id, step_id):
 | 
			
		|||
                defaults={'approved_by': request.user, 'decision': 'rejected', 'reason': reason}
 | 
			
		||||
            )
 | 
			
		||||
            StepRejection.objects.create(step_instance=step_instance, rejected_by=request.user, reason=reason)
 | 
			
		||||
            # If current step is ahead of this step, reset it back to this step (align behavior with other steps)
 | 
			
		||||
            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('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -984,6 +985,7 @@ def add_final_payment(request, instance_id, step_id):
 | 
			
		|||
            return JsonResponse({'success': False, 'message': 'شما مجوز افزودن تراکنش تسویه را ندارید'}, status=403)
 | 
			
		||||
    except Exception:
 | 
			
		||||
        return JsonResponse({'success': False, 'message': 'شما مجوز افزودن تراکنش تسویه را ندارید'}, status=403)
 | 
			
		||||
 | 
			
		||||
    amount = (request.POST.get('amount') or '').strip()
 | 
			
		||||
    payment_date = (request.POST.get('payment_date') or '').strip()
 | 
			
		||||
    payment_method = (request.POST.get('payment_method') or '').strip()
 | 
			
		||||
| 
						 | 
				
			
			@ -1038,12 +1040,14 @@ def add_final_payment(request, instance_id, step_id):
 | 
			
		|||
    )
 | 
			
		||||
    # After creation, totals auto-updated by model save. Respond with redirect and new totals for UX.
 | 
			
		||||
    invoice.refresh_from_db()
 | 
			
		||||
    # After payment change, set step back to in_progress
 | 
			
		||||
 | 
			
		||||
    # On delete, return to awaiting approval
 | 
			
		||||
    try:
 | 
			
		||||
        si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step)
 | 
			
		||||
        si.status = 'in_progress'
 | 
			
		||||
        si.completed_at = None
 | 
			
		||||
        si.save()
 | 
			
		||||
        si.approvals.all().delete()
 | 
			
		||||
    except Exception:
 | 
			
		||||
        pass
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -1065,6 +1069,16 @@ def add_final_payment(request, instance_id, step_id):
 | 
			
		|||
                    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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return JsonResponse({
 | 
			
		||||
        'success': True,
 | 
			
		||||
        'redirect': reverse('invoices:final_settlement_step', args=[instance.id, step_id]),
 | 
			
		||||
| 
						 | 
				
			
			@ -1091,14 +1105,44 @@ def delete_final_payment(request, instance_id, step_id, payment_id):
 | 
			
		|||
        return JsonResponse({'success': False, 'message': 'شما مجوز حذف تراکنش تسویه را ندارید'}, status=403)
 | 
			
		||||
    payment.delete()
 | 
			
		||||
    invoice.refresh_from_db()
 | 
			
		||||
    # After payment change, set step back to in_progress
 | 
			
		||||
    
 | 
			
		||||
    # On delete, return to awaiting approval
 | 
			
		||||
    try:
 | 
			
		||||
        si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step)
 | 
			
		||||
        si.status = 'in_progress'
 | 
			
		||||
        si.completed_at = None
 | 
			
		||||
        si.save()
 | 
			
		||||
        si.approvals.all().delete()
 | 
			
		||||
    except Exception:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    # Reset ALL subsequent completed steps to in_progress
 | 
			
		||||
    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':
 | 
			
		||||
                # Bypass validation by using update() instead of save()
 | 
			
		||||
                instance.step_instances.filter(step=subsequent_step).update(
 | 
			
		||||
                    status='in_progress',
 | 
			
		||||
                    completed_at=None
 | 
			
		||||
                )
 | 
			
		||||
                # Clear previous approvals if the step requires re-approval
 | 
			
		||||
                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
 | 
			
		||||
 | 
			
		||||
    return JsonResponse({'success': True, 'redirect': reverse('invoices:final_settlement_step', args=[instance.id, step_id]), 'totals': {
 | 
			
		||||
        'final_amount': str(invoice.final_amount),
 | 
			
		||||
        'paid_amount': str(invoice.paid_amount),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue