fix auto complete final invoice
This commit is contained in:
parent
23f50aacd4
commit
6367d34f0c
7 changed files with 160 additions and 39 deletions
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 5.2.4 on 2025-10-09 10:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('invoices', '0002_remove_historicalinvoice_paid_amount_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='historicalpayment',
|
||||
name='payment_stage',
|
||||
field=models.CharField(choices=[('quote', 'پیش\u200cفاکتور'), ('final_settlement', 'تسویه نهایی')], default='quote', max_length=20, verbose_name='مرحله پرداخت'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='payment',
|
||||
name='payment_stage',
|
||||
field=models.CharField(choices=[('quote', 'پیش\u200cفاکتور'), ('final_settlement', 'تسویه نهایی')], default='quote', max_length=20, verbose_name='مرحله پرداخت'),
|
||||
),
|
||||
]
|
||||
|
|
@ -350,6 +350,11 @@ class InvoiceItem(BaseModel):
|
|||
|
||||
class Payment(BaseModel):
|
||||
"""مدل پرداختها"""
|
||||
PAYMENT_STAGE_CHOICES = [
|
||||
('quote', 'پیشفاکتور'),
|
||||
('final_settlement', 'تسویه نهایی'),
|
||||
]
|
||||
|
||||
invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE, related_name='payments', verbose_name="فاکتور")
|
||||
amount = models.DecimalField(max_digits=15, decimal_places=2, verbose_name="مبلغ پرداخت")
|
||||
direction = models.CharField(
|
||||
|
|
@ -370,6 +375,12 @@ class Payment(BaseModel):
|
|||
default='cash',
|
||||
verbose_name="روش پرداخت"
|
||||
)
|
||||
payment_stage = models.CharField(
|
||||
max_length=20,
|
||||
choices=PAYMENT_STAGE_CHOICES,
|
||||
default='quote',
|
||||
verbose_name="مرحله پرداخت"
|
||||
)
|
||||
reference_number = models.CharField(max_length=100, verbose_name="شماره مرجع", blank=True, unique=True)
|
||||
payment_date = models.DateField(verbose_name="تاریخ پرداخت")
|
||||
notes = models.TextField(verbose_name="یادداشتها", blank=True)
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@
|
|||
<div class="bs-stepper-content">
|
||||
|
||||
<div class="row g-3">
|
||||
{% if is_broker and invoice.get_remaining_amount != 0 %}
|
||||
{% if is_broker and needs_approval %}
|
||||
<div class="col-12 col-lg-5">
|
||||
<div class="card border h-100">
|
||||
<div class="card-header"><h5 class="mb-0">ثبت تراکنش تسویه</h5></div>
|
||||
|
|
@ -193,7 +193,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if approver_statuses and invoice.get_remaining_amount != 0 and step_instance.status != 'completed' %}
|
||||
{% if approver_statuses and needs_approval and step_instance.status != 'completed' %}
|
||||
<div class="card border mt-2">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0">وضعیت تاییدها</h6>
|
||||
|
|
@ -318,7 +318,11 @@
|
|||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{% if invoice.get_remaining_amount != 0 %}
|
||||
{% if not needs_approval %}
|
||||
<div class="alert alert-info" role="alert">
|
||||
فاکتور کاملاً تسویه شده است و نیازی به تایید ندارد.
|
||||
</div>
|
||||
{% elif invoice.get_remaining_amount != 0 %}
|
||||
<div class="alert alert-warning" role="alert">
|
||||
مانده فاکتور: <strong>{{ invoice.get_remaining_amount|floatformat:0|intcomma:False }} ریال</strong><br>
|
||||
امکان تایید تا تسویه کامل فاکتور وجود ندارد.
|
||||
|
|
|
|||
|
|
@ -564,6 +564,7 @@ def add_quote_payment(request, instance_id, step_id):
|
|||
amount=amount_dec,
|
||||
payment_date=payment_date,
|
||||
payment_method=payment_method,
|
||||
payment_stage='quote',
|
||||
reference_number=reference_number,
|
||||
receipt_image=receipt_image,
|
||||
notes=notes,
|
||||
|
|
@ -1049,10 +1050,56 @@ 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'})
|
||||
|
||||
# Check if there are changes that require approval
|
||||
# (used for both auto-complete and UI display)
|
||||
has_special_charges = False
|
||||
has_installation_changes = False
|
||||
has_final_settlement_payments = False
|
||||
|
||||
try:
|
||||
has_special_charges = invoice.items.filter(item__is_special=True, is_deleted=False).exists()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
from installations.models import InstallationAssignment
|
||||
assignment = InstallationAssignment.objects.filter(process_instance=instance).first()
|
||||
if assignment:
|
||||
reports = assignment.reports.all()
|
||||
for report in reports:
|
||||
if report.item_changes.filter(is_deleted=False).exists():
|
||||
has_installation_changes = True
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Check if there are payments added during final settlement step
|
||||
# using the payment_stage field
|
||||
try:
|
||||
final_settlement_payments = invoice.payments.filter(
|
||||
is_deleted=False,
|
||||
payment_stage='final_settlement'
|
||||
)
|
||||
if final_settlement_payments.exists():
|
||||
has_final_settlement_payments = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Auto-complete step when invoice is fully settled (no approvals needed)
|
||||
# BUT only if no special charges were added in final_invoice step
|
||||
# AND no installation item changes were made
|
||||
# AND no payments were added in this final settlement step
|
||||
# (meaning the remaining amount is from the original quote_payment step)
|
||||
try:
|
||||
invoice.calculate_totals()
|
||||
if invoice.get_remaining_amount() == 0:
|
||||
remaining = invoice.get_remaining_amount()
|
||||
|
||||
# Only auto-complete if:
|
||||
# 1. Remaining amount is zero
|
||||
# 2. No special charges were added (meaning this is settling the original quote)
|
||||
# 3. No installation item changes (meaning no items added/removed in installation step)
|
||||
# 4. No payments added in final settlement step (meaning no new receipts need approval)
|
||||
if remaining == 0 and not has_special_charges and not has_installation_changes and not has_final_settlement_payments:
|
||||
if step_instance.status != 'completed':
|
||||
step_instance.status = 'completed'
|
||||
step_instance.completed_at = timezone.now()
|
||||
|
|
@ -1199,6 +1246,21 @@ def final_settlement_step(request, instance_id, step_id):
|
|||
except Exception:
|
||||
is_broker = False
|
||||
|
||||
# Determine if approval is needed
|
||||
# Approval is needed if:
|
||||
# 1. Remaining amount is not zero, OR
|
||||
# 2. Special charges were added (meaning new balance was created in final_invoice step), OR
|
||||
# 3. Installation item changes were made (meaning items were added/removed in installation step), OR
|
||||
# 4. Payments were added in final settlement step (new receipts need approval)
|
||||
needs_approval = True
|
||||
try:
|
||||
remaining = invoice.get_remaining_amount()
|
||||
# No approval needed only if: remaining is zero AND no special charges AND no installation changes AND no final settlement payments
|
||||
if remaining == 0 and not has_special_charges and not has_installation_changes and not has_final_settlement_payments:
|
||||
needs_approval = False
|
||||
except Exception:
|
||||
needs_approval = True
|
||||
|
||||
return render(request, 'invoices/final_settlement_step.html', {
|
||||
'instance': instance,
|
||||
'step': step,
|
||||
|
|
@ -1211,6 +1273,7 @@ def final_settlement_step(request, instance_id, step_id):
|
|||
'can_approve_reject': can_approve_reject,
|
||||
'is_broker': is_broker,
|
||||
'current_user_has_decided': current_user_has_decided,
|
||||
'needs_approval': needs_approval,
|
||||
'is_manager': bool(getattr(getattr(request.user, 'profile', None), 'roles', Role.objects.none()).filter(slug=UserRoles.MANAGER.value).exists()) if getattr(request.user, 'profile', None) else False,
|
||||
})
|
||||
|
||||
|
|
@ -1283,6 +1346,7 @@ def add_final_payment(request, instance_id, step_id):
|
|||
amount=amount_dec,
|
||||
payment_date=payment_date,
|
||||
payment_method=payment_method,
|
||||
payment_stage='final_settlement',
|
||||
reference_number=reference_number,
|
||||
direction='in' if direction != 'out' else 'out',
|
||||
receipt_image=receipt_image,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue