fix print and preview quote and add broker to req and complete company model.

This commit is contained in:
aminhashemi92 2025-09-07 18:43:14 +03:30
parent 5ce94214d5
commit 246a2c0759
19 changed files with 872 additions and 260 deletions

View file

@ -54,6 +54,7 @@ class ProcessInstanceAdmin(SimpleHistoryAdmin):
'well_display',
'representative',
'requester',
'broker',
'process',
'status_display',
'priority_display',
@ -65,7 +66,8 @@ class ProcessInstanceAdmin(SimpleHistoryAdmin):
'status',
'priority',
'created',
'well__representative'
'well__representative',
'broker'
]
search_fields = [
'code',
@ -86,6 +88,7 @@ class ProcessInstanceAdmin(SimpleHistoryAdmin):
'well',
'representative',
'requester',
'broker',
'process',
'current_step'
]
@ -99,7 +102,7 @@ class ProcessInstanceAdmin(SimpleHistoryAdmin):
'fields': ('well', 'representative')
}),
('اطلاعات درخواست', {
'fields': ('requester', 'priority')
'fields': ('requester', 'broker', 'priority')
}),
('وضعیت و پیشرفت', {
'fields': ('status', 'current_step')

View file

@ -0,0 +1,20 @@
# Generated by Django 5.2.4 on 2025-09-07 13:43
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('locations', '0003_remove_broker_company'),
('processes', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='processinstance',
name='broker',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='process_instances', to='locations.broker', verbose_name='کارگزار'),
),
]

View file

