main flow checked

This commit is contained in:
aminhashemi92 2025-10-03 21:56:25 +03:30
parent b5bf3a5dbe
commit f853ad9784
21 changed files with 365 additions and 89 deletions

View file

@ -145,7 +145,17 @@
<!-- Customer & Well Info (compact to match preview) -->
<div class="row mb-3">
<div class="col-6">
<h6 class="fw-bold mb-2">اطلاعات مشترک</h6>
<h6 class="fw-bold mb-2">
{% if instance.representative.profile.user_type == 'legal' %}
اطلاعات مشترک (حقوقی)
{% else %}
اطلاعات مشترک (حقیقی)
{% endif %}
</h6>
{% if instance.representative.profile.user_type == 'legal' %}
<div class="small mb-1"><span class="text-muted">نام شرکت:</span> {{ instance.representative.profile.company_name|default:"-" }}</div>
<div class="small mb-1"><span class="text-muted">شناسه ملی:</span> {{ instance.representative.profile.company_national_id|default:"-" }}</div>
{% endif %}
<div class="small mb-1"><span class="text-muted">نام:</span> {{ quote.customer.get_full_name }}</div>
{% if instance.representative.profile and instance.representative.profile.national_code %}
<div class="small mb-1"><span class="text-muted">کد ملی:</span> {{ instance.representative.profile.national_code }}</div>

View file

@ -57,7 +57,7 @@
<div class="alert alert-info">
<h6>پیش‌فاکتور موجود</h6>
<span class="mb-1">{{ existing_quote.name }} | </span>
<span class="mb-1">مبلغ کل: {{ existing_quote.final_amount|floatformat:0|intcomma:False }} تومان | </span>
<span class="mb-1">مبلغ کل (با احتساب مالیات): {{ existing_quote.final_amount|floatformat:0|intcomma:False }} تومان | </span>
<span class="mb-0">وضعیت: {{ existing_quote.get_status_display_with_color|safe }}</span>
</div>
</div>

View file

@ -358,14 +358,28 @@ def quote_payment_step(request, instance_id, step_id):
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 []
approvals_list = list(step_instance.approvals.select_related('role', 'approved_by').filter(is_deleted=False))
rejections_list = list(step_instance.rejections.select_related('role', 'rejected_by').filter(is_deleted=False))
approvals_by_role = {a.role_id: a for a in approvals_list}
rejections_by_role = {r.role_id: r for r in rejections_list}
approver_statuses = []
for r in reqs:
appr = approvals_by_role.get(r.role_id)
rejection = rejections_by_role.get(r.role_id)
if appr:
status = 'approved'
reason = appr.reason
elif rejection:
status = 'rejected'
reason = rejection.reason
else:
status = None
reason = ''
approver_statuses.append({
'role': r.role,
'status': (appr.decision if appr else None),
'reason': (appr.reason if appr else ''),
'status': status,
'reason': reason,
})
# dynamic permission: who can approve/reject this step (based on requirements)
@ -398,10 +412,11 @@ def quote_payment_step(request, instance_id, step_id):
action = request.POST.get('action')
if action == 'approve':
StepApproval.objects.update_or_create(
StepApproval.objects.create(
step_instance=step_instance,
role=matching_role,
defaults={'approved_by': request.user, 'decision': 'approved', 'reason': ''}
approved_by=request.user,
reason=''
)
if step_instance.is_fully_approved():
step_instance.status = 'completed'
@ -422,12 +437,12 @@ def quote_payment_step(request, instance_id, step_id):
if not reason:
messages.error(request, 'علت رد شدن را وارد کنید')
return redirect('invoices:quote_payment_step', instance_id=instance.id, step_id=step.id)
StepApproval.objects.update_or_create(
StepRejection.objects.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)
rejected_by=request.user,
reason=reason
)
# 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:
@ -928,15 +943,29 @@ def final_settlement_step(request, instance_id, step_id):
# Build approver statuses for template (include reason to display in UI)
reqs = list(step.approver_requirements.select_related('role').all())
approvals = list(step_instance.approvals.select_related('role').all())
rejections = list(step_instance.rejections.select_related('role').all())
approvals_by_role = {a.role_id: a for a in approvals}
approver_statuses = [
{
rejections_by_role = {r.role_id: r for r in rejections}
approver_statuses = []
for r in reqs:
appr = approvals_by_role.get(r.role_id)
rejection = rejections_by_role.get(r.role_id)
if appr:
status = 'approved'
reason = appr.reason
elif rejection:
status = 'rejected'
reason = rejection.reason
else:
status = None
reason = ''
approver_statuses.append({
'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
]
'status': status,
'reason': reason,
})
# dynamic permission to control approve/reject UI
try:
user_roles_qs = getattr(getattr(request.user, 'profile', None), 'roles', Role.objects.none())
@ -971,10 +1000,11 @@ def final_settlement_step(request, instance_id, step_id):
if invoice.remaining_amount != 0:
messages.error(request, f"تا زمانی که مانده فاکتور صفر نشده امکان تایید نیست (مانده فعلی: {invoice.remaining_amount})")
return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id)
StepApproval.objects.update_or_create(
StepApproval.objects.create(
step_instance=step_instance,
role=matching_role,
defaults={'approved_by': request.user, 'decision': 'approved', 'reason': ''}
approved_by=request.user,
reason=''
)
if step_instance.is_fully_approved():
step_instance.status = 'completed'
@ -993,12 +1023,12 @@ def final_settlement_step(request, instance_id, step_id):
if not reason:
messages.error(request, 'علت رد شدن را وارد کنید')
return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id)
StepApproval.objects.update_or_create(
StepRejection.objects.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)
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: