128 lines
6.6 KiB
Python
128 lines
6.6 KiB
Python
from django.shortcuts import render, get_object_or_404, redirect
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
from decimal import Decimal
|
|
from django.template import Template, Context
|
|
from django.utils.safestring import mark_safe
|
|
from processes.models import ProcessInstance, StepInstance
|
|
from common.consts import UserRoles
|
|
from .models import ContractTemplate, ContractInstance
|
|
from invoices.models import Invoice, Quote
|
|
from _helpers.utils import jalali_converter2
|
|
from django.http import JsonResponse
|
|
from processes.utils import get_scoped_instance_or_404
|
|
|
|
|
|
def build_contract_context(instance: ProcessInstance) -> dict:
|
|
representative = instance.representative
|
|
profile = getattr(representative, 'profile', None)
|
|
well = instance.well
|
|
# Compute prepayment from Quote-linked invoice payments
|
|
quote = Quote.objects.filter(process_instance=instance).first()
|
|
invoice = Invoice.objects.filter(quote=quote).first() if quote else None
|
|
payments_qs = invoice.payments.filter(is_deleted=False, direction='in').all() if invoice else []
|
|
total_paid = sum((p.amount for p in payments_qs), Decimal('0'))
|
|
try:
|
|
latest_payment_date = max((p.payment_date for p in payments_qs)) if payments_qs else None
|
|
except Exception:
|
|
latest_payment_date = None
|
|
|
|
return {
|
|
'customer_full_name': mark_safe(f"<span class=\"fw-bold\">{representative.get_full_name() if representative else ''}</span>"),
|
|
'registration_number': mark_safe(f"<span class=\"fw-bold\">{instance.broker.company.registration_number if instance.broker and instance.broker.company else ''}</span>"),
|
|
'national_code': mark_safe(f"<span class=\"fw-bold\">{profile.national_code if profile else ''}</span>"),
|
|
'address': mark_safe(f"<span class=\"fw-bold\">{profile.address if profile else ''}</span>"),
|
|
'phone': mark_safe(f"<span class=\"fw-bold\">{profile.phone_number_1 if profile else ''}</span>"),
|
|
'phone2': mark_safe(f"<span class=\"fw-bold\">{profile.phone_number_2 if profile else ''}</span>"),
|
|
'water_subscription_number': mark_safe(f"<span class=\"fw-bold\">{well.water_subscription_number if well else ''}</span>"),
|
|
'electricity_subscription_number': mark_safe(f"<span class=\"fw-bold\">{well.electricity_subscription_number if well else ''}</span>"),
|
|
'water_meter_serial_number': mark_safe(f"<span class=\"fw-bold\">{well.water_meter_serial_number if well else ''}</span>"),
|
|
'well_power': mark_safe(f"<span class=\"fw-bold\">{well.well_power if well else ''}</span>"),
|
|
'request_code': mark_safe(f"<span class=\"fw-bold\">{instance.code}</span>"),
|
|
'today': mark_safe(f"<span class=\"fw-bold\">{jalali_converter2(timezone.now())}</span>"),
|
|
'company_name': mark_safe(f"<span class=\"fw-bold\">{instance.broker.company.name if instance.broker and instance.broker.company else ''}</span>"),
|
|
'city_name': mark_safe(f"<span class=\"fw-bold\">{instance.broker.affairs.county.city.name if instance.broker and instance.broker.affairs and instance.broker.affairs.county and instance.broker.affairs.county.city else ''}</span>"),
|
|
'card_number': mark_safe(f"<span class=\"fw-bold\">{instance.representative.profile.card_number if instance.representative else ''}</span>"),
|
|
'account_number': mark_safe(f"<span class=\"fw-bold\">{instance.representative.profile.account_number if instance.representative else ''}</span>"),
|
|
'bank_name': mark_safe(f"<span class=\"fw-bold\">{instance.representative.profile.get_bank_name_display() if instance.representative else ''}</span>"),
|
|
'prepayment_amount': mark_safe(f"<span class=\"fw-bold\">{int(total_paid):,}</span>"),
|
|
'prepayment_date': mark_safe(f"<span class=\"fw-bold\">{jalali_converter2(latest_payment_date)}</span>") if latest_payment_date else '',
|
|
}
|
|
|
|
|
|
@login_required
|
|
def contract_step(request, instance_id, step_id):
|
|
instance = get_scoped_instance_or_404(request, instance_id)
|
|
# Resolve step navigation
|
|
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()
|
|
|
|
profile = getattr(request.user, 'profile', None)
|
|
is_broker = False
|
|
can_view_contract_body = True
|
|
try:
|
|
is_broker = bool(profile and profile.has_role(UserRoles.BROKER))
|
|
if profile and profile.has_role(UserRoles.INSTALLER):
|
|
can_view_contract_body = False
|
|
except Exception:
|
|
pass
|
|
|
|
template_obj = ContractTemplate.objects.first()
|
|
if not template_obj:
|
|
return render(request, 'contracts/contract_missing.html', {'instance': instance})
|
|
|
|
ctx = build_contract_context(instance)
|
|
rendered = Template(template_obj.body).render(Context(ctx))
|
|
|
|
contract, _ = ContractInstance.objects.get_or_create(
|
|
process_instance=instance,
|
|
defaults={
|
|
'template': template_obj,
|
|
'rendered_body': rendered,
|
|
'created_by': request.user,
|
|
}
|
|
)
|
|
# keep latest rendering if template changed (optional)
|
|
contract.template = template_obj
|
|
contract.rendered_body = rendered
|
|
contract.save()
|
|
|
|
# If user submits to go next, only broker can complete and go to next
|
|
if request.method == 'POST':
|
|
if not is_broker:
|
|
return JsonResponse({'success': False, 'message': 'شما مجوز تایید این مرحله را ندارید'}, status=403)
|
|
StepInstance.objects.update_or_create(
|
|
process_instance=instance,
|
|
step=step,
|
|
defaults={'status': 'completed', 'completed_at': timezone.now()}
|
|
)
|
|
if next_step:
|
|
instance.current_step = next_step
|
|
instance.save()
|
|
return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id)
|
|
return redirect('processes:request_list')
|
|
|
|
return render(request, 'contracts/contract_step.html', {
|
|
'instance': instance,
|
|
'step': step,
|
|
'contract': contract,
|
|
'template': template_obj,
|
|
'previous_step': previous_step,
|
|
'next_step': next_step,
|
|
'is_broker': is_broker,
|
|
'can_view_contract_body': can_view_contract_body,
|
|
})
|
|
|
|
|
|
@login_required
|
|
def contract_print(request, instance_id):
|
|
instance = get_scoped_instance_or_404(request, instance_id)
|
|
contract = get_object_or_404(ContractInstance, process_instance=instance)
|
|
return render(request, 'contracts/contract_print.html', {
|
|
'instance': instance,
|
|
'contract': contract,
|
|
})
|
|
|
|
|