@ -5,7 +5,7 @@ from simple_history.models import HistoricalRecords
from django.core.exceptions import ValidationError
from django.utils import timezone
from django.conf import settings
from accounts.models import Role
from accounts.models import Role, Broker
from _helpers.utils import generate_unique_slug
import random
@ -174,7 +174,7 @@ class ProcessInstance(SluggedModel):
blank=True,
verbose_name="مرحله فعلی",
)
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
@ -188,6 +188,15 @@ class ProcessInstance(SluggedModel):
default='medium',
verbose_name="اولویت"
)
broker = models.ForeignKey(
Broker,
on_delete=models.SET_NULL,
verbose_name="کارگزار",
blank=True,
null=True,
related_name='process_instances'
)
completed_at = models.DateTimeField(
null=True,
@ -205,13 +214,6 @@ class ProcessInstance(SluggedModel):
return f"{self.process.name} - {self.well.water_subscription_number}"
return f"{self.process.name} - {self.requester.get_full_name()}"
def clean(self):
"""اعتبارسنجی مدل"""
if self.well and self.representative and self.well.representative != self.representative:
raise ValidationError("نماینده درخواست باید همان نماینده ثبت شده در چاه باشد")
if self.well and self.representative and self.requester == self.representative:
raise ValidationError("درخواست کننده نمی‌تواند نماینده چاه باشد")
def save(self, *args, **kwargs):
# Generate unique 5-digit numeric code if missing
@ -233,6 +235,13 @@ class ProcessInstance(SluggedModel):
if self.status == 'completed' and not self.completed_at:
self.completed_at = timezone.now()
# Auto-set broker if not already set
if not self.broker:
if self.well and hasattr(self.well, 'broker') and self.well.broker:
self.broker = self.well.broker
elif self.requester and hasattr(self.requester, 'profile') and self.requester.profile and hasattr(self.requester.profile, 'broker') and self.requester.profile.broker:
self.broker = self.requester.profile.broker
super().save(*args, **kwargs)
def get_status_display_with_color(self):

View file

@ -0,0 +1,275 @@
{% load common_tags %}
<!-- Modal for Instance Info -->
<div class="modal fade" id="{{ modal_id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">اطلاعات درخواست {{ instance.code }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row g-4">
<!-- Well Information -->
{% if well %}
<div class="col-12">
<div class="card border-0 bg-light">
<div class="card-header bg-label-primary text-white py-2">
<h6 class="mb-0">
<i class="bx bx-water me-2"></i>اطلاعات چاه
</h6>
</div>
<div class="card-body pt-3">
<div class="row g-3">
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-droplet text-primary me-2"></i>
<strong>شماره اشتراک آب:</strong>
<span class="ms-2">{{ well.water_subscription_number|default:"-" }}</span>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-bolt text-warning me-2"></i>
<strong>شماره اشتراک برق:</strong>
<span class="ms-2">{{ well.electricity_subscription_number|default:"-" }}</span>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-barcode text-info me-2"></i>
<strong>سریال کنتور:</strong>
<span class="ms-2">{{ well.water_meter_serial_number|default:"-" }}</span>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-barcode-reader text-secondary me-2"></i>
<strong>سریال قدیمی:</strong>
<span class="ms-2">{{ well.water_meter_old_serial_number|default:"-" }}</span>
</div>
</div>
{% if well.water_meter_manufacturer %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-factory text-success me-2"></i>
<strong>سازنده کنتور:</strong>
<span class="ms-2">{{ well.water_meter_manufacturer.name }}</span>
</div>
</div>
{% endif %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-tachometer text-danger me-2"></i>
<strong>قدرت چاه:</strong>
<span class="ms-2">{{ well.well_power|default:"-" }}</span>
</div>
</div>
{% if well.utm_x and well.utm_y %}
<div class="col-12">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-map text-info me-2"></i>
<strong>مختصات:</strong>
<span class="ms-2">X: {{ well.utm_x }}, Y: {{ well.utm_y }}</span>
{% if well.utm_zone %}<span class="text-muted ms-2">(Zone: {{ well.utm_zone }})</span>{% endif %}
</div>
</div>
{% endif %}
{% if well.county %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-map-pin text-warning me-2"></i>
<strong>شهرستان:</strong>
<span class="ms-2">{{ well.county }}</span>
</div>
</div>
{% endif %}
{% if well.affairs %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-building text-primary me-2"></i>
<strong>امور:</strong>
<span class="ms-2">{{ well.affairs }}</span>
</div>
</div>
{% endif %}
{% if well.reference_letter_number %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-file text-secondary me-2"></i>
<strong>شماره معرفی نامه:</strong>
<span class="ms-2">{{ well.reference_letter_number }}</span>
{% if well.reference_letter_date %}
<span class="text-muted ms-2">({{ well.reference_letter_date|to_jalali }})</span>
{% endif %}
</div>
</div>
{% endif %}
{% if well.representative_letter_file %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-file text-secondary me-2"></i>
<strong>فایل نامه نمایندگی:</strong>
<a href="{{ well.representative_letter_file.url }}" class="ms-2">دانلود</a>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
<!-- Representative Information -->
{% if representative %}
<div class="col-12">
<div class="card border-0 bg-light">
<div class="card-header bg-label-success text-white py-2">
<h6 class="mb-0">
<i class="bx bx-user me-2"></i>اطلاعات نماینده
</h6>
</div>
<div class="card-body pt-3">
<div class="row g-3">
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-user-circle text-primary me-2"></i>
<strong>نام و نام خانوادگی:</strong>
<span class="ms-2">{{ representative.get_full_name|default:representative.username }}</span>
</div>
</div>
{% if representative.profile.national_code %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-id-card text-info me-2"></i>
<strong>کد ملی:</strong>
<span class="ms-2">{{ representative.profile.national_code }}</span>
</div>
</div>
{% endif %}
{% if representative.profile.phone_number_1 %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-phone text-success me-2"></i>
<strong>تلفن اول:</strong>
<span class="ms-2">{{ representative.profile.phone_number_1 }}</span>
</div>
</div>
{% endif %}
{% if representative.profile.phone_number_2 %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-phone text-success me-2"></i>
<strong>تلفن دوم:</strong>
<span class="ms-2">{{ representative.profile.phone_number_2 }}</span>
</div>
</div>
{% endif %}
{% if representative.profile.bank_name %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-credit-card text-warning me-2"></i>
<strong>بانک:</strong>
<span class="ms-2">{{ representative.profile.get_bank_name_display }}</span>
</div>
</div>
{% endif %}
{% if representative.profile.card_number %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-credit-card-alt text-secondary me-2"></i>
<strong>شماره کارت:</strong>
<span class="ms-2">{{ representative.profile.card_number }}</span>
</div>
</div>
{% endif %}
{% if representative.profile.account_number %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-wallet text-info me-2"></i>
<strong>شماره حساب:</strong>
<span class="ms-2">{{ representative.profile.account_number }}</span>
</div>
</div>
{% endif %}
{% if representative.profile.address %}
<div class="col-md-6">
<div class="d-flex align-items-start mb-2">
<i class="bx bx-map text-danger me-2 mt-1"></i>
<div>
<strong>آدرس:</strong>
<p class="mb-0 ms-2 text-wrap">{{ representative.profile.address }}</p>
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
<!-- Process Information -->
<div class="col-12">
<div class="card border-0 bg-light">
<div class="card-header bg-label-info text-white py-2">
<h6 class="mb-0">
<i class="bx bx-cog me-2"></i>اطلاعات فرآیند
</h6>
</div>
<div class="card-body pt-3">
<div class="row g-3">
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-list-ul text-primary me-2"></i>
<strong>نوع فرآیند:</strong>
<span class="ms-2">{{ instance.process.name }}</span>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-calendar text-success me-2"></i>
<strong>تاریخ ایجاد:</strong>
<span class="ms-2">{{ instance.jcreated }}</span>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-check-circle text-info me-2"></i>
<strong>وضعیت:</strong>
<span class="ms-2 badge bg-label-primary">{{ instance.get_status_display }}</span>
</div>
</div>
{% if instance.current_step %}
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<i class="bx bx-step-forward text-primary me-2"></i>
<strong>مرحله فعلی:</strong>
<span class="ms-2 badge bg-label-success">{{ instance.current_step.name }}</span>
</div>
</div>
{% endif %}
{% if instance.description %}
<div class="col-md-6">
<div class="d-flex align-items-start mb-2">
<i class="bx bx-note text-secondary me-2 mt-1"></i>
<div>
<strong>توضیحات:</strong>
<p class="mb-0 ms-2 text-wrap">{{ instance.description }}</p>
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">بستن</button>
</div>
</div>
</div>
</div>

View file

@ -50,3 +50,57 @@ def stepper_header(instance, current_step=None):
}
# moved to _base/common/templatetags/common_tags.py
@register.inclusion_tag('processes/includes/instance_info_modal.html')
def instance_info_modal(instance, modal_id=None):
"""
نمایش مدال اطلاعات کامل چاه و نماینده
استفاده:
{% load processes_tags %}
{% instance_info_modal instance %}
یا با modal_id سفارشی:
{% instance_info_modal instance "myCustomModal" %}
"""
if not isinstance(instance, ProcessInstance):
return {}
if not modal_id:
modal_id = f"instanceInfoModal_{instance.id}"
return {
'instance': instance,
'modal_id': modal_id,
'well': instance.well,
'representative': instance.representative,
}
@register.simple_tag
def instance_info(instance, modal_id=None):
"""
آیکون info برای نمایش مدال اطلاعات
استفاده:
{% load processes_tags %}
نام کاربر: {{ user.name }} {% instance_info_icon instance %}
یا با modal_id سفارشی:
{% instance_info_icon instance "myCustomModal" %}
"""
if not isinstance(instance, ProcessInstance):
return ""
if not modal_id:
modal_id = f"instanceInfoModal_{instance.id}"
html = f'''
اشتراک آب: {instance.well.water_subscription_number }
| نماینده: {instance.representative.profile.national_code }
<i class="bx bx-info-circle text-muted ms-1"
style="cursor: pointer; font-size: 14px;"
data-bs-toggle="modal"
data-bs-target="#{modal_id}"
title="اطلاعات کامل چاه و نماینده"></i>
'''
return mark_safe(html)

View file

@ -237,6 +237,7 @@ def create_request_with_entities(request):
well=well,
representative=representative_user,
requester=request.user,
broker=request.user.profile.broker if request.user.profile else None,
status='pending',
priority='medium',
)