from typing import TYPE_CHECKING from django.db import models from django.utils.translation import gettext_lazy as _ from rules.contrib.models import RulesModel from outbo.apps.activity_log.enums import MessageType from outbo.apps.activity_log.models import ProjectLogItem from outbo.apps.core.models import TimestampableDeletableMixin, SoftDeletableManager from outbo.apps.products.models import Product from outbo.apps.projects import rules from outbo.apps.projects.fields import SlugField from outbo.apps.projects.models import ProjectNumber if TYPE_CHECKING: from outbo.apps.authentication.models import User class ProjectManager(SoftDeletableManager): def create(self, **kwargs): products_amount = kwargs.pop("products_amount", None) project: "Project" = super().create(**kwargs) if products_amount is not None: project.add_products(products_amount=products_amount) project.save() return project class Project(TimestampableDeletableMixin, RulesModel): objects = ProjectManager() all_objects = ProjectManager(alive_only=False) class Meta: rules_permissions = { "view": rules.can_read_project, "add": rules.can_create_project, "change": rules.can_update_project, "delete": rules.can_delete_project, } ordering = ["-updated_at__date", "slug"] def slugify(self): brand_prefix = self.category.name[:2].upper() category_prefix = self.brand.name[:2].upper() number = ProjectNumber.objects.get_next_number( prefix=f"{brand_prefix}{category_prefix}" ) week_year = self.promo_date.strftime("%U%y") return f"#{brand_prefix}{category_prefix}{number}-{week_year}" slug = SlugField( verbose_name=_("project identifier"), max_length=15, slugify=slugify ) name = models.CharField( verbose_name=_("name"), max_length=254, blank=False, null=False ) description = models.TextField( verbose_name=_("description"), blank=False, null=False ) category = models.ForeignKey( verbose_name=_("category"), to="organizations.Category", on_delete=models.CASCADE, null=False, ) brand = models.ForeignKey( verbose_name=_("brand"), to="organizations.Brand", on_delete=models.CASCADE, null=False, ) printing_files_date = models.DateField( verbose_name=_("printing files delivery date"), null=False ) promo_date = models.DateField(verbose_name=_("promo date"), null=False) # internal fields published = models.BooleanField( verbose_name=_("published"), blank=False, null=False, default=False ) @property def _max_product_stage_obj(self): max_product_stage_obj = ( self.products.order_by("-stage__order").first() ) if not max_product_stage_obj: return None return max_product_stage_obj @property def max_product_stage(self): return self._max_product_stage_obj.stage.id @property def max_product_stage_description(self): return self._max_product_stage_obj.stage.description def __str__(self): if self.slug: return f"[{self.slug}] {self.name}" else: return self.name def publishable_for_user(self, *, user: "User"): if user.is_observer or user.is_trader: return False return not self.published and all( [ product.has_brief_publishable_for_user(user=user) for product in self.products.all() ] ) def save(self, *args, **kwargs): products_amount = kwargs.pop("products_amount", None) super().save(*args, **kwargs) if products_amount is not None: self.add_products(products_amount=products_amount) def add_products(self, *, products_amount): [Product.objects.create(project=self) for _ in range(products_amount)] self.save() def publish_as_user(self, *, user: "User"): self.published = True for product in self.products.all(): product.send_action_emails_for_users_passing_test( test=lambda u: u in [m.user for m in self.members.all()] and u.is_agency, action_url="https://outbo.pl/projects/{id}/products/{pid}", ) product.publish_brief_as_user(user=user) product.save() ProjectLogItem.objects.create( project=self, created_by=user, message="Project published", message_type=MessageType.SUCCESS, ) self.save()