from django.db import models from django.contrib.auth.models import AbstractUser, User import uuid from django.urls import reverse class ContactInfo(models.Model): """Модель для контактной информации компании""" company_name = models.CharField(max_length=200, default="SmartSolTech", verbose_name="Название компании") email = models.EmailField(default="info@smartsoltech.kr", verbose_name="Email") phone = models.CharField(max_length=20, default="+82-10-5693-6103", verbose_name="Телефон") telegram = models.CharField(max_length=100, default="@smartsoltech", verbose_name="Telegram") address = models.TextField(default="Чолланамдо, Кванджу", verbose_name="Адрес") working_hours = models.CharField(max_length=100, default="Пн-Пт 9:00-18:00", verbose_name="Часы работы") description = models.TextField(default="Мы - команда профессионалов в сфере IT-решений", verbose_name="Описание") call_to_action = models.CharField(max_length=200, default="Начнем сотрудничество?", verbose_name="Призыв к действию") subtitle = models.CharField(max_length=200, default="Свяжитесь с нами для обсуждения вашего проекта", verbose_name="Подзаголовок") is_active = models.BooleanField(default=True, verbose_name="Активно") class Meta: verbose_name = 'Контактная информация' verbose_name_plural = 'Контактная информация' def __str__(self): return f"Контакты - {self.company_name}" @classmethod def get_active(cls): """Получить активную контактную информацию""" return cls.objects.filter(is_active=True).first() or cls.objects.create() class HeroBanner(models.Model): """Модель для главного баннера на сайте""" title = models.CharField(max_length=200, verbose_name="Заголовок") subtitle = models.TextField(blank=True, verbose_name="Подзаголовок") description = models.TextField(blank=True, verbose_name="Описание") image = models.ImageField(upload_to='static/img/hero/', blank=True, null=True, verbose_name="Фоновое изображение") video = models.FileField(upload_to='static/video/hero/', blank=True, null=True, help_text='Фоновое видео для баннера (MP4, WebM)', verbose_name="Фоновое видео") video_poster = models.ImageField(upload_to='static/img/hero/posters/', blank=True, null=True, help_text='Превью изображение для видео', verbose_name="Превью видео") button_text = models.CharField(max_length=100, blank=True, verbose_name="Текст кнопки") button_link = models.URLField(blank=True, verbose_name="Ссылка кнопки") is_active = models.BooleanField(default=True, verbose_name="Активен") order = models.PositiveIntegerField(default=0, verbose_name="Порядок отображения") created_at = models.DateTimeField(auto_now_add=True) class Meta: verbose_name = 'Hero Баннер' verbose_name_plural = 'Hero Баннеры' ordering = ['order', '-created_at'] def __str__(self): return self.title class Category(models.Model): name = models.CharField(max_length=100) description = models.TextField(default='Описание категории') class Meta: verbose_name = 'Категория' verbose_name_plural = 'Категории' ordering = ['name'] def __str__(self): return self.name class Service(models.Model): name = models.CharField(max_length=200) description = models.TextField(default='Описание услуги') price = models.DecimalField(max_digits=10, decimal_places=2) category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='services') image = models.ImageField(upload_to='static/img/services/', blank=True, null=True) video = models.FileField(upload_to='static/video/services/', blank=True, null=True, help_text='Видео файл для услуги (MP4, WebM, AVI)') video_poster = models.ImageField(upload_to='static/img/services/posters/', blank=True, null=True, help_text='Превью изображение для видео') video = models.FileField(upload_to='static/video/services/', blank=True, null=True, help_text='Видео файл для услуги (MP4, WebM, AVI)') video_poster = models.ImageField(upload_to='static/img/services/posters/', blank=True, null=True, help_text='Превью изображение для видео') class Meta: verbose_name = 'Услуга' verbose_name_plural = 'Услуги' ordering = ['name'] def __str__(self): return self.name def average_rating(self): reviews = self.reviews.all() if reviews: return sum(review.rating for review in reviews) / reviews.count() return 0 def review_count(self): return self.reviews.count() class Client(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='client_profile', null=True, blank=True) first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) email = models.EmailField(unique=True) phone_number = models.CharField(max_length=15, unique=True) image = models.ImageField(upload_to='static/img/customer/', blank=True, null=True) chat_id = models.CharField(max_length=100, blank=True, null=True) # Telegram chat ID class Meta: verbose_name = 'Клиент' verbose_name_plural = 'Клиенты' ordering = ['last_name', 'first_name'] def __str__(self): return f"{self.first_name} {self.last_name} {self.chat_id}" class BlogPost(models.Model): title = models.CharField(max_length=200) content = models.TextField() published_date = models.DateTimeField(auto_now_add=True) image = models.ImageField(upload_to='static/img/blog/', blank=True, null=True) video = models.FileField(upload_to='static/video/blog/', blank=True, null=True, help_text='Видео файл для блог поста') video_poster = models.ImageField(upload_to='static/img/blog/posters/', blank=True, null=True, help_text='Превью изображение для видео') class Meta: verbose_name = 'Блог' verbose_name_plural = 'Блоги' ordering = ['-published_date'] def __str__(self): return self.title class ServiceRequest(models.Model): service = models.ForeignKey(Service, on_delete=models.CASCADE) client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='related_service_requests', null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) token = models.UUIDField(default=uuid.uuid4, unique=True) # Генерация уникального токена chat_id = models.CharField(max_length=100, blank=True, null=True) # Telegram chat ID is_verified = models.BooleanField(default=False) class Meta: verbose_name = 'Заявка на услугу' verbose_name_plural = 'Заявки на услуги' ordering = ['-is_verified', '-created_at'] def __str__(self): return f"Request for {self.service.name} by {self.client.first_name}" class Order(models.Model): service_request = models.OneToOneField(ServiceRequest, on_delete=models.CASCADE, related_name='related_order') client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='related_orders') service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name='related_orders') message = models.TextField(blank=True, null=True) order_date = models.DateTimeField(auto_now_add=True) status = models.CharField( max_length=50, choices=[ ('pending', 'Ожидание'), ('in_progress', 'В процессе'), ('completed', 'Завершен'), ('cancelled', 'Отменён') ], default='pending' ) class Meta: ordering = ['-order_date'] verbose_name = 'Заказ' verbose_name_plural = 'Заказы' def __str__(self): return f"Order #{self.id} by {self.client.first_name}" def is_completed(self): return self.status == 'completed' def get_absolute_url(self): return reverse('order_detail', kwargs={'pk': self.pk}) class Project(models.Model): name = models.CharField(max_length=200) description = models.TextField(default='Описание проекта') completion_date = models.DateField(blank=True, null=True) client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='projects') service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name='projects') order = models.OneToOneField(Order, on_delete=models.CASCADE, related_name='project', null=True, blank=True) category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True) image = models.ImageField(upload_to='static/img/project/', blank=True, null=True) video = models.FileField(upload_to='static/video/project/', blank=True, null=True, help_text='Видео презентация проекта') video_poster = models.ImageField(upload_to='static/img/project/posters/', blank=True, null=True, help_text='Превью изображение для видео проекта') status = models.CharField(max_length=50, choices=[('in_progress', 'В процессе'), ('completed', 'Завершен')], default='in_progress') class Meta: verbose_name = 'Проект' verbose_name_plural = 'Проекты' ordering = ['-completion_date'] def __str__(self): return self.name class Review(models.Model): client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='reviews') service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name='reviews') project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='reviews', blank=True, null=True) rating = models.IntegerField() comment = models.TextField() review_date = models.DateTimeField(auto_now_add=True) image = models.ImageField(upload_to='static/img/review/', blank=True, null=True) video = models.FileField(upload_to='static/video/review/', blank=True, null=True, help_text='Видео отзыв о работе') video_poster = models.ImageField(upload_to='static/img/review/posters/', blank=True, null=True, help_text='Превью для видео отзыва') class Meta: verbose_name = 'Отзыв' verbose_name_plural = 'Отзывы' ordering = ['-review_date'] 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