Initial commit from ZIP
This commit is contained in:
commit
378d4692a9
454 files changed
+40367
No files matched your search
@@ -0,0 +1,3 @@
|
||||
from .project_number import ProjectNumber
|
||||
from .project import Project
|
||||
from .member import Member
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from enum import Enum
|
||||
|
||||
from django.db import models
|
||||
from rules.contrib.models import RulesModel
|
||||
|
||||
from outbo.apps.core.fields import EnumCharField
|
||||
from outbo.apps.core.models import TimestampableDeletableMixin
|
||||
from outbo.apps.projects import rules
|
||||
from outbo.apps.projects.models import Project
|
||||
|
||||
|
||||
class MembershipType(Enum):
|
||||
AGENCY = "agency"
|
||||
CLIENT = "client"
|
||||
SUPPLIER = "supplier"
|
||||
|
||||
|
||||
class Member(TimestampableDeletableMixin, RulesModel):
|
||||
class Meta:
|
||||
rules_permissions = {
|
||||
"view": rules.can_read_project_member,
|
||||
"add": rules.can_create_project_member,
|
||||
"change": rules.can_update_project_member,
|
||||
"delete": rules.can_delete_project_member,
|
||||
}
|
||||
|
||||
user = models.ForeignKey(
|
||||
to="authentication.User",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="member",
|
||||
related_query_name="member",
|
||||
)
|
||||
project = models.ForeignKey(
|
||||
to=Project,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="members",
|
||||
related_query_name="member",
|
||||
)
|
||||
role = EnumCharField(
|
||||
verbose_name=_("membership type"),
|
||||
max_length=25,
|
||||
blank=False,
|
||||
null=False,
|
||||
default=MembershipType.SUPPLIER,
|
||||
enum=MembershipType,
|
||||
)
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
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()
|
||||
@@ -0,0 +1,26 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import F
|
||||
|
||||
from rules.contrib.models import RulesModel
|
||||
from outbo.apps.core.models import TimestampableDeletableMixin, SoftDeletableManager
|
||||
|
||||
|
||||
class ProjectNumberManager(SoftDeletableManager):
|
||||
def get_next_number(self, prefix):
|
||||
current = self.get_or_create(prefix=prefix)[0]
|
||||
value = current.value
|
||||
|
||||
current.value = F("value") + 1
|
||||
current.save()
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class ProjectNumber(TimestampableDeletableMixin, RulesModel):
|
||||
objects = ProjectNumberManager()
|
||||
all_objects = ProjectNumberManager(alive_only=False)
|
||||
|
||||
prefix = models.CharField(max_length=4)
|
||||
value = models.PositiveIntegerField(default=1)
|
||||
Reference in new issue
Block a user