hiring: вакансии и найм, HR-портал

Модель вакансий, флоу рекрутинга и панели HR-портала (заявки/вакансии).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-09 18:03:32 +03:00
parent 3359a8db13
commit ac0418083b
12 changed files with 1317 additions and 286 deletions
+82
View File
@@ -186,3 +186,85 @@ class HiringRequest(BaseModel):
except Exception:
return
account.release()
class Vacancy(BaseModel):
"""Открытая вакансия (рекрутинг в HR-портале)."""
STATUS_CHOICES = [
('open', _('Открыта')),
('closed', _('Закрыта')),
('filled', _('Закрыта наймом')),
]
PRIORITY_CHOICES = [
('urgent', _('Срочно')),
('medium', _('Средний')),
('normal', _('Обычный')),
]
LOCATION_TYPES = [
('pharmacy', _('Аптека')),
('warehouse', _('Склад')),
('office', _('Офис')),
]
title = models.CharField(_('Название'), max_length=200)
post = models.ForeignKey(
'core.Directory', on_delete=models.SET_NULL,
null=True, blank=True,
limit_choices_to={'directory_type__code': 'post', 'is_active': True},
related_name='vacancies', verbose_name=_('Должность'),
)
user_location_type = models.CharField(
_('Тип локации'), max_length=20, choices=LOCATION_TYPES, default='pharmacy',
)
department = models.ForeignKey(
'core.Directory', on_delete=models.SET_NULL,
null=True, blank=True,
limit_choices_to={'directory_type__code': 'department', 'is_active': True},
related_name='vacancies_department', verbose_name=_('Отдел'),
)
pharmacy = models.ForeignKey(
'locations.Pharmacy', on_delete=models.SET_NULL,
null=True, blank=True,
related_name='pharmacy_vacancies', verbose_name=_('Аптека'),
)
location = models.ForeignKey(
'locations.Location', on_delete=models.SET_NULL,
null=True, blank=True,
related_name='location_vacancies', verbose_name=_('Локация'),
)
priority = models.CharField(
_('Приоритет'), max_length=16, choices=PRIORITY_CHOICES, default='normal',
)
status = models.CharField(
_('Статус'), max_length=16, choices=STATUS_CHOICES, default='open',
)
opened_at = models.DateField(_('Открыта с'), default=timezone.localdate)
description = models.TextField(_('Описание'), blank=True)
candidates_count = models.PositiveIntegerField(_('Кандидатов'), default=0)
created_by = models.ForeignKey(
'employees.Employee', on_delete=models.SET_NULL,
null=True, blank=True,
related_name='vacancies_created', verbose_name=_('Создал'),
)
class Meta:
verbose_name = _('Вакансия')
verbose_name_plural = _('Вакансии')
ordering = ['-opened_at', '-created_at']
indexes = [
models.Index(fields=['status', 'priority']),
]
def __str__(self):
return self.title
@property
def place_label(self) -> str:
if self.user_location_type == 'pharmacy' and self.pharmacy_id:
return str(self.pharmacy)
if self.department_id:
return self.department.name
if self.location_id:
return str(self.location)
return '—'