from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import models from django.db.models import Q from django.utils import timezone from hitcount.models import HitCountMixin from mptt.models import TreeForeignKey import pydash as _; _.map = _.map_; _.filter = _.filter_ from users.models import User, Team from specializations.models import Specialization CURRENCIES = ( ('rur', 'RUR'), ('usd', 'USD'), ('eur', 'EUR'), ) TERMS = ( ('project', 'За проект'), ('hour', 'За час'), ('day', 'За день'), ('month', 'За месяц'), ) class BuildingClassfication(models.Model): name = models.CharField(max_length=255) def __str__(self): return self.name class Meta: verbose_name = 'Тип здания' verbose_name_plural = 'Типы зданий' class ConstructionType(models.Model): name = models.CharField(max_length=255) def __str__(self): return self.name class Meta: verbose_name = 'Вид строительства' verbose_name_plural = 'Виды строительства' class Realty(models.Model): building_classification = models.ForeignKey(BuildingClassfication, related_name='realties') construction_type = models.ForeignKey(ConstructionType, related_name='realties') location = TreeForeignKey('common.Location', related_name='realties', null=True, blank=True) name = models.CharField(max_length=255) user = models.ForeignKey(User, related_name='realties') # Do we actually need this field? def __str__(self): return self.name class Meta: verbose_name = 'Объект' verbose_name_plural = 'Объекты' class Project(models.Model, HitCountMixin): WORK_TYPES = ( (1, 'Проектирование'), (2, 'Техническое сопровождение') ) DEAL_TYPES = ( ('secure_deal', 'Безопасная сделка'), # "Безопасная сделка (с резервированием бюджета)" ('direct_payment', 'Прямая оплата'), # "Прямая оплата Исполнителю на его кошелек/счет" ) STATES = ( ('active', 'Активный'), ('trashed', 'В корзине'), ('deleted', 'Удален'), ) budget = models.DecimalField(max_digits=10, decimal_places=0) budget_by_agreement = models.BooleanField(default=False) created = models.DateTimeField(default=timezone.now) cro = models.BooleanField(default=False) currency = models.CharField(max_length=20, default='rur', choices=CURRENCIES) customer = models.ForeignKey(User, related_name='projects') # Related name should've been "customer_projects" deal_type = models.CharField(max_length=20, default='secure_deal', choices=DEAL_TYPES) name = models.CharField(max_length=255) price_and_term_required = models.BooleanField(default=False) realty = models.ForeignKey(Realty, blank=True, null=True, related_name='projects') specialization = TreeForeignKey(Specialization, related_name='projects') state = models.CharField(default='active', max_length=20, choices=STATES) term = models.IntegerField(default=0) term_type = models.CharField(max_length=20, choices=TERMS, default='hour') text = models.TextField(blank=True) work_type = models.IntegerField(default=1, choices=WORK_TYPES) def __str__(self): return self.name class Meta: verbose_name = 'Проект' verbose_name_plural = 'Проекты' ordering = ('-created',) def secure_deal(self): return self.deal_type == 'secure_deal' def get_team_answers(self): return _.filter(self.answers.all(), lambda a: isinstance(a.author, Team)) class ProjectFile(models.Model): file = models.FileField(upload_to='projects/project_files') project = models.ForeignKey(Project, related_name='files', blank=True, null=True) class Meta: verbose_name = 'Файл проекта' verbose_name_plural = 'Файлы проектов' def __str__(self): return self.file and self.file.url or self.pk class Answer(models.Model): budget = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) created = models.DateTimeField(default=timezone.now) currency = models.CharField(max_length=5, choices=CURRENCIES, blank=True, null=True) portfolios = models.ManyToManyField('Portfolio', related_name ='answers', blank=True) project = models.ForeignKey(Project, related_name='answers') secure_deal_only = models.BooleanField(default=False) term = models.IntegerField(blank=True, null=True) term_type = models.CharField(max_length=10, choices=TERMS, blank=True, null=True) is_archive = models.BooleanField(default=False) rejected = models.BooleanField(default=False) content_type = models.ForeignKey(ContentType, limit_choices_to=Q(app_label='users', model='user') | Q(app_label='users', model='team')) object_id = models.IntegerField() author = GenericForeignKey('content_type', 'object_id') def get_first_message(self): return self.messages.first().text def get_term_type_labels(self): term_type_labels = { 'hour': 'час,часа,часов', 'day': 'день,дня,дней', 'month': 'месяц,мксяца,месяцев', 'project': 'проект,проект,проект' } return term_type_labels.get(self.term_type) def __str__(self): return "{author}'s answer ({id})".format(author=type(self.author).__name__, id=self.pk) class Meta: verbose_name = 'Отклик на проект' verbose_name_plural = 'Отклики на проекты' ordering = ('-created',) class AnswerMessage(models.Model): answer = models.ForeignKey(Answer, related_name='messages') created = models.DateTimeField(default=timezone.now) is_sender_customer = models.BooleanField(default=False) text = models.TextField() content_type = models.ForeignKey( ContentType, limit_choices_to=Q(app_label='users', model='user') | Q(app_label='users', model='team'), null=True, blank=True, ) object_id = models.IntegerField(null=True, blank=True) contractor_or_team = GenericForeignKey('content_type', 'object_id') def __str__(self): return str(self.pk) class Meta: ordering = ['created'] verbose_name = 'Отклики на проекты -- переписка' verbose_name_plural = 'Отклики на проекты -- переписки' class AnswerFile(models.Model): answer = models.ForeignKey(Answer, related_name='files', blank=True, null=True) name = models.CharField(max_length=255) file = models.FileField(upload_to='projects/answer_files') class Meta: verbose_name = 'Файл для отклика' verbose_name_plural = 'Файлы для откликов' def __str__(self): return self.file and self.file.url or self.pk class Order(models.Model): STATUSES = ( ('created', 'Создан'), ('process', 'В процессе'), ('completed', 'Завершен'), ) contractor = models.ForeignKey(User, null=True, blank=True, related_name='orders') team = models.ForeignKey(Team, null=True, blank=True, related_name='orders') created = models.DateTimeField(default=timezone.now) project = models.OneToOneField(Project, related_name='order') secure = models.BooleanField(default=False) status = models.CharField(max_length=30, choices=STATUSES, default='created') def __str__(self): return self.project.name class Meta: verbose_name = 'Заказ' verbose_name_plural = 'Заказы' STATUSES = ( ('not_agreed','Не согласован'), ('in_process','В процессе'), ('completed','Завершен'), ) class Stage(models.Model): cost = models.DecimalField(max_digits=10, decimal_places=0) cost_type = models.CharField(max_length=5, choices=CURRENCIES, default='rur') name = models.CharField(max_length=255) order = models.ForeignKey(Order, related_name='stages') result = models.CharField(max_length=255) term = models.IntegerField(default=0) term_type = models.CharField(max_length=10, choices=TERMS, default='hour') status = models.CharField(choices=STATUSES, max_length=30, default='not_agreed') created = models.DateTimeField(default=timezone.now) pos = models.IntegerField(default=0, null=True, blank=True) is_paid = models.BooleanField(default=False) close_contractor = models.BooleanField(default=False) close_customer = models.BooleanField(default=False) def __str__(self): return self.name class Meta: ordering = ['pos'] verbose_name = 'Этап' verbose_name_plural = 'Этапы' class Candidate(models.Model): answer = models.ForeignKey(Answer, related_name='candidates') project = models.ForeignKey(Project, related_name='candidates') status = models.BooleanField(default=False) position = models.PositiveIntegerField(default=0) def __str__(self): return str(self.answer.pk) class Meta: ordering = ('position',) verbose_name = 'Кандидат' verbose_name_plural = 'Кандидаты' class Portfolio(models.Model): budget = models.DecimalField(max_digits=10, decimal_places=0, default=0, null=True, blank=True) building_classification = models.ForeignKey(BuildingClassfication, related_name='portfolios', null=True, blank=True) construction_type = models.ForeignKey(ConstructionType, related_name='portfolios', null=True, blank=True) currency = models.CharField(max_length=20, default='rur', choices=CURRENCIES, null=True, blank=True) description = models.TextField() location = TreeForeignKey('common.Location', related_name='portfolios', null=True, blank=True) name = models.CharField(max_length=255) specialization = TreeForeignKey(Specialization, related_name='portfolios', null=True, blank=True) term = models.IntegerField(default=0, null=True, blank=True) term_type = models.CharField(max_length=20, choices=TERMS, default='hour', null=True, blank=True) user = models.ForeignKey(User, related_name='portfolios', null=True, blank=True) worksell = models.BooleanField(default=False) created = models.DateTimeField(default=timezone.now) def __str__(self): return self.name def get_prev(self): try: return self.get_previous_by_created() except self.DoesNotExist: return None def get_next(self): try: return self.get_next_by_created() except self.DoesNotExist: return None class Meta: ordering = ['-created'] verbose_name = 'Портфолио' verbose_name_plural = 'Портфолио' def get_cover(self): photo = self.photos.first() return photo and photo.img class PortfolioPhoto(models.Model): img = models.ImageField(upload_to='projects/portfolio') portfolio = models.ForeignKey(Portfolio, related_name='photos') class Meta: verbose_name = 'Фото портфолио' verbose_name_plural = 'Фото портфолио' def __str__(self): return self.img and self.img.url or str(self.img)