first commit
This commit is contained in:
commit
b71ea45681
898 changed files with 138202 additions and 0 deletions
0
processes/__init__.py
Normal file
0
processes/__init__.py
Normal file
107
processes/admin.py
Normal file
107
processes/admin.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
from django.contrib import admin
|
||||
from simple_history.admin import SimpleHistoryAdmin
|
||||
from django.utils.html import format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from .models import Process, ProcessStep, ProcessInstance, StepInstance, StepDependency, StepRejection, StepRevision
|
||||
|
||||
@admin.register(Process)
|
||||
class ProcessAdmin(SimpleHistoryAdmin):
|
||||
list_display = ['name', 'is_active', 'created_by', 'created', 'steps_count']
|
||||
list_filter = ['is_active', 'created']
|
||||
search_fields = ['name', 'description', 'created_by__username']
|
||||
prepopulated_fields = {'slug': ('name',)}
|
||||
readonly_fields = ['deleted_at', 'created', 'updated']
|
||||
|
||||
def steps_count(self, obj):
|
||||
return obj.steps.count()
|
||||
steps_count.short_description = "تعداد مراحل"
|
||||
|
||||
@admin.register(ProcessStep)
|
||||
class ProcessStepAdmin(SimpleHistoryAdmin):
|
||||
list_display = ['name', 'process', 'order', 'is_required', 'dependencies_display', 'blocks_previous', 'can_go_back']
|
||||
list_filter = ['process', 'is_required', 'blocks_previous', 'can_go_back']
|
||||
search_fields = ['name', 'process__name', 'description']
|
||||
prepopulated_fields = {'slug': ('name',)}
|
||||
readonly_fields = ['deleted_at']
|
||||
ordering = ['process', 'order']
|
||||
|
||||
def dependencies_display(self, obj):
|
||||
dependencies = obj.get_dependencies()
|
||||
if dependencies:
|
||||
dependency_names = ProcessStep.objects.filter(id__in=dependencies).values_list('name', flat=True)
|
||||
return ", ".join(dependency_names)
|
||||
return "بدون وابستگی"
|
||||
dependencies_display.short_description = "وابسته به"
|
||||
|
||||
@admin.register(StepDependency)
|
||||
class StepDependencyAdmin(admin.ModelAdmin):
|
||||
list_display = ['dependent_step', 'dependency_step', 'process_display', 'created_at']
|
||||
list_filter = ['dependent_step__process', 'created_at']
|
||||
search_fields = ['dependent_step__name', 'dependency_step__name']
|
||||
ordering = ['dependent_step__process', 'dependent_step__order']
|
||||
|
||||
def process_display(self, obj):
|
||||
return obj.dependent_step.process.name
|
||||
process_display.short_description = "فرآیند"
|
||||
|
||||
@admin.register(ProcessInstance)
|
||||
class ProcessInstanceAdmin(SimpleHistoryAdmin):
|
||||
list_display = ['name', 'process', 'requester', 'current_step', 'status', 'started_at', 'progress_display']
|
||||
list_filter = ['process', 'status', 'started_at']
|
||||
search_fields = ['name', 'process__name', 'requester__username', 'requester__first_name']
|
||||
readonly_fields = ['deleted_at', 'started_at', 'completed_at']
|
||||
ordering = ['-started_at']
|
||||
|
||||
def progress_display(self, obj):
|
||||
total_steps = obj.process.steps.count()
|
||||
completed_steps = obj.step_instances.filter(status='completed').count()
|
||||
percentage = (completed_steps / total_steps * 100) if total_steps > 0 else 0
|
||||
percentage_int = int(percentage)
|
||||
return format_html(
|
||||
'<div class="progress" style="width: 100px;"><div class="progress-bar" style="width: {}%">{}/{} ({}%)</div></div>',
|
||||
percentage_int, completed_steps, total_steps, percentage_int
|
||||
)
|
||||
progress_display.short_description = "پیشرفت"
|
||||
|
||||
@admin.register(StepInstance)
|
||||
class StepInstanceAdmin(SimpleHistoryAdmin):
|
||||
list_display = ['process_instance', 'step', 'assigned_to', 'status_display', 'rejection_count', 'started_at', 'completed_at']
|
||||
list_filter = ['status', 'step__process', 'started_at']
|
||||
search_fields = ['process_instance__name', 'step__name', 'assigned_to__username']
|
||||
readonly_fields = ['started_at', 'completed_at']
|
||||
ordering = ['process_instance', 'step__order']
|
||||
|
||||
def status_display(self, obj):
|
||||
return mark_safe(obj.get_status_display_with_color())
|
||||
status_display.short_description = "وضعیت"
|
||||
|
||||
def rejection_count(self, obj):
|
||||
count = obj.get_rejection_count()
|
||||
if count > 0:
|
||||
return format_html('<span class="badge bg-danger">{}</span>', count)
|
||||
return format_html('<span class="badge bg-success">0</span>')
|
||||
rejection_count.short_description = "تعداد رد شدنها"
|
||||
|
||||
@admin.register(StepRejection)
|
||||
class StepRejectionAdmin(SimpleHistoryAdmin):
|
||||
list_display = ['step_instance', 'rejected_by', 'reason_short', 'created_at']
|
||||
list_filter = ['rejected_by', 'created_at', 'step_instance__step__process']
|
||||
search_fields = ['step_instance__step__name', 'rejected_by__username', 'reason']
|
||||
readonly_fields = ['created_at']
|
||||
ordering = ['-created_at']
|
||||
|
||||
def reason_short(self, obj):
|
||||
return obj.reason[:50] + "..." if len(obj.reason) > 50 else obj.reason
|
||||
reason_short.short_description = "دلیل رد شدن"
|
||||
|
||||
@admin.register(StepRevision)
|
||||
class StepRevisionAdmin(SimpleHistoryAdmin):
|
||||
list_display = ['step_instance', 'rejection', 'revised_by', 'changes_short', 'created_at']
|
||||
list_filter = ['revised_by', 'created_at', 'step_instance__step__process']
|
||||
search_fields = ['step_instance__step__name', 'revised_by__username', 'changes_description']
|
||||
readonly_fields = ['created_at']
|
||||
ordering = ['-created_at']
|
||||
|
||||
def changes_short(self, obj):
|
||||
return obj.changes_description[:50] + "..." if len(obj.changes_description) > 50 else obj.changes_description
|
||||
changes_short.short_description = "تغییرات"
|
7
processes/apps.py
Normal file
7
processes/apps.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ProcessesConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'processes'
|
||||
verbose_name = 'فرآیندها'
|
19
processes/forms.py
Normal file
19
processes/forms.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
from django import forms
|
||||
from .models import ProcessInstance, StepInstance
|
||||
|
||||
class ProcessInstanceForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = ProcessInstance
|
||||
fields = ['name']
|
||||
widgets = {
|
||||
'name': forms.TextInput(attrs={'class': 'form-control'})
|
||||
}
|
||||
|
||||
class StepInstanceForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = StepInstance
|
||||
fields = ['status', 'notes']
|
||||
widgets = {
|
||||
'status': forms.Select(attrs={'class': 'form-control'}),
|
||||
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3})
|
||||
}
|
314
processes/migrations/0001_initial.py
Normal file
314
processes/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,314 @@
|
|||
# Generated by Django 5.2.4 on 2025-08-07 09:08
|
||||
|
||||
import django.db.models.deletion
|
||||
import simple_history.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HistoricalProcess',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('created', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ ایجاد')),
|
||||
('updated', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ بروزرسانی')),
|
||||
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||
('slug', models.SlugField(max_length=100, verbose_name='اسلاگ')),
|
||||
('name', models.CharField(max_length=100, verbose_name='نام')),
|
||||
('description', models.TextField(blank=True, verbose_name='توضیحات')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='فعال')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('created_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='ایجاد کننده')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical فرآیند',
|
||||
'verbose_name_plural': 'historical فرآیندها',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Process',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')),
|
||||
('updated', models.DateTimeField(auto_now=True, verbose_name='تاریخ بروزرسانی')),
|
||||
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||
('slug', models.SlugField(max_length=100, unique=True, verbose_name='اسلاگ')),
|
||||
('name', models.CharField(max_length=100, verbose_name='نام')),
|
||||
('description', models.TextField(blank=True, verbose_name='توضیحات')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='فعال')),
|
||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='ایجاد کننده')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'فرآیند',
|
||||
'verbose_name_plural': 'فرآیندها',
|
||||
'ordering': ['-created'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalProcessStep',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('created', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ ایجاد')),
|
||||
('updated', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ بروزرسانی')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='فعال')),
|
||||
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||
('slug', models.SlugField(max_length=100, verbose_name='اسلاگ')),
|
||||
('name', models.CharField(max_length=100, verbose_name='نام')),
|
||||
('order', models.PositiveIntegerField(verbose_name='ترتیب')),
|
||||
('description', models.TextField(blank=True, verbose_name='توضیحات')),
|
||||
('is_required', models.BooleanField(default=True, verbose_name='اجباری')),
|
||||
('estimated_duration', models.PositiveIntegerField(blank=True, null=True, verbose_name='مدت زمان تخمینی (روز)')),
|
||||
('blocks_previous', models.BooleanField(default=False, help_text='اگر فعال باشد، پس از تکمیل این مرحله، مراحل قبلی غیرقابل ویرایش می\u200cشوند', verbose_name='مسدود کننده مراحل قبلی')),
|
||||
('can_go_back', models.BooleanField(default=True, help_text='آیا می\u200cتوان به مراحل قبلی بازگشت', verbose_name='قابل بازگشت')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('process', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='processes.process', verbose_name='فرآیند')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical مرحله فرآیند',
|
||||
'verbose_name_plural': 'historical مراحل فرآیند',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ProcessStep',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')),
|
||||
('updated', models.DateTimeField(auto_now=True, verbose_name='تاریخ بروزرسانی')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='فعال')),
|
||||
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||
('slug', models.SlugField(max_length=100, unique=True, verbose_name='اسلاگ')),
|
||||
('name', models.CharField(max_length=100, verbose_name='نام')),
|
||||
('order', models.PositiveIntegerField(verbose_name='ترتیب')),
|
||||
('description', models.TextField(blank=True, verbose_name='توضیحات')),
|
||||
('is_required', models.BooleanField(default=True, verbose_name='اجباری')),
|
||||
('estimated_duration', models.PositiveIntegerField(blank=True, null=True, verbose_name='مدت زمان تخمینی (روز)')),
|
||||
('blocks_previous', models.BooleanField(default=False, help_text='اگر فعال باشد، پس از تکمیل این مرحله، مراحل قبلی غیرقابل ویرایش می\u200cشوند', verbose_name='مسدود کننده مراحل قبلی')),
|
||||
('can_go_back', models.BooleanField(default=True, help_text='آیا می\u200cتوان به مراحل قبلی بازگشت', verbose_name='قابل بازگشت')),
|
||||
('process', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='steps', to='processes.process', verbose_name='فرآیند')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'مرحله فرآیند',
|
||||
'verbose_name_plural': 'مراحل فرآیند',
|
||||
'ordering': ['process', 'order'],
|
||||
'unique_together': {('process', 'order')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ProcessInstance',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')),
|
||||
('updated', models.DateTimeField(auto_now=True, verbose_name='تاریخ بروزرسانی')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='فعال')),
|
||||
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||
('slug', models.SlugField(max_length=100, unique=True, verbose_name='اسلاگ')),
|
||||
('name', models.CharField(max_length=100, verbose_name='نام')),
|
||||
('status', models.CharField(choices=[('pending', 'در انتظار'), ('in_progress', 'در حال انجام'), ('completed', 'تکمیل شده'), ('cancelled', 'لغو شده'), ('rejected', 'رد شده')], default='pending', max_length=20, verbose_name='وضعیت')),
|
||||
('started_at', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ شروع')),
|
||||
('completed_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ تکمیل')),
|
||||
('process', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='processes.process', verbose_name='فرآیند')),
|
||||
('requester', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='درخواست کننده')),
|
||||
('current_step', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='processes.processstep', verbose_name='مرحله فعلی')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'نمونه فرآیند',
|
||||
'verbose_name_plural': 'نمونه\u200cهای فرآیند',
|
||||
'ordering': ['-started_at'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalStepInstance',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('status', models.CharField(choices=[('pending', 'در انتظار'), ('in_progress', 'در حال انجام'), ('completed', 'تکمیل شده'), ('skipped', 'رد شده'), ('blocked', 'مسدود شده'), ('rejected', 'رد شده و نیاز به اصلاح')], default='pending', max_length=20, verbose_name='وضعیت')),
|
||||
('notes', models.TextField(blank=True, verbose_name='یادداشت\u200cها')),
|
||||
('started_at', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ شروع')),
|
||||
('completed_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ تکمیل')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('assigned_to', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='واگذار شده به')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('process_instance', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='processes.processinstance', verbose_name='نمونه فرآیند')),
|
||||
('step', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='processes.processstep', verbose_name='مرحله')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical نمونه مرحله',
|
||||
'verbose_name_plural': 'historical نمونه\u200cهای مرحله',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalProcessInstance',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('created', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ ایجاد')),
|
||||
('updated', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ بروزرسانی')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='فعال')),
|
||||
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||
('slug', models.SlugField(max_length=100, verbose_name='اسلاگ')),
|
||||
('name', models.CharField(max_length=100, verbose_name='نام')),
|
||||
('status', models.CharField(choices=[('pending', 'در انتظار'), ('in_progress', 'در حال انجام'), ('completed', 'تکمیل شده'), ('cancelled', 'لغو شده'), ('rejected', 'رد شده')], default='pending', max_length=20, verbose_name='وضعیت')),
|
||||
('started_at', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ شروع')),
|
||||
('completed_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ تکمیل')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('requester', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='درخواست کننده')),
|
||||
('process', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='processes.process', verbose_name='فرآیند')),
|
||||
('current_step', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='processes.processstep', verbose_name='مرحله فعلی')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical نمونه فرآیند',
|
||||
'verbose_name_plural': 'historical نمونه\u200cهای فرآیند',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StepInstance',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('status', models.CharField(choices=[('pending', 'در انتظار'), ('in_progress', 'در حال انجام'), ('completed', 'تکمیل شده'), ('skipped', 'رد شده'), ('blocked', 'مسدود شده'), ('rejected', 'رد شده و نیاز به اصلاح')], default='pending', max_length=20, verbose_name='وضعیت')),
|
||||
('notes', models.TextField(blank=True, verbose_name='یادداشت\u200cها')),
|
||||
('started_at', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ شروع')),
|
||||
('completed_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ تکمیل')),
|
||||
('assigned_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='واگذار شده به')),
|
||||
('process_instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='step_instances', to='processes.processinstance', verbose_name='نمونه فرآیند')),
|
||||
('step', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='processes.processstep', verbose_name='مرحله')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'نمونه مرحله',
|
||||
'verbose_name_plural': 'نمونه\u200cهای مرحله',
|
||||
'ordering': ['process_instance', 'step__order'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalStepRejection',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('reason', models.TextField(help_text='توضیح کامل دلیل رد شدن', verbose_name='دلیل رد شدن')),
|
||||
('instructions', models.TextField(blank=True, help_text='دستورالعمل\u200cهایی برای اصلاح مرحله', verbose_name='دستورالعمل\u200cهای اصلاح')),
|
||||
('created_at', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ رد شدن')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('rejected_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='رد کننده')),
|
||||
('step_instance', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='processes.stepinstance', verbose_name='نمونه مرحله')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical رد شدن مرحله',
|
||||
'verbose_name_plural': 'historical رد شدن\u200cهای مراحل',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StepRejection',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('reason', models.TextField(help_text='توضیح کامل دلیل رد شدن', verbose_name='دلیل رد شدن')),
|
||||
('instructions', models.TextField(blank=True, help_text='دستورالعمل\u200cهایی برای اصلاح مرحله', verbose_name='دستورالعمل\u200cهای اصلاح')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ رد شدن')),
|
||||
('rejected_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='step_rejections', to=settings.AUTH_USER_MODEL, verbose_name='رد کننده')),
|
||||
('step_instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rejections', to='processes.stepinstance', verbose_name='نمونه مرحله')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'رد شدن مرحله',
|
||||
'verbose_name_plural': 'رد شدن\u200cهای مراحل',
|
||||
'ordering': ['-created_at'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HistoricalStepRevision',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('changes_description', models.TextField(help_text='توضیح تغییراتی که برای اصلاح انجام شده', verbose_name='توضیح تغییرات')),
|
||||
('created_at', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ اصلاح')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('revised_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='اصلاح کننده')),
|
||||
('step_instance', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='processes.stepinstance', verbose_name='نمونه مرحله')),
|
||||
('rejection', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='processes.steprejection', verbose_name='رد شدن مربوطه')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical بازبینی مرحله',
|
||||
'verbose_name_plural': 'historical بازبینی\u200cهای مراحل',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StepRevision',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('changes_description', models.TextField(help_text='توضیح تغییراتی که برای اصلاح انجام شده', verbose_name='توضیح تغییرات')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ اصلاح')),
|
||||
('rejection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='revisions', to='processes.steprejection', verbose_name='رد شدن مربوطه')),
|
||||
('revised_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='step_revisions', to=settings.AUTH_USER_MODEL, verbose_name='اصلاح کننده')),
|
||||
('step_instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='revisions', to='processes.stepinstance', verbose_name='نمونه مرحله')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'بازبینی مرحله',
|
||||
'verbose_name_plural': 'بازبینی\u200cهای مراحل',
|
||||
'ordering': ['-created_at'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StepDependency',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')),
|
||||
('dependency_step', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dependents', to='processes.processstep', verbose_name='مرحله مورد نیاز')),
|
||||
('dependent_step', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dependencies', to='processes.processstep', verbose_name='مرحله وابسته')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'وابستگی مرحله',
|
||||
'verbose_name_plural': 'وابستگی\u200cهای مراحل',
|
||||
'ordering': ['dependent_step__order', 'dependency_step__order'],
|
||||
'unique_together': {('dependent_step', 'dependency_step')},
|
||||
},
|
||||
),
|
||||
]
|
0
processes/migrations/__init__.py
Normal file
0
processes/migrations/__init__.py
Normal file
293
processes/models.py
Normal file
293
processes/models.py
Normal file
|
@ -0,0 +1,293 @@
|
|||
from django.db import models
|
||||
from django.contrib.auth import get_user_model
|
||||
from common.models import NameSlugModel
|
||||
from simple_history.models import HistoricalRecords
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
class Process(NameSlugModel):
|
||||
"""مدل فرآیند اصلی"""
|
||||
description = models.TextField(verbose_name="توضیحات", blank=True)
|
||||
is_active = models.BooleanField(default=True, verbose_name="فعال")
|
||||
created_by = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="ایجاد کننده")
|
||||
history = HistoricalRecords()
|
||||
|
||||
class Meta:
|
||||
verbose_name = "فرآیند"
|
||||
verbose_name_plural = "فرآیندها"
|
||||
ordering = ['-created']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class ProcessStep(NameSlugModel):
|
||||
"""مدل مراحل فرآیند"""
|
||||
process = models.ForeignKey(Process, on_delete=models.CASCADE, related_name='steps', verbose_name="فرآیند")
|
||||
order = models.PositiveIntegerField(verbose_name="ترتیب")
|
||||
description = models.TextField(verbose_name="توضیحات", blank=True)
|
||||
is_required = models.BooleanField(default=True, verbose_name="اجباری")
|
||||
estimated_duration = models.PositiveIntegerField(verbose_name="مدت زمان تخمینی (روز)", null=True, blank=True)
|
||||
|
||||
# فیلدهای جدید برای کنترل وابستگیها
|
||||
blocks_previous = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name="مسدود کننده مراحل قبلی",
|
||||
help_text="اگر فعال باشد، پس از تکمیل این مرحله، مراحل قبلی غیرقابل ویرایش میشوند"
|
||||
)
|
||||
can_go_back = models.BooleanField(
|
||||
default=True,
|
||||
verbose_name="قابل بازگشت",
|
||||
help_text="آیا میتوان به مراحل قبلی بازگشت"
|
||||
)
|
||||
history = HistoricalRecords()
|
||||
|
||||
class Meta:
|
||||
verbose_name = "مرحله فرآیند"
|
||||
verbose_name_plural = "مراحل فرآیند"
|
||||
ordering = ['process', 'order']
|
||||
unique_together = ['process', 'order']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.process.name} - {self.name}"
|
||||
|
||||
def get_dependencies(self):
|
||||
"""دریافت مراحل وابسته"""
|
||||
return StepDependency.objects.filter(dependent_step=self).values_list('dependency_step', flat=True)
|
||||
|
||||
def get_dependents(self):
|
||||
"""دریافت مراحلی که به این مرحله وابسته هستند"""
|
||||
return StepDependency.objects.filter(dependency_step=self).values_list('dependent_step', flat=True)
|
||||
|
||||
class StepDependency(models.Model):
|
||||
"""مدل وابستگی بین مراحل"""
|
||||
dependent_step = models.ForeignKey(
|
||||
ProcessStep,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='dependencies',
|
||||
verbose_name="مرحله وابسته"
|
||||
)
|
||||
dependency_step = models.ForeignKey(
|
||||
ProcessStep,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='dependents',
|
||||
verbose_name="مرحله مورد نیاز"
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ ایجاد")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "وابستگی مرحله"
|
||||
verbose_name_plural = "وابستگیهای مراحل"
|
||||
unique_together = ['dependent_step', 'dependency_step']
|
||||
ordering = ['dependent_step__order', 'dependency_step__order']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.dependent_step} ← {self.dependency_step}"
|
||||
|
||||
def clean(self):
|
||||
"""اعتبارسنجی مدل"""
|
||||
if self.dependent_step == self.dependency_step:
|
||||
raise ValidationError("مرحله نمیتواند به خودش وابسته باشد")
|
||||
|
||||
if self.dependent_step.process != self.dependency_step.process:
|
||||
raise ValidationError("مراحل باید از یک فرآیند باشند")
|
||||
|
||||
if self.dependent_step.order <= self.dependency_step.order:
|
||||
raise ValidationError("مرحله وابسته باید بعد از مرحله مورد نیاز باشد")
|
||||
|
||||
class ProcessInstance(NameSlugModel):
|
||||
"""مدل نمونه فرآیند (برای هر درخواست)"""
|
||||
process = models.ForeignKey(Process, on_delete=models.CASCADE, related_name='instances', verbose_name="فرآیند")
|
||||
requester = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="درخواست کننده")
|
||||
current_step = models.ForeignKey('ProcessStep', on_delete=models.SET_NULL, null=True, blank=True, verbose_name="مرحله فعلی")
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('pending', 'در انتظار'),
|
||||
('in_progress', 'در حال انجام'),
|
||||
('completed', 'تکمیل شده'),
|
||||
('cancelled', 'لغو شده'),
|
||||
('rejected', 'رد شده'),
|
||||
],
|
||||
default='pending',
|
||||
verbose_name="وضعیت"
|
||||
)
|
||||
started_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ شروع")
|
||||
completed_at = models.DateTimeField(null=True, blank=True, verbose_name="تاریخ تکمیل")
|
||||
history = HistoricalRecords()
|
||||
|
||||
class Meta:
|
||||
verbose_name = "نمونه فرآیند"
|
||||
verbose_name_plural = "نمونههای فرآیند"
|
||||
ordering = ['-started_at']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.process.name} - {self.requester.get_full_name()}"
|
||||
|
||||
def get_available_steps(self):
|
||||
"""دریافت مراحل قابل دسترس"""
|
||||
available_steps = []
|
||||
for step in self.process.steps.all():
|
||||
if self.can_access_step(step):
|
||||
available_steps.append(step)
|
||||
return available_steps
|
||||
|
||||
def can_access_step(self, step):
|
||||
"""بررسی امکان دسترسی به مرحله"""
|
||||
# بررسی وابستگیها
|
||||
dependencies = step.get_dependencies()
|
||||
for dependency_id in dependencies:
|
||||
step_instance = self.step_instances.filter(step_id=dependency_id).first()
|
||||
if not step_instance or step_instance.status != 'completed':
|
||||
return False
|
||||
return True
|
||||
|
||||
def can_edit_step(self, step):
|
||||
"""بررسی امکان ویرایش مرحله"""
|
||||
# اگر مرحله مسدود کننده باشد و مراحل بعدی تکمیل شده باشند
|
||||
if step.blocks_previous:
|
||||
later_steps = self.step_instances.filter(
|
||||
step__order__gt=step.order,
|
||||
status='completed'
|
||||
)
|
||||
if later_steps.exists():
|
||||
return False
|
||||
return True
|
||||
|
||||
class StepInstance(models.Model):
|
||||
"""مدل نمونه مرحله (برای هر مرحله در هر درخواست)"""
|
||||
process_instance = models.ForeignKey(ProcessInstance, on_delete=models.CASCADE, related_name='step_instances', verbose_name="نمونه فرآیند")
|
||||
step = models.ForeignKey(ProcessStep, on_delete=models.CASCADE, verbose_name="مرحله")
|
||||
assigned_to = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="واگذار شده به")
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('pending', 'در انتظار'),
|
||||
('in_progress', 'در حال انجام'),
|
||||
('completed', 'تکمیل شده'),
|
||||
('skipped', 'رد شده'),
|
||||
('blocked', 'مسدود شده'),
|
||||
('rejected', 'رد شده و نیاز به اصلاح'),
|
||||
],
|
||||
default='pending',
|
||||
verbose_name="وضعیت"
|
||||
)
|
||||
notes = models.TextField(verbose_name="یادداشتها", blank=True)
|
||||
started_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ شروع")
|
||||
completed_at = models.DateTimeField(null=True, blank=True, verbose_name="تاریخ تکمیل")
|
||||
history = HistoricalRecords()
|
||||
|
||||
class Meta:
|
||||
verbose_name = "نمونه مرحله"
|
||||
verbose_name_plural = "نمونههای مرحله"
|
||||
ordering = ['process_instance', 'step__order']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.process_instance} - {self.step.name}"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""ذخیره با اعتبارسنجی"""
|
||||
# بررسی وابستگیها
|
||||
if self.status == 'in_progress' or self.status == 'completed':
|
||||
if not self.process_instance.can_access_step(self.step):
|
||||
raise ValidationError("مراحل وابسته تکمیل نشدهاند")
|
||||
|
||||
# بررسی امکان ویرایش
|
||||
if self.status == 'completed' and not self.process_instance.can_edit_step(self.step):
|
||||
raise ValidationError("این مرحله قابل ویرایش نیست")
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def get_status_display_with_color(self):
|
||||
"""نمایش وضعیت با رنگ"""
|
||||
status_colors = {
|
||||
'pending': 'secondary',
|
||||
'in_progress': 'primary',
|
||||
'completed': 'success',
|
||||
'skipped': 'warning',
|
||||
'blocked': 'danger',
|
||||
'rejected': 'danger',
|
||||
}
|
||||
color = status_colors.get(self.status, 'secondary')
|
||||
return '<span class="badge bg-{}">{}</span>'.format(color, self.get_status_display())
|
||||
|
||||
def get_rejection_count(self):
|
||||
"""دریافت تعداد رد شدنها"""
|
||||
return self.rejections.count()
|
||||
|
||||
def get_latest_rejection(self):
|
||||
"""دریافت آخرین رد شدن"""
|
||||
return self.rejections.order_by('-created_at').first()
|
||||
|
||||
class StepRejection(models.Model):
|
||||
"""مدل رد شدن مرحله"""
|
||||
step_instance = models.ForeignKey(
|
||||
StepInstance,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='rejections',
|
||||
verbose_name="نمونه مرحله"
|
||||
)
|
||||
rejected_by = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name="رد کننده",
|
||||
related_name='step_rejections'
|
||||
)
|
||||
reason = models.TextField(verbose_name="دلیل رد شدن", help_text="توضیح کامل دلیل رد شدن")
|
||||
instructions = models.TextField(
|
||||
verbose_name="دستورالعملهای اصلاح",
|
||||
help_text="دستورالعملهایی برای اصلاح مرحله",
|
||||
blank=True
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ رد شدن")
|
||||
history = HistoricalRecords()
|
||||
|
||||
class Meta:
|
||||
verbose_name = "رد شدن مرحله"
|
||||
verbose_name_plural = "رد شدنهای مراحل"
|
||||
ordering = ['-created_at']
|
||||
|
||||
def __str__(self):
|
||||
return f"رد شدن {self.step_instance} توسط {self.rejected_by.get_full_name()}"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""ذخیره با تغییر وضعیت مرحله"""
|
||||
# تغییر وضعیت مرحله به رد شده
|
||||
self.step_instance.status = 'rejected'
|
||||
self.step_instance.save()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
class StepRevision(models.Model):
|
||||
"""مدل بازبینی و اصلاح مرحله"""
|
||||
step_instance = models.ForeignKey(
|
||||
StepInstance,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='revisions',
|
||||
verbose_name="نمونه مرحله"
|
||||
)
|
||||
rejection = models.ForeignKey(
|
||||
StepRejection,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='revisions',
|
||||
verbose_name="رد شدن مربوطه"
|
||||
)
|
||||
revised_by = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name="اصلاح کننده",
|
||||
related_name='step_revisions'
|
||||
)
|
||||
changes_description = models.TextField(
|
||||
verbose_name="توضیح تغییرات",
|
||||
help_text="توضیح تغییراتی که برای اصلاح انجام شده"
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ اصلاح")
|
||||
history = HistoricalRecords()
|
||||
|
||||
class Meta:
|
||||
verbose_name = "بازبینی مرحله"
|
||||
verbose_name_plural = "بازبینیهای مراحل"
|
||||
ordering = ['-created_at']
|
||||
|
||||
def __str__(self):
|
||||
return f"بازبینی {self.step_instance} توسط {self.revised_by.get_full_name()}"
|
3
processes/tests.py
Normal file
3
processes/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
12
processes/urls.py
Normal file
12
processes/urls.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
app_name = 'processes'
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.process_list, name='process_list'),
|
||||
path('<int:process_id>/', views.process_detail, name='process_detail'),
|
||||
path('<int:process_id>/start/', views.start_process, name='start_process'),
|
||||
path('instance/<int:instance_id>/', views.instance_detail, name='instance_detail'),
|
||||
path('my-processes/', views.my_processes, name='my_processes'),
|
||||
]
|
79
processes/views.py
Normal file
79
processes/views.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
from django.http import JsonResponse
|
||||
from django.views.decorators.http import require_POST
|
||||
from .models import Process, ProcessInstance, StepInstance
|
||||
from .forms import ProcessInstanceForm
|
||||
|
||||
@login_required
|
||||
def process_list(request):
|
||||
"""نمایش لیست فرآیندهای فعال"""
|
||||
processes = Process.objects.filter(is_active=True)
|
||||
return render(request, 'processes/process_list.html', {
|
||||
'processes': processes
|
||||
})
|
||||
|
||||
@login_required
|
||||
def process_detail(request, process_id):
|
||||
"""نمایش جزئیات فرآیند"""
|
||||
process = get_object_or_404(Process, id=process_id, is_active=True)
|
||||
return render(request, 'processes/process_detail.html', {
|
||||
'process': process
|
||||
})
|
||||
|
||||
@login_required
|
||||
def start_process(request, process_id):
|
||||
"""شروع فرآیند جدید"""
|
||||
process = get_object_or_404(Process, id=process_id, is_active=True)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = ProcessInstanceForm(request.POST)
|
||||
if form.is_valid():
|
||||
instance = form.save(commit=False)
|
||||
instance.process = process
|
||||
instance.requester = request.user
|
||||
instance.save()
|
||||
|
||||
# ایجاد نمونههای مرحله
|
||||
for step in process.steps.all():
|
||||
StepInstance.objects.create(
|
||||
process_instance=instance,
|
||||
step=step
|
||||
)
|
||||
|
||||
# تنظیم مرحله اول به عنوان مرحله فعلی
|
||||
first_step = process.steps.first()
|
||||
if first_step:
|
||||
instance.current_step = first_step
|
||||
instance.status = 'in_progress'
|
||||
instance.save()
|
||||
|
||||
messages.success(request, f'فرآیند {process.name} با موفقیت شروع شد.')
|
||||
return redirect('processes:instance_detail', instance_id=instance.id)
|
||||
else:
|
||||
form = ProcessInstanceForm()
|
||||
|
||||
return render(request, 'processes/start_process.html', {
|
||||
'process': process,
|
||||
'form': form
|
||||
})
|
||||
|
||||
@login_required
|
||||
def instance_detail(request, instance_id):
|
||||
"""نمایش جزئیات نمونه فرآیند"""
|
||||
instance = get_object_or_404(ProcessInstance, id=instance_id)
|
||||
return render(request, 'processes/instance_detail.html', {
|
||||
'instance': instance
|
||||
})
|
||||
|
||||
@login_required
|
||||
def my_processes(request):
|
||||
"""نمایش فرآیندهای کاربر"""
|
||||
my_instances = ProcessInstance.objects.filter(requester=request.user)
|
||||
assigned_steps = StepInstance.objects.filter(assigned_to=request.user, status='in_progress')
|
||||
|
||||
return render(request, 'processes/my_processes.html', {
|
||||
'my_instances': my_instances,
|
||||
'assigned_steps': assigned_steps
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue