👥 Добавлено управление персоналом и карьерой

 Новые функции:
- 🧑‍💻 Team модель для управления сотрудниками
  • Полная информация о персонале (имя, должность, отдел)
  • Фотографии и контактные данные
  • Социальные сети (LinkedIn, GitHub, Telegram)
  • Навыки и опыт работы
  • Гибкие настройки отображения

- 💼 Career модель для вакансий
  • Детальное описание позиций
  • Требования и обязанности
  • Зарплатные вилки
  • Типы занятости и уровни опыта
  • Статусы вакансий и дедлайны

🔧 Админ-панель:
- Удобные интерфейсы для HR-менеджмента
- Группировка полей и фильтрация
- Быстрые действия для массовых операций
- Сортировка по приоритету

📊 База данных:
- Миграция 0013_career_team.py
- Оптимизированные индексы и связи
This commit is contained in:
2025-11-25 15:44:57 +09:00
parent c1616ac542
commit ec01a2ae10
3 changed files with 340 additions and 2 deletions

View File

@@ -212,3 +212,174 @@ class Review(models.Model):
def __str__(self):
return f"Отзыв от {self.client.first_name} {self.client.last_name} for {self.service.name}"
class Team(models.Model):
"""Модель для управления персоналом компании"""
first_name = models.CharField(max_length=100, verbose_name="Имя")
last_name = models.CharField(max_length=100, verbose_name="Фамилия")
position = models.CharField(max_length=200, verbose_name="Должность")
department = models.CharField(max_length=100, verbose_name="Отдел", blank=True)
bio = models.TextField(verbose_name="Биография/Описание", blank=True)
photo = models.ImageField(upload_to='static/img/team/', blank=True, null=True, verbose_name="Фотография")
email = models.EmailField(blank=True, verbose_name="Email")
phone = models.CharField(max_length=20, blank=True, verbose_name="Телефон")
# Социальные сети
linkedin = models.URLField(blank=True, verbose_name="LinkedIn")
github = models.URLField(blank=True, verbose_name="GitHub")
telegram = models.CharField(max_length=100, blank=True, verbose_name="Telegram")
# Навыки и технологии
skills = models.TextField(blank=True, verbose_name="Навыки", help_text="Разделите навыки запятыми")
experience_years = models.PositiveIntegerField(default=0, verbose_name="Лет опыта")
# Настройки отображения
is_active = models.BooleanField(default=True, verbose_name="Активен")
display_order = models.PositiveIntegerField(default=0, verbose_name="Порядок отображения")
show_on_about = models.BooleanField(default=True, verbose_name="Показывать на странице О нас")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'Сотрудник'
verbose_name_plural = 'Команда'
ordering = ['display_order', 'last_name', 'first_name']
def __str__(self):
return f"{self.first_name} {self.last_name} - {self.position}"
@property
def full_name(self):
return f"{self.first_name} {self.last_name}"
@property
def skills_list(self):
"""Возвращает список навыков"""
if self.skills:
return [skill.strip() for skill in self.skills.split(',') if skill.strip()]
return []
class Career(models.Model):
"""Модель для управления вакансиями и карьерными возможностями"""
EMPLOYMENT_TYPE_CHOICES = [
('full_time', 'Полная занятость'),
('part_time', 'Частичная занятость'),
('contract', 'Контракт'),
('internship', 'Стажировка'),
('remote', 'Удаленная работа'),
('freelance', 'Фриланс'),
]
EXPERIENCE_LEVEL_CHOICES = [
('junior', 'Junior (0-1 год)'),
('middle', 'Middle (2-4 года)'),
('senior', 'Senior (5+ лет)'),
('lead', 'Team Lead'),
('intern', 'Стажер'),
]
STATUS_CHOICES = [
('active', 'Активная'),
('paused', 'Приостановлена'),
('closed', 'Закрыта'),
('draft', 'Черновик'),
]
title = models.CharField(max_length=200, verbose_name="Название вакансии")
department = models.CharField(max_length=100, verbose_name="Отдел")
location = models.CharField(max_length=200, default="Кванджу, Южная Корея", verbose_name="Местоположение")
employment_type = models.CharField(
max_length=20,
choices=EMPLOYMENT_TYPE_CHOICES,
default='full_time',
verbose_name="Тип занятости"
)
experience_level = models.CharField(
max_length=20,
choices=EXPERIENCE_LEVEL_CHOICES,
default='middle',
verbose_name="Уровень опыта"
)
# Описание вакансии
description = models.TextField(verbose_name="Описание вакансии")
responsibilities = models.TextField(verbose_name="Обязанности")
requirements = models.TextField(verbose_name="Требования")
benefits = models.TextField(blank=True, verbose_name="Преимущества и условия")
# Зарплатная вилка
salary_min = models.PositiveIntegerField(blank=True, null=True, verbose_name="Зарплата от (₩)")
salary_max = models.PositiveIntegerField(blank=True, null=True, verbose_name="Зарплата до (₩)")
salary_currency = models.CharField(max_length=10, default="KRW", verbose_name="Валюта")
# Необходимые навыки
required_skills = models.TextField(verbose_name="Обязательные навыки", help_text="Разделите навыки запятыми")
preferred_skills = models.TextField(blank=True, verbose_name="Желательные навыки", help_text="Разделите навыки запятыми")
# Статус и метаданные
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='active',
verbose_name="Статус"
)
is_featured = models.BooleanField(default=False, verbose_name="Рекомендуемая вакансия")
application_deadline = models.DateField(blank=True, null=True, verbose_name="Дедлайн подачи заявок")
# Контактная информация
contact_email = models.EmailField(default="hr@smartsoltech.kr", verbose_name="Email для связи")
contact_person = models.CharField(max_length=200, blank=True, verbose_name="Контактное лицо")
# Timestamps
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
published_at = models.DateTimeField(blank=True, null=True, verbose_name="Дата публикации")
class Meta:
verbose_name = 'Вакансия'
verbose_name_plural = 'Карьера'
ordering = ['-is_featured', '-published_at', '-created_at']
def __str__(self):
return f"{self.title} ({self.get_experience_level_display()})"
@property
def required_skills_list(self):
"""Возвращает список обязательных навыков"""
if self.required_skills:
return [skill.strip() for skill in self.required_skills.split(',') if skill.strip()]
return []
@property
def preferred_skills_list(self):
"""Возвращает список желательных навыков"""
if self.preferred_skills:
return [skill.strip() for skill in self.preferred_skills.split(',') if skill.strip()]
return []
@property
def salary_range(self):
"""Возвращает строку с зарплатной вилкой"""
if self.salary_min and self.salary_max:
return f"{self.salary_min:,} - ₩{self.salary_max:,}"
elif self.salary_min:
return f"от ₩{self.salary_min:,}"
elif self.salary_max:
return f"до ₩{self.salary_max:,}"
return "По договоренности"
def is_active_position(self):
"""Проверяет, активна ли вакансия"""
from django.utils import timezone
if self.status != 'active':
return False
if self.application_deadline and self.application_deadline < timezone.now().date():
return False
return True