diff --git a/db.sqlite3 b/db.sqlite3 index ec04a21..53afd6e 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/invoices/views.py b/invoices/views.py index 4651dba..19e988f 100644 --- a/invoices/views.py +++ b/invoices/views.py @@ -885,6 +885,8 @@ def add_special_charge(request, instance_id, step_id): """افزودن هزینه ویژه تعمیر/تعویض به فاکتور نهایی به‌صورت آیتم جداگانه""" instance = get_scoped_instance_or_404(request, instance_id) invoice = get_object_or_404(Invoice, process_instance=instance) + step = get_object_or_404(instance.process.steps, id=step_id) + # only MANAGER can add special charges try: if not (hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.MANAGER)): @@ -914,29 +916,50 @@ def add_special_charge(request, instance_id, step_id): unit_price=amount_dec, ) invoice.calculate_totals() - # If the next step was completed, reopen it (set to in_progress) due to invoice change + + + # After modifying payments, set step back to in_progress try: - step = get_object_or_404(instance.process.steps, id=step_id) - next_step = instance.process.steps.filter(order__gt=step.order).first() - if next_step: - si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=next_step) - if si.status in ['completed', 'approved']: - si.status = 'in_progress' - si.completed_at = None - si.save(update_fields=['status', 'completed_at']) - # Clear prior approvals/rejections as the underlying totals changed + si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step) + si.status = 'in_progress' + si.completed_at = None + si.save() + 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: + # Bypass validation by using update() instead of save() + instance.step_instances.filter(step=subsequent_step).update( + status='in_progress', + completed_at=None + ) + # Clear prior approvals/rejections as the underlying totals changed try: - for appr in list(si.approvals.all()): + for appr in list(subsequent_step_instance.approvals.all()): appr.delete() except Exception: pass try: - for rej in list(si.rejections.all()): + for rej in list(subsequent_step_instance.rejections.all()): rej.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_invoice_step', args=[instance.id, step_id])}) @@ -945,6 +968,8 @@ def add_special_charge(request, instance_id, step_id): def delete_special_charge(request, instance_id, step_id, item_id): instance = get_scoped_instance_or_404(request, instance_id) invoice = get_object_or_404(Invoice, process_instance=instance) + step = get_object_or_404(instance.process.steps, id=step_id) + # only MANAGER can delete special charges try: if not (hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.MANAGER)): @@ -960,29 +985,51 @@ def delete_special_charge(request, instance_id, step_id, item_id): return JsonResponse({'success': False, 'message': 'امکان حذف این مورد وجود ندارد'}) inv_item.hard_delete() invoice.calculate_totals() - # If the next step was completed, reopen it (set to in_progress) due to invoice change + + + # After modifying payments, set step back to in_progress try: - step = get_object_or_404(instance.process.steps, id=step_id) - next_step = instance.process.steps.filter(order__gt=step.order).first() - if next_step: - si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=next_step) - if si.status in ['completed', 'approved']: - si.status = 'in_progress' - si.completed_at = None - si.save(update_fields=['status', 'completed_at']) - # Clear prior approvals/rejections as the underlying totals changed + si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step) + si.status = 'in_progress' + si.completed_at = None + si.save() + 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: + # Bypass validation by using update() instead of save() + instance.step_instances.filter(step=subsequent_step).update( + status='in_progress', + completed_at=None + ) + # Clear prior approvals/rejections as the underlying totals changed try: - for appr in list(si.approvals.all()): + for appr in list(subsequent_step_instance.approvals.all()): appr.delete() except Exception: pass try: - for rej in list(si.rejections.all()): + for rej in list(subsequent_step_instance.rejections.all()): rej.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_invoice_step', args=[instance.id, step_id])